<!DOCTYPE html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="format-detection" content="telephone=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<head">
    <meta charset="utf-8">
    <title>MDO System Interface</title>
    <!--Include this css file in the <head> tag -->
    <link rel="stylesheet" href="static/lib/bootstrap/bootstrap.css" rel="stylesheet">
    <link rel="stylesheet" href="static/lib/bootstrap/bootstrap.min.css"/>
    <link rel="stylesheet" href="static/lib/jquery/jquery-ui.min.css"/>
    <link rel="stylesheet" href="static/lib/lobipanel/github.css"/>
    <link rel="stylesheet" href="static/lib/lobipanel/lobipanel.min.css"/>
    <link rel="stylesheet" href="static/lib/vistoms.css"/>
</head>

 <!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="static/lib/bootstrap/jquery-3.js" integrity="" crossorigin="anonymous"></script>
<script>window.jQuery || document.write('<script src="../../assets/js/vendor/jquery-slim.min.js"><\/script>')</script>
<script src="static/lib/bootstrap/popper.js"></script>
<script src="static/lib/bootstrap/bootstrap.js"></script>
<script src="static/lib/jquery/jquery.3.20.min.js"></script>
<script src="static/lib/jquery/jquery-ui.min.js"></script>
<script src="static/lib/jquery/jquery.ui.touch-punch.min.js"></script>
<script src="static/lib/bootstrap/bootstrap.min.js"></script>
<script src="static/lib/bootstrap/bootbox.min.js"></script>
<script src="static/lib/lobipanel/highlight.pack.js"></script>
<script src="static/lib/lobipanel/lobipanel.js"></script>
<script src="static/lib/vkbeautify/vkbeautify.js"></script>
<script src="static/lib/bowser/bowser.js"></script>

<body>    
    <nav style="position:fixed" class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
      <div class="collapse navbar-collapse" id="navbarsExampleDefault">
        <ul class="navbar-nav mr-auto">
          <li style="margin-left:10pt" class="nav-item">
            <a id="mainPage" class="nav-link" style="cursor: pointer"><span style="font-size: 18pt" title="Home" class="glyphicon glyphicon-home"></span></a>           
          </li>
          <li style="margin-left:10pt" class="nav-item">
            <a id="tutorial" class="nav-link" target="_blank" href="http://www.agile-project.eu/files/VISTOMS_Tutorial.mp4"><span style="font-size: 18pt" title="Tutorial" class="glyphicon glyphicon-question-sign"></span></a>
          </li>
          <li style="margin-left:10pt" class="nav-item">
            <a id="email" class="nav-link" href="mailto:support@agile-project.eu"><span style="font-size: 18pt" title="Contact support team" class="glyphicon glyphicon-envelope"></span></a>           
          </li>
          <li style="margin-left:10pt" class="nav-item">
            <a id="acknowledgements" class="nav-link" href="#"><span style="font-size: 18pt" title="Acknowledgements" class="glyphicon glyphicon-thumbs-up"></span></a>
          </li>
          <div style="margin-left:10pt" class="dropdown">
              <button style="font-size:18pt" class="btn btn-secondary dropdown-toggle" type="button" id="inspect_button"  data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                Open
              </button>
              <div id="inspect_dropdown" style="font-size:14pt" class="dropdown-menu" aria-labelledby="inspect_button">
              </div>              
          </div>
          <div style="margin-left:10pt; float:left" class="dropdown">
              <button style="font-size:18pt" class="btn btn-secondary dropdown-toggle" type="button" id="addGraph" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                Add
              </button>
              <div style="font-size:14pt" class="dropdown-menu" aria-labelledby="addGraph">
                <a id="from_scratch" class="dropdown-item" onmouseover=""  style="cursor: pointer;">Create graph from scratch</a>
                <a id="kdms_upload" class="dropdown-item" onmouseover=""  style="cursor: pointer;">Upload KDMS file(s)</a>
                <a id="cmdows_upload" class="dropdown-item" onmouseover=""  style="cursor: pointer;">Upload CMDOWS file(s)</a>
                <a id="database_upload" class="dropdown-item" onmouseover=""  style="cursor: pointer;">Upload database (zip-file)</a>
              </div>              
          </div>
          <div id="save_div" style="margin-left: 10pt; float:left; visibility:hidden" class="dropdown">
              <a style="font-size:18pt; color:white" class="btn btn-secondary" type="button" id="save_button">Save</a>           
          </div>
		  <div id="delete_div" style="margin-left: 10pt; float:left; visibility:hidden">
              <a style="font-size:18pt; color:white" class="btn btn-secondary" type="button" id="delete_button">Delete</a>
          </div>
          
        </ul>
        <div style="float: none;text-align: center;" class="navbar-header">
            <img style="margin-bottom:10pt" src="static/pictures/AGILE_Icon.png" width="40" height="40"></img>
            <a style="color: white;font-size:24pt; float:none" class="navbar-brand">MDO System Interface</a>
       </div>
       
      </div>
    </nav>

    <main style="" id="main" role="main">
      <!-- Main jumbotron for a primary marketing message or call to action -->
      <div style="margin-top:10pt;" class="jumbotron">
        <div class="container">
          <h1 class="display-3">Welcome to the MDO System Interface!</h1>
          <p style="font-size: 18pt">This package has been created within the context of the European Horizon 2020 project <a href="http://www.agile-project.eu/" target="_blank"><img src="static/pictures/AGILE_Logo_large.png" width="100" height="20"></img></a> </p>
          </br></br>
          <p style="font-size: 12pt">To set up, modify, or inspect an already existing MDO system, click "Open" in the navigation bar, select the graph you would like to see and choose one of the visualization types (XDSM, Edge Bundles, Sankey Diagram).</p>
          <p style="font-size: 12pt">If you haven't set up a system yet, or you want to add a new one, use the "Add" option to upload an MDO system file, or start a new system from scratch.</p>
          <p style="font-size: 12pt">You can also save all your currently active graphs at once by using the "Save" option.</p>
        </div>
      </div>
    </main>   
</body>
    <script>           
			var sessionId = {{ sessionId|tojson }};
			var interfaceMode = {{ interfaceMode|tojson }};
            if (bowser.name=="Internet Explorer")
			{
                alert("OOPS! The MDO System Interface unfortunately does not work properly on " + bowser.name + ". Please use a different browser to see its awesome visualization features!")
			}
            /* see docs: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage */
			if (window && window.localStorage && interfaceMode==='server')
			{
				var storage = window.localStorage;
				var existingSessionId = storage.getItem('sessionId');
				if (existingSessionId)
				{
					sessionId = existingSessionId;
				}
				else {
					storage.setItem('sessionId', sessionId);
				}
			}
            //aigner: Include function
			function include(arr,obj) {
					return (arr.indexOf(obj) != -1);
			}
            
            //aigner: Move to front function
			d3.selection.prototype.moveToFront = function() {  
			  return this.each(function(){
				this.parentNode.appendChild(this);
			  });
			};
			//aigner: Move to back function
			d3.selection.prototype.moveToBack = function() {  
				return this.each(function() { 
					var firstChild = this.parentNode.firstChild; 
					if (firstChild) { 
						this.parentNode.insertBefore(this, firstChild); 
					} 
				});
			};
            
            /**
             * Returns a random integer between min (inclusive) and max (inclusive)
             * Using Math.round() will give you a non-uniform distribution!
             */
            function getRandomInt(min, max) {
                return Math.floor(Math.random() * (max - min + 1)) + min;
            }

            
            //aigner: General functions communicating with the user via bootbox after kadmos requests
            //##############################################################
            function kadmosErrorMessage(content)
            {
                var message="";
                if (content.message.responseText){message = content.message.responseText}
                else if (content.message == ""){message = "ERROR: Something went wrong in KADMOS!"}
                else {message = content.message}
                bootbox.hideAll();
                bootbox.dialog({
                closeButton: false,
                title: content.title,
                message: message,
                buttons : { cancel:
                            {
                                label: "OK",
                                className: 'btn-default',
                                callback: function(){}
                            }
                          }
                });
            }
            
            function kadmosSuccessMessage(content)
            {
                bootbox.hideAll();
                bootbox.dialog(
                {
                    closeButton: false,
                    title: content.title,
                    message: content.message,
                    buttons : { cancel:
                                {
                                    label: "OK",
                                    callback: function(){}
                                }
                              }
                });
            }
            
            function kadmosHavePatience(anXhr, content)
            {
                bootbox.hideAll();
                bootbox.dialog({
                    closeButton: false,
                    title: content.title,
                    message: content.message,
                    buttons : { cancel:
                            {
                                label: "Cancel request",
                                className: 'btn-danger',
                                callback: function () {
                                    anXhr.abort()
                                    bootbox.hideAll();
                                    bootbox.dialog(
                                    {
                                        closeButton: false,
                                        title: content.title,
                                        message: "<b>KADMOS request aborted!</b> <p>CAUTION: Python functions could still be running in the background!</p>",
                                        buttons : { cancel:
                                                    {
                                                        label: "OK",
                                                        className: 'btn-danger',
                                                        callback: function(){}
                                                    }
                                                  }
                                    });
                                }
                            }
                        }
                });
            }
            
            //Remove obsolete files
            function remove_obsolete_files()
            {
                $.ajax({
                    type: 'POST', 
                    url: '/kadmos_remove_obsolete_files',
                    data: {'sessionId': sessionId},
                    success: function(result){},
                    error: function(result)
                    {
                        bootboxContent.message = result
                        kadmosErrorMessage(bootboxContent);
                    }
                });
            }
			
			function delete_graphs(graphIDs)
			{
				var bootboxContent = {title: "Deleting graphs", message: '<p>Please be patient...</p>'};
				bootbox.hideAll();
				bootbox.confirm("Are you sure you want to permanently delete these graphs? <b>This cannot be reverted!</b>", function(sure)
				{
					if (sure)
					{   
						graphIDs_str = JSON.stringify(graphIDs)
						var xhr = $.ajax(
						{
							type: 'POST',
							url: '/kadmos_delete_graphs',
							data: {'graphIDs':graphIDs_str, 'sessionId': sessionId},
							success: function(result)
							{
								if (result.includes("ERROR:"))
								{
									bootboxContent.message = result
									kadmosErrorMessage(bootboxContent);
								}
								else
								{                                        
									var dataArray = result;       
									var data = []
									if (dataArray.length == 0){data="REP__GRAPH_DATA__REP"}
									else{data = {"graphs":[], "categories":""};}
									for (var i = 0; i < dataArray.length; i++)
									{
										var data_tmp = JSON.parse(dataArray[i])
										data.graphs.push(data_tmp.graphs[0]);
										if (data.categories == [])
										{
											data.categories = data_tmp.categories;                                        
										}
									}
									
									clearView();
									makeKadmosMenu(data);
									mainPage();
									
									bootboxContent.message = "Graphs were sucessfully deleted!"
									kadmosSuccessMessage(bootboxContent)
								}
							},
							error: function(result)
							{
								bootboxContent.message = result
								kadmosErrorMessage(bootboxContent);
							}
						});
						kadmosHavePatience(xhr, bootboxContent)
					}
				})
				
			}
            //##############################################################
             
            //aigner: Function to filter a list with a search
            //##############################################################
            function filterSearch() 
            {
                var input, filter, table, tr, td, i;
                input = document.getElementById("myInput");
                filter = input.value.toUpperCase();
                table = document.getElementById("myTable");
                tr = table.getElementsByTagName("tr");
                for (i = 0; i < tr.length; i++) 
                {
                    td = tr[i].getElementsByTagName("td")[0];
                    if (td) 
                    {
                            if (td.innerHTML.toUpperCase().indexOf(filter) > -1) 
                            {
                                tr[i].style.display = "";
                            } 
                            else 
                            {
                                tr[i].style.display = "none";
                            }
                    }          
                }
            }
            //##############################################################
            
            //aigner: General functions for tree layout
            //##############################################################
            //Function to prune the tree according to list of xPaths that are actually there
            function prune_tree(aPipeData,aTreeData,direction="none")
            {
                //First, clean up pipeData if necessary
                var cleanPipeData = aPipeData.split(",")
                var index = cleanPipeData.indexOf("");
                if (index > -1) {cleanPipeData.splice(index, 1);}
                var firstEl = cleanPipeData[0].split("/")[1]                             
                
                for (var i=0; i < aTreeData.length; ++i) 
                {
                    var treeElement = aTreeData[i];
                    var relevant_xPath = "/"+firstEl+"/"+treeElement.xPath.split("/"+firstEl+"/")[1]
                    if(cleanPipeData.indexOf(relevant_xPath)<=-1)
                    {
                        aTreeData.splice(i,1);
                        i--;
                    }
                    else
                    {
                        if (direction=="in")
                        {
                            aTreeData[i].pipeLineIn = true;
                        }
                        else if (direction=="out")
                        {
                            aTreeData[i].pipeLineOut = true;
                        }
                    }
                }
            }
            
            function childExists(name, children)
            {
                var exists = false
                children.forEach(function(child)
                {
                    if (child.name == name)
                    {
                        exists = true;
                    }
                })
                return exists;
            }
            
            //Function to create the tree layout by appending children to the layout
            function appendChildren(anElement, aParent, xPath_list)
            {
                if (xPath_list.length > 0)
                {
                    //If element has no children yet, initialize children array
                    if (!aParent.children){aParent.children = [];}
                    //Create a new child element, if it does not exist yet
                    if (!childExists(xPath_list[0], aParent.children))
                    {
                        //Create a new child object
                        var newChild = {level: aParent.level+1, name: xPath_list[0], type: "variable", xPath: aParent.xPath+"/"+xPath_list[0]};
                        if (anElement.pipeLineIn){newChild.pipeLineIn=true}
                        if (anElement.pipeLineOut){newChild.pipeLineOut=true}
                        aParent.children.push(newChild);
                    }
                    aParent.children.forEach(function(aChild)
                    {
                        if (xPath_list[0] == aChild.name)
                        {
                            let newXPath_list = JSON.parse(JSON.stringify(xPath_list));
                            newXPath_list.shift();
                            appendChildren(anElement, aChild, newXPath_list)
                        }
                    })
                }
                else
                {
                    aParent.value = anElement.value;
                }
            }
            
            //function builds tree layout from xPaths of the elements
            function buildTree(root, aTreeData)
            {
                aTreeData.forEach(function(treeElement)
                {
                    var xPath_split = treeElement.xPath.split("/");
                    var index = xPath_split.indexOf("");
                    if (index > -1) {xPath_split.splice(index, 1);}
                    root.level = 0
                    root.name = xPath_split[0]
                    root.xPath = "/"+xPath_split[0]
                    <!-- console.log("####################") -->
                    <!-- console.log(treeElement.xPath) -->
                    let newXPath_split = JSON.parse(JSON.stringify(xPath_split));
                    newXPath_split.shift();
                    appendChildren(treeElement, root, newXPath_split)
                    <!-- console.log("DONE!") -->
                    <!-- console.log("####################") -->
                })
            }
            //##############################################################
            
            
            //aigner: Function to download data to a file
            //##############################################################
            function download_from_kadmos(filename, href) {
                var element = document.createElement('a');
                element.setAttribute('href', href);
                element.setAttribute('download', filename);

                element.style.display = 'none';
                document.body.appendChild(element);

                element.click();

                document.body.removeChild(element);
            }
            //##############################################################
             
                          
            function makeKadmosMenu(data, initial=false)
			{
                //First of all, clear everything if it is already there
                var lobiPanels = d3.selectAll(".lobipanel");
                if (lobiPanels)
                {
                    lobiPanels.each(function()
                    {
                        this.remove();
                    })
                }
				
				
				//@aigner: Make Acknowledgments, Help and Home buttons
				//##########################################################
				///Home button
				d3.select("#mainPage").on("mousedown",function()
				{		
					mainPage();
				})
                d3.select("#acknowledgements").on("mousedown",function()
				{		
					acknowledgements();
				})
				//##########################################################               
                
				//aigner: Remove save and delete buttons at first
				//##########################################################
				var save_div = d3.select("#save_div")
				save_div.style("visibility","hidden")
				var delete_div = d3.select("#delete_div")
				delete_div.style("visibility","hidden")
				//##########################################################
				
                var tmpGraphsAvailable = false;
				if (initial)
                {
                    var bootboxContent = {title: "Session start", message: '<p>Please be patient...</p>'};
                    var xhr = $.ajax({
                        type: 'POST',
                        url: '/kadmos_find_temp_graphs',
                        data: {'sessionId': sessionId},
                        success: function(result)
                        {
                            if (result.includes("ERROR:"))
                            {
                                bootboxContent.message = result
                                kadmosErrorMessage(bootboxContent);
                            }
                            else
                            {
                                var dataArray = result;          
                                for (var i = 0; i < dataArray.length; i++)
                                {
                                    if (data=="REP__GRAPH_DATA__REP")
                                    {
                                        data = {"graphs":[], "categories":""};
                                    }
                                    var data_tmp = JSON.parse(dataArray[i])
                                    data.graphs.push(data_tmp.graphs[0]);
                                    if (data.categories == [])
                                    {
                                        data.categories = data_tmp.categories;                                        
                                    }
                                    tmpGraphsAvailable = true;
                                }
                                
                                
                                if (tmpGraphsAvailable)
                                {
                                    bootboxContent.title = "Interface ready!"
                                    bootboxContent.message = "There are already existing graphs. You can select them from the drop-down menu."
                                    kadmosSuccessMessage(bootboxContent)
                                }
                                else {bootbox.hideAll();}
                                //aigner: make dropwdown section for the multiple MDO graphs tzhat can be visualized
                                make_open_button(data);
                                //aigner: Add special button to open graphs from kdms or cmdows files
                                add_graph_dropdown(data.graphs);
                                save_graphs_dropdown(data.graphs);
                                delete_graphs_dropdown(data.graphs);
                            }
                        },
                        error: function(result)
                        {
                            bootboxContent.message = result
                            kadmosErrorMessage(bootboxContent);
                        }
                    });
                    kadmosHavePatience(xhr, bootboxContent)
                }
                else
                {
                    //aigner: make dropwdown section for the multiple MDO graphs tzhat can be visualized
                    make_open_button(data);
                    //aigner: Add special button to open graphs from kdms or cmdows files
                    add_graph_dropdown(data.graphs);
                    save_graphs_dropdown(data.graphs);
                    delete_graphs_dropdown(data.graphs);
                }
                
                
                
                
				//aigner: make dropwdown section for the multiple MDO graphs tzhat can be visualized
				//##########################################################           
				function make_open_button(data)
				{
                    var numberOfGraphs = 0;
					var dropdown = d3.select("#inspect_dropdown")
                    if (dropdown){dropdown.selectAll("a").remove();}
                    if (data=="REP__GRAPH_DATA__REP" && !tmpGraphsAvailable)
                    {                  
                        dropdown.append("a")
                            .attr("class","dropdown-item")
                            .style("font-style","Italic")
                            .text("No graphs to inspect ...")
                            .on("mouseover", function(){d3.select(this).style("cursor", "pointer")})
                            .on("click", function(){
                                    
                                bootbox.hideAll();
                                bootbox.dialog({
                                    closeButton: false,
                                    message: "No graph to inspect yet! \nYou can add a graph by uploading a KDMS or CMDOWS file.",
                                    buttons : { cancel:
                                                {
                                                    label: "OK",
                                                    className: 'btn-danger',
                                                    callback: function(){}
                                                }
                                              }
                                });
                            })
                    }
                    else
                    {
                        var graphs_tmp = data.graphs;
                        graphs_tmp.forEach(function(theGraph)
                        {
                            var name_tmp="";
                            if (theGraph.name){name_tmp=theGraph.name}
                            else{name_tmp="Graph " + theGraph.id}
                            dropdown.append("a")
                                .attr("class","dropdown-item")
                                .text(name_tmp)
                                .on("mouseover", function(){d3.select(this).style("cursor", "pointer")})
                                .on("click", function()
                                {
                                        bootbox.prompt({
                                        closeButton: false,
                                        title: "Please select the visualization type",
                                        inputType: 'select',
                                        value: 'xdsm',
                                        inputOptions: [
                                            {
                                                text: 'XDSM view',
                                                value: 'xdsm',
                                            },
                                            {
                                                text: 'Edge Bundles view',
                                                value: 'edgeBundles',
                                            },
                                            {
                                                text: 'Sankey Diagram',
                                                value: 'sankeyDiagram',
                                            }
                                        ],
                                        callback: function (theView) {
                                            if (theView == "xdsm")
                                            {
                                                clearView();
                                                xdsm_script(data,theGraph.id)
                                            }
                                            else if (theView == "edgeBundles")
                                            {
                                                clearView();
                                                edgeBundles_script(data,theGraph.id)
                                            }
                                            else if (theView == "sankeyDiagram")
                                            {
                                                clearView();
                                                sankeyDiagram_script(data,theGraph.id)
                                            }
                                            else
                                            {}
                                        }
                                    });
                                })
                            numberOfGraphs ++;
                        })
                    }
				}
                
				//##########################################################
                
                
                //aigner: Add general panel to open and save graphs and for adding contacts to the organization
                //##########################################################                               
                function add_graph_dropdown(theGraphs)
                {                   
                    var newGraphID = '01';
                    if (theGraphs)
                    {
                        theGraphs.forEach(function(graph) 
                        {
                            id_int = parseInt(graph.id)
                            if (theGraphs.length < 100){newGraphID = "0" + String(id_int+1);}
                            else{newGraphID = String(id_int+1);}
                        })
                    }
                    
                    d3.select("#from_scratch").on("mousedown", function()
					{
						bootbox.hideAll();
                        var html = d3.select("html")
                        var form_content = html.append("div").attr("class","form-content").attr("style","display:none")
                        var form = form_content.append("form").attr("class","form").attr("role","form")
                        var form_group, input;
                            
                        //Tool information
                        form_group = form.append("div").attr("class","form-group")
                        form_group.append("text").text("Name")
                        input = form_group.append("input")
                            .attr("id","graph_name")
                            .attr("class","form-control")
                            .attr("name","graph_name")
                            .attr("placeholder","Add a graph name here...")
                        //Tool information
                        form_group = form.append("div").attr("class","form-group")
                        form_group.append("text").text("Description")
                        input = form_group.append("input")
                            .attr("id","graph_description")
                            .attr("class","form-control")
                            .attr("name","graph_description")
                            .attr("placeholder","Add a graph description here...")
                        
                       bootbox.dialog({
                            closeButton: false,
                            message: $(".form-content").html(),
                            title: "Create new graph",
                            size: "large",
                            buttons: [
                              {
                                label: "Cancel",
                                className: "btn btn-default pull-left",
                                callback: function() {
                                  d3.selectAll(".form-content").remove();
                                }
                              },
                              {
                                label: "OK",
                                className: "btn btn-primary pull-left",
                                callback: function() {
                                    
                                    //get form data
                                    var graph_name = $('form #graph_name').val();
                                    var graph_description = $('form #graph_description').val();
                                    
                                    
                                    var bootboxContent = {title: "Create new graph", message: '<p>Please be patient...</p>'};
                                    var xhr = $.ajax({
                                        type: 'POST',
                                        url: '/kadmos_create_new_graph',
                                        data: {'graph_name':graph_name, 'graph_description':graph_description, 'graphID':newGraphID, 'sessionId':sessionId},
                                        success: function(result)
                                        {
                                            if (result.includes("ERROR:"))
                                            {
                                                bootboxContent.message = result
                                                kadmosErrorMessage(bootboxContent);
                                                
                                                clearView();
                                                makeKadmosMenu(data);
                                                mainPage();
                                            }
                                            else
                                            {
                                                var updatedData = {};
                                                if (data != "REP__GRAPH_DATA__REP")
                                                {
                                                    updatedData = data;
                                                    var graphData = JSON.parse(result);                                        
                                                    updatedData.graphs.push(graphData.graphs[0]);                                        
                                                }
                                                else
                                                {
                                                    updatedData = JSON.parse(result);
                                                }
                                                                                                    
                                                clearView();
                                                makeKadmosMenu(updatedData);
                                                mainPage();
                                                
                                                bootboxContent.message = "Success!"
                                                kadmosSuccessMessage(bootboxContent)
                                                
                                            }
                                        },
                                        error: function(result)
                                        {
                                            bootboxContent.message = result
                                            kadmosErrorMessage(bootboxContent);
                                        }
                                    });
                                    kadmosHavePatience(xhr, bootboxContent)
                                    d3.selectAll(".form-content").remove();
                                }
                              }
                            ]
                        });
					})
                    
                    d3.select("#kdms_upload").on("mousedown", function()
					{
                        prepare_graphUpload("Upload KDMS file(s)", "Choose KDMS files (max. 2: 1 data graph & 1 process graph)", "KDMS")
                    })
                    
                    d3.select("#cmdows_upload").on("mousedown", function()
					{
                        prepare_graphUpload("Upload CMDOWS file", "Choose CMDOWS file", "CMDOWS")
                    })
                    
                    d3.select("#database_upload").on("mousedown", function()
					{
                        prepare_graphUpload("Upload Database", "Choose Database zip file", "Database")
                    })
                    
                    function prepare_graphUpload(title, text, fileType)
                    {
                        bootbox.dialog({
                            closeButton: false,
                            title: title,
                            message: "<form id='addForm' method='post' enctype='multipart/form-data' ><label><text>"+text+"</text><input type='file' name='file[]' multiple=''></input><input type='submit' value='Submit'></input></label></form>",
                            buttons: [{
                            label: "Cancel",
                            className: "btn btn-danger pull-left",
                            callback: function() {}}]
                        });
                        $('#addForm').on('submit',function(event){
                            event.preventDefault();
                            //aigner: Uploading files for KADMOS
                            var formData = new FormData($('#addForm')[0]);
                            formData.append('newGraphID', newGraphID);
                            formData.append('fileType', fileType);
                            formData.append('sessionId',sessionId)
                            upload_graph(formData);
                        });
                    }
                    
                    function upload_graph(formData)
                    {
                        //aigner: Upload file to VISTOMS and inspect it
                        //##########################################################
                        var bootboxContent = {title: "Upload file to interface", message: '<p>Please be patient...</p>'};
                        var xhr = $.ajax({
                            type: 'POST',
                            url: '/kadmos_upload_files',
                            data: formData,
                            processData: false,
                            contentType: false,
                            success: function(result)
                            {
                                if (result.includes("ERROR:"))
                                {
                                    
                                    bootboxContent.message = result
                                    kadmosErrorMessage(bootboxContent);
                                    
                                    clearView();
                                    makeKadmosMenu(data);
                                    mainPage();
                                }
                                else
                                {
									var dataArray = result;        
									var updatedData = {};
									if (data != "REP__GRAPH_DATA__REP")
									{
										updatedData = data;
									}
									else
									{
										updatedData = {"graphs":[], "categories":""};
									}
									for (var i = 0; i < dataArray.length; i++)
									{
										var graphData = JSON.parse(dataArray[i]);       
										updatedData.graphs.push(graphData.graphs[0]); 
									}
									                                    
                                    clearView();
                                    makeKadmosMenu(updatedData);
                                    mainPage();
                                    
                                    bootboxContent.message = "Success!"
                                    kadmosSuccessMessage(bootboxContent)
                                    
                                }
                            },
                            error: function(result)
                            {
                                bootboxContent.message = result
                                kadmosErrorMessage(bootboxContent);
                            }
                        });
                        kadmosHavePatience(xhr, bootboxContent)
                        //##########################################################
                    }
                    
                }
				
				function save_graphs_dropdown(theGraphs)
				{
					function save_graphs(graphNames, graphIDs, fileType)
					{
						var graphIDs_str = JSON.stringify(graphIDs)
						var graphNames_str = JSON.stringify(graphNames)
						//aigner: Saving files
						//##########################################################
						bootbox.hideAll();
						var bootboxContent = {title: "Save graph", message: '<p>Please be patient...</p>'};
						var xhr = $.ajax(
						{
							type: 'POST',
							data: {'fileNames_str':graphNames_str, 'graphIDs_str':graphIDs_str, 'fileType': fileType, 'sessionId': sessionId},
							url: '/kadmos_export_graphs',
							success: function(result)
							{                  
								var files = result
								files.forEach(function(file)
								{
									download_from_kadmos(file,"/kadmos_download/"+file+"/"+sessionId)
								})
								
								
								if (result.includes("ERROR:"))
								{
									bootboxContent.message = result
									kadmosErrorMessage(bootboxContent);
								}
								else
								{
									bootboxContent.message = "Successfully created the files. Ready for download."
									bootbox.hideAll();
									bootbox.confirm(
									{
										closeButton: false,
										title: bootboxContent.title,
										message: bootboxContent.message,
										callback: function(){
											remove_obsolete_files()
										}
									});
								}
							},
							error: function(result)
							{
								bootboxContent.message = result
								kadmosErrorMessage(bootboxContent);
							}
						});
						kadmosHavePatience(xhr, bootboxContent)
						//##########################################################
					}
					
                    if (theGraphs)
                    {
						var options = []
                        theGraphs.forEach(function(theGraph)
                        {
							options.push({text:theGraph.name, value:theGraph.id})
                        })
						
                        var save_div = d3.select("#save_div")
                        save_div.style("visibility","visible")
						var save_button = d3.select("#save_button")
						save_button.on("mousedown", function()
                        {
                            bootbox.hideAll();
							bootbox.prompt({
								closeButton: false,
								title: "Which graphs should be saved?",
								inputType: 'checkbox',
								inputOptions: options,
								callback: function (graphIDs) {
									if (graphIDs != null)
									{	
										graphNames = []
										theGraphs.forEach(function(theGraph)
										{
											if (graphIDs.indexOf(theGraph.id) > -1)
											{
												graphNames.push(theGraph.name)
											}
										})
										bootbox.prompt({
											closeButton: false,
											title: "Please select a filetype",
											inputType: 'select',
											inputOptions: [{text:"Please choose...", value:"-"},
											               {text:"CMDOWS", value:"cmdows"},
														   {text:"KDMS", value:"kdms"}],
											value: "-",
											callback: function (filetype) {
												if (filetype != null)
												{
													save_graphs(graphNames,graphIDs,filetype)
													//@aigner: HIER WEITER!!!
												}
											}
										});
									}
								}
							});
                        })
                    }
				}
				
				
				
				function delete_graphs_dropdown(theGraphs)
				{
                    if (theGraphs)
                    {
						var options = []
                        theGraphs.forEach(function(theGraph)
                        {
							options.push({text:theGraph.name, value:theGraph.id})
                        })
						
                        var delete_div = d3.select("#delete_div")
                        delete_div.style("visibility","visible")
						var delete_button = d3.select("#delete_button")
						delete_button.on("mousedown", function()
                        {
                            bootbox.hideAll();
							bootbox.prompt({
								closeButton: false,
								title: "Which graphs should be deleted?",
								inputType: 'checkbox',
								inputOptions: options,
								callback: function (graphIDs) {
									if (graphIDs != null)
									{
										delete_graphs(graphIDs)
										//@aigner: HIER WEITER!!!
									}
								}
							});
                        })
                    }
				}
            }
			//##########################################################


			function getTextWidth(text, font) 
			{
				// re-use canvas object for better performance
				var canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas"));
				var context = canvas.getContext("2d");
				context.font = font;
				var metrics = context.measureText(text);
				return metrics.width;
			}					


			//#####################################################################//
            //@aigner: !!!
			function mainPage()
			{	
				clearView();
                
                var visualizationTypes = "<div id='visualizationTypes'><div><div class='container' style='margin-top:0pt;'><h2>Explore your MDO systems with three different visualization types...</h2></div></div><div style='height: 500pt' id='myCarousel' class='carousel slide' data-ride='carousel'><ol class='carousel-indicators'><li data-target='#myCarousel' data-slide-to='0' class=''></li><li data-target='#myCarousel' data-slide-to='1' class='active'></li><li data-target='#myCarousel' data-slide-to='2' class=''></li></ol><div style='height: 100%;' class='carousel-inner'><div style='height: 100%; background-color: #fff' class='carousel-item'><div class='container'><img class='first-slide' src='static/pictures/XDSM.png' style='width:100%; margin-top: 50pt' alt=''><div class='carousel-caption'><h1 style='color: #5a5a5a; text-shadow: none'>Extended Design Structure Matrix (XDSM)</h1></div></div></div><div style='height: 100%; background-color: #fff' class='carousel-item active carousel-item'><div class='container'><img class='second-slide' src='static/pictures/EdgeBundles.png' style='display: block; width:50%; margin:auto' alt=''><div class='carousel-caption'><h1 style='color: #5a5a5a; text-shadow: none'>Edge Bundling Diagram</h1></div></div></div><div style='height: 100%; background-color: #fff' class='carousel-item carousel-item-next carousel-item'><div class='container'><img class='third-slide' src='static/pictures/SankeyDiagram.png' style='display: block; width:50%; margin:auto; margin-top:20pt' alt=''><div class='carousel-caption'><h1 style='color: #5a5a5a; text-shadow: none'>Sankey Diagram</h1></div></div></div></div><a class='carousel-control-prev' href='#myCarousel' role='button' data-slide='prev'><span class='carousel-control-prev-icon' aria-hidden='true'></span><span class='sr-only'>Previous</span></a><a class='carousel-control-next' href='#myCarousel' role='button' data-slide='next'><span class='carousel-control-next-icon' aria-hidden='true'></span><span class='sr-only'>Next</span></a></div></div>";
                var technologies = "<div id='technologies' style='' class='jumbotron'><div class='container'><h1 style='font-size:24pt'>The MDO System Interface is powered by the following technologies</h1><div style='margin-top:20pt' class='row'><div class='col-md-4'><h2 style='font-size:20pt'><img  src='static/pictures/KADMOS_Logo.png' width='150' height='40'/></h2><p style='font-size:12pt'><b>K</b>nowledge- and graph-based <b>A</b>gile <b>D</b>esign with <b>M</b>ultidisciplinary <b>O</b>ptimization <b>S</b>ystem</p><p style='font-size:10pt'>KADMOS is the python-based engine running under the hood of the MDO System Interface. To get more information on its capabilities, go to the open-source KADMOS repository.</p><p style='margin-bottom:10pt'><button style='font-size:12pt' class='btn btn-secondary' onclick='window.open(\"https://bitbucket.org/imcovangent/kadmos\")'>KADMOS repository »</button></p></div><div class='col-md-4'><h2 style='font-size:20pt'><img src='static/pictures/CMDOWS_Logo.png' width='40' height='%0'/> CMDOWS</h2><p style='font-size:12pt'><b>C</b>ommon <b>MDO</b> <b>W</b>orkflow <b>S</b>chema</p><p style='font-size:10pt'>CMDOWS is the XML-based storage system for MDO systems. It contains all the data of an MDO system including the tools, their interconnections, execution order, optimization strategy, etc. <br> To get more information, please go to the open-source CMDOWS repository.</p><p style='margin-bottom:10pt'><button style='font-size:12pt' class='btn btn-secondary' onclick='window.open(\"https://bitbucket.org/imcovangent/cmdows\")'>CMDOWS repository »</button></p></div><div class='col-md-4'><h2 style='font-size:20pt'><img src='static/pictures/VISTOMS_Label.png' width='150' height='45'/></h2><p style='font-size:12pt'><b>VIS</b>ualization <b>TO</b>ol for <b>M</b>DO <b>S</b>ystems</p><p style='font-size:10pt'>VISTOMS is the graphical user interface that allows you to easily set up, inspect and modify your MDO systems. If you need more information about how to use it, simply click on the tutorial button in the navigation bar, or just click on the button below.</p><p style='margin-bottom:10pt'><button style='font-size:12pt' class='btn btn-secondary'  onclick='window.open(\"http://www.agile-project.eu/files/VISTOMS_Tutorial.mp4\")'>Tutorial »</button></p></div></div><hr></div></div>";
                var footer = "<footer style=''' class='container'><p>© Benedikt Aigner (RWTH Aachen) & Imco van Gent (TU Delft) 2016-2018</p><p><a style='padding:5pt' href=\"https://www.rwth-aachen.de\" target='_blank'><img  title='RWTH Aachen Homepage' src='static/pictures/RWTH_Logo.png' width='150' height='40'></img></a><a style='padding:5pt' href=\"https://www.tudelft.nl/\" target='_blank'><img  title='TU Delft Homepage' src='static/pictures/TUDelft_Logo.png' width='100' height='40'></img></a></p></footer>";
                
                
                d3.select("#main").append("div").html(visualizationTypes)
                d3.select("#main").append("div").html(technologies)
                d3.select("#main").append("div").html(footer)
				<!-- var introDiv = d3.select("body").append("div") -->
					<!-- .attr("id","mainPage") -->
					<!-- .classed("wordWrap",true) -->
				
				<!-- var theHeading = "Welcome to the MDO System Interface!\n" -->
                <!-- introDiv.append("text") -->
                    <!-- .style("font-size","16pt") -->
                    <!-- .html(theHeading) -->
				
				<!-- var theText = "The interface is a combination of two open-source packages, the <b>K</b>nowledge- and graph-based <b>A</b>gile <b>D</b>esign with <b>M</b>ultidisciplinary <b>O</b>ptimization <b>S</b>ystem (<b>KADMOS</b>) " -->
                            <!-- +"and the <b>VIS</b>ualization <b>TO</b>ol for <b>M</b>DO <b>S</b>ystems (<b>VISTOMS</b>). " -->
                            <!-- +"While KADMOS is the python-based engine running under the hood, VISTOMS is the graphical user interface to facilitate the setup of your MDO systems." -->
                            <!-- +"\n\nTo set up, modify, or inspect an already existing MDO system, go to one of the visualizations in the navigation bar (XDSM, Edge Bundles, or Sankey Diagram). Then select a graph from the dropdown menu." -->
                            <!-- +"\n\nIf you haven't already set up a system, use the \"Add graph\" option to upload an MDO system file, or start a new system from scratch." -->
                            <!-- +"\n\nIf you need help with how to use the package, there is a tutorial video available. Just click on the \"Tutorial\" symbol in the top right corner." -->
                            <!-- +"\n\nIf you want to get more information on the package, here is a link to the open-source <b><a target=\"_blank\" href=\"https://bitbucket.org/imcovangent/kadmos\">KADMOS repository</a></b> "; -->
				<!-- introDiv.append("text").html(theText) -->
				<!-- introDiv.append("text") -->
					<!-- .style("font-size","8pt") -->
					<!-- .html("\n\n\nCopyright 2016-2018 by Benedikt Aigner (RWTH Aachen) & Imco van Gent (TU Delft)") -->
				
				<!-- var introSvg = introDiv.append("svg") -->
					<!-- .attr("width",500) -->
					<!-- .attr("height",100) -->
				<!-- var imgs = introSvg.selectAll("img").data([0]); -->
					<!-- imgs.enter() -->
					<!-- .append("svg:image") -->
					<!-- .attr("xlink:href", fileReference.RWTH_Logo) -->
					<!-- .attr("width", "150") -->
					<!-- .attr("height", "70") -->
					<!-- .on("mouseover", function(){d3.select(this).style("cursor", "pointer")}) -->
					<!-- .on("click",function(){window.open("https://www.rwth-aachen.de/", '_blank');} ) -->
				<!-- var imgs = introSvg.selectAll("img").data([0]); -->
					<!-- imgs.enter() -->
					<!-- .append("svg:image") -->
					<!-- .attr("xlink:href", fileReference.TUDelft_Logo) -->
					<!-- .attr("x", 175) -->
					<!-- .attr("y", 5) -->
					<!-- .attr("width", "105") -->
					<!-- .attr("height", "50") -->
					<!-- .on("mouseover", function(){d3.select(this).style("cursor", "pointer")}) -->
					<!-- .on("click",function(){window.open("https://www.tudelft.nl/en/", '_blank');} ) -->

			}
			//#####################################################################//

			//#####################################################################//
            //@aigner: !!!
			function acknowledgements()
			{				
				clearView();
				var acknDiv = d3.select("body").append("div")
					.attr("id","acknowledgements_text")
					.classed("acknText", true)
					.classed("wordWrap",true)
				
				var theHeading = "Acknowledgements";
				acknDiv.append("text")
				.style("font-size","16pt")
				.html(theHeading)
				
				acknDiv.append("text")
					.style("font-size","10pt")
					.style("margin", "20px 10px")
					.html("<br><br>Created by Benedikt Aigner and Imco van Gent for the "
						+"<a target='_blank' href=\"http://www.agile-project.eu/\">AGILE Project</a> in 2016. This project has received funding from the European Union's Horizon 2020 research and innovation framework programme under grant agreement No 636202."
						+"<br>"
						+"<br>"
						+"<b>VISTOMS was created using the following open source packages:</b>"
						+"<br>"
						+"<a target='_blank' href=\"https://d3js.org/\">D3.js</a> package &copy 2015 by Mike Bostock released under the <a target='_blank' href=\"https://opensource.org/licenses/BSD-3-Clause\">BSD license</a>"
						+"<br>"
						+"<a target='_blank' href=\"https://github.com/OneraHub/XDSMjs\">XDSM.js </a> package &copy 2016 by R&eacutemi Lafage released under the <a target='blank' href=\"http://www.apache.org/licenses/LICENSE-2.0\">Apache License, Version 2.0</a>"
						+"<br>"
						+"<a target='_blank' href=\"https://github.com/Neilos/bihisankey\">BiHiSankey.js</a> package &copy 2015 by Neil Atkinson released under the <a target='blank' href=\"https://opensource.org/licenses/MIT\">MIT License</a>"
						+"<br>"
						+"<br>"
                        +"<b>For further information on KADMOS, please visit the open-source <a target='_blank' href=\"https://bitbucket.org/imcovangent/kadmos/\">KADMOS repository</a> </b>"
						+"<br>"
						+"<br>"
						+"<br>"
						+"<font size=\"-2\">Rev. 10/29/2018</font>")
			}
			//#####################################################################//

			//#####################################################################//
            //@aigner: !!!
			function tutorial()
			{
				<!-- var theButtons =  -->
				<!-- { -->
					<!-- cancel: { -->
						<!-- label: "OK, got it!", -->
						<!-- className: 'btn-danger', -->
						<!-- callback: function(){} -->
					<!-- }, -->
					<!-- Feedback: { -->
						<!-- label: "Feedback", -->
						<!-- className: 'btn-warning', -->
						<!-- callback: function(){sendMail("VISTOMS feedback")} -->
					<!-- }, -->
					<!-- Tutorial: { -->
						<!-- label: "Tutorial", -->
						<!-- className: 'btn-info', -->
						<!-- callback: function(){showTutorialPage();} -->
					<!-- } -->
				<!-- } -->
				
				
				<!-- function showTutorialPage() -->
				<!-- { -->
					<!-- clearView(); -->
					<!-- var tutorialDiv = d3.select("body").append("div") -->
						<!-- .attr("id","tutorial") -->
						<!-- .style("margin-top", "16px") -->
						<!-- .style("font-size","16pt") -->
						<!-- .style("margin", "20px 10px") -->
						<!-- .style("color","#555555") -->
					
					<!-- var theHeading = "<b>VISTOMS Tutorial</b><br/><br/>"; -->
					<!-- tutorialDiv.append("text") -->
					<!-- .style("font-size","16pt") -->
					<!-- .html(theHeading) -->
					
					<!-- tutorialDiv.append("div") -->
						<!-- .style("-webkit-overflow-scrolling", "touch") -->
						<!-- .style("-webkit-text-size-adjust", "none") -->
						<!-- .html("<video width=\"1000\" height=\"563\" controls>" -->
							<!-- +"<source src=\"http://www.agile-project.eu/files/VISTOMS_Tutorial.mp4 \" type=\"video/mp4\">" -->
							<!-- +"Your browser does not support the video tag." -->
							<!-- +"</video>" -->
							<!-- +"</div>") -->
				<!-- } -->
				
				
				<!-- if (!d3.select(".xdsmDiv").empty()) -->
				<!-- { -->
                    <!-- bootbox.hideAll(); -->
					<!-- var dialog = bootbox.dialog( -->
					<!-- { -->
						<!-- title: 'XDSM View', -->
						<!-- message: "<p><b>To interactively inspect the MDO system there are several options.</b></p>" -->
								 <!-- +"<ol><li>With a right-click on an edge (rhomboids on the off-diagonal) you can take a closer look at the data processed between competences.</li>" -->
								 <!-- +"<li>Click right on a competence (boxes on the diagonal) for more information, such as input/output data or a detailed tool description.</li>" -->
								 <!-- +"<li>To view the full data model, go to the \"Data model\" box and select a variable categorization. </li></ol><br />" -->
								 <!-- +"<p>To switch to another visualization (<i>Edge Bundles</i> or <i>Sankey Diagram</i>), go to the navigation bar and select a graph from the drop down menu.</p>" -->
								 <!-- +"<p>If you need more information on how to use the visualization package, click on the \"Tutorial\" button below.</p>" -->
								 <!-- +"<p><br/>Any questions or feedback? Contact the support team with the \"Feedback\" button below!</p>" -->
						<!-- , -->
						<!-- buttons : theButtons -->
					<!-- }); -->
				<!-- } -->
				<!-- else if(!d3.select(".edgeBundlesDiv").empty()) -->
				<!-- { -->
                    <!-- bootbox.hideAll(); -->
					<!-- var dialog = bootbox.dialog({ -->
						<!-- title: 'Edge Bundles View', -->
						<!-- message: "<p><b>To interactively inspect the MDO system there are several options.</b></p>" -->
								 <!-- +"<ol><li>With a right-click on an edge (connecting lines between the competences) you can take a closer look at the data processed between those competences.</li>" -->
								 <!-- +"<li>When you hover over one of the competences with the mouse, the respective input and output connections are highlighted. Input connections are highlighted in red, output connections in green.</li>" -->
								 <!-- +"<li>Click right on a competence for more information, such as input/output data or a detailed tool description.</li>" -->
								 <!-- +"<li>To view the full data model, go to the \"Data model\" box and select a variable categorization.</li></ol><br />" -->
								 <!-- +"<p>To switch to another visualization (<i>Edge Bundles</i> or <i>Sankey Diagram</i>), go to the navigation bar and select a graph from the drop down menu.</p>" -->
								 <!-- +"<p>If you need more information on how to use the visualization package, click on the \"Tutorial\" button below.</p>" -->
								 <!-- +"<p><br/>Any questions or feedback? Contact the support team with the \"Feedback\" button below!</p>" -->
						<!-- , -->
						<!-- buttons: theButtons -->
					<!-- }) -->
				<!-- } -->
				<!-- else if(!d3.select(".sankeyDiagramDiv").empty()) -->
				<!-- { -->
                    <!-- bootbox.hideAll(); -->
					<!-- var dialog = bootbox.dialog({ -->
						<!-- title: 'Sankey Diagram', -->
						<!-- message: "<p><b>To interactively inspect the MDO system there are several options.</b></p>" -->
								 <!-- +"<ol><li>When you hover over one of the competences with the mouse, the respective input and output connections are highlighted. Input connections are highlighted in red, output connections in green.</li>" -->
								 <!-- +"<li> With a right-click on an edge (connecting arrows between the competences) you can take a closer look at the data processed between those competences.</li>" -->
								 <!-- +"<li>Click right on a competence for more information, such as input/output data or a detailed tool description. You can also rearrange the layout by dragging the competences with the computer mouse.</li>" -->
								 <!-- +"<li>To view the full data model, go to the \"Data model\" box and select a variable categorization.</li></ol><br />" -->
								 <!-- +"<p>To switch to another visualization (<i>XDSM</i> or <i>Edge Bundles</i>), go to the navigation bar and select a graph from the drop down menu.</p>" -->
								 <!-- +"<p>If you need more information on how to use the visualization package, click on the \"Tutorial\" button below.</p>" -->
								 <!-- +"<p><br/>Any questions or feedback? Contact the support team with the \"Feedback\" button below!</p>" -->
						<!-- , -->
						<!-- buttons: theButtons -->
					<!-- }) -->
				<!-- } -->
				<!-- else -->
				<!-- { -->
					<!-- showTutorialPage(); -->
				<!-- } -->
			}
			//#####################################################################//
			
			
			//aigner: Load entire MDO data
			//###############################################################################################################################################################################################################//
			//#####################################################################//
			//#####################################################################//
			//@Imco: here the references to all files are done
			var fileReference = {
				Acknowledgements : "static/pictures/Acknowledgements.png",
				AGILE_Icon : "static/pictures/AGILE_Icon.png",
				AGILE_Logo : "static/pictures/AGILE_Logo.png",
				Contact : "static/pictures/Contact.png",
				Home: "static/pictures/Home.png",
				RWTH_Logo : "static/pictures/RWTH_Logo.png",
				TUDelft_Logo : "static/pictures/TUDelft_Logo.png",
				Tutorial : "static/pictures/Tutorial.png",
				VISTOMS_Label : "static/pictures/VISTOMS_Label.png",
			}
			
			//@Imco: Here is the sellar problem example data
			theData = "REP__GRAPH_DATA__REP"
			function loadAllData(data)
			{
				makeKadmosMenu(data,true);
				mainPage();
			}
			loadAllData(theData);
			//#####################################################################//
			//#####################################################################//
			//###############################################################################################################################################################################################################//
			
			//aigner: Clear view function
			//#####################################################################//
			function clearView()
			{
				var visualizationScript = d3.select(".visualizationScript");
				if (visualizationScript){visualizationScript.remove();}
				var xdsm = d3.select(".xdsmDiv");							
				if (xdsm){xdsm.remove();}
				var edgeBundles = d3.select(".edgeBundlesDiv");							
				if (edgeBundles){edgeBundles.remove();}
				var sankeyDiagram = d3.select(".sankeyDiagramDiv");
				if (sankeyDiagram){sankeyDiagram.remove();}
                var visualizationTypes = d3.select("#visualizationTypes");
				if (visualizationTypes){visualizationTypes.remove();}
                var technologies = d3.select("#technologies");
				if (technologies){technologies.remove();}
                var acknowledgements = d3.select("#acknowledgements_text");
                if (acknowledgements){acknowledgements.remove();}
                var footer = d3.select("footer");
				if (footer){footer.remove();}
			}
			//#####################################################################//
		
		function xdsm_script(data,graphID)
		{        
			(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
			// https://d3js.org Version 4.3.0. Copyright 2016 Mike Bostock.
			(function (global, factory) {
			  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
			  typeof define === 'function' && define.amd ? define(['exports'], factory) :
			  (factory((global.d3 = global.d3 || {})));
			}(this, (function (exports) { 'use strict';

			var version = "4.3.0";

			var ascending = function(a, b) {
			  return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
			};

			var bisector = function(compare) {
			  if (compare.length === 1) compare = ascendingComparator(compare);
			  return {
				left: function(a, x, lo, hi) {
				  if (lo == null) lo = 0;
				  if (hi == null) hi = a.length;
				  while (lo < hi) {
					var mid = lo + hi >>> 1;
					if (compare(a[mid], x) < 0) lo = mid + 1;
					else hi = mid;
				  }
				  return lo;
				},
				right: function(a, x, lo, hi) {
				  if (lo == null) lo = 0;
				  if (hi == null) hi = a.length;
				  while (lo < hi) {
					var mid = lo + hi >>> 1;
					if (compare(a[mid], x) > 0) hi = mid;
					else lo = mid + 1;
				  }
				  return lo;
				}
			  };
			};

			function ascendingComparator(f) {
			  return function(d, x) {
				return ascending(f(d), x);
			  };
			}

			var ascendingBisect = bisector(ascending);
			var bisectRight = ascendingBisect.right;
			var bisectLeft = ascendingBisect.left;

			var descending = function(a, b) {
			  return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
			};

			var number = function(x) {
			  return x === null ? NaN : +x;
			};

			var variance = function(array, f) {
			  var n = array.length,
				  m = 0,
				  a,
				  d,
				  s = 0,
				  i = -1,
				  j = 0;

			  if (f == null) {
				while (++i < n) {
				  if (!isNaN(a = number(array[i]))) {
					d = a - m;
					m += d / ++j;
					s += d * (a - m);
				  }
				}
			  }

			  else {
				while (++i < n) {
				  if (!isNaN(a = number(f(array[i], i, array)))) {
					d = a - m;
					m += d / ++j;
					s += d * (a - m);
				  }
				}
			  }

			  if (j > 1) return s / (j - 1);
			};

			var deviation = function(array, f) {
			  var v = variance(array, f);
			  return v ? Math.sqrt(v) : v;
			};

			var extent = function(array, f) {
			  var i = -1,
				  n = array.length,
				  a,
				  b,
				  c;

			  if (f == null) {
				while (++i < n) if ((b = array[i]) != null && b >= b) { a = c = b; break; }
				while (++i < n) if ((b = array[i]) != null) {
				  if (a > b) a = b;
				  if (c < b) c = b;
				}
			  }

			  else {
				while (++i < n) if ((b = f(array[i], i, array)) != null && b >= b) { a = c = b; break; }
				while (++i < n) if ((b = f(array[i], i, array)) != null) {
				  if (a > b) a = b;
				  if (c < b) c = b;
				}
			  }

			  return [a, c];
			};

			var array = Array.prototype;

			var slice = array.slice;
			var map = array.map;

			var constant$1 = function(x) {
			  return function() {
				return x;
			  };
			};

			var identity = function(x) {
			  return x;
			};

			var range = function(start, stop, step) {
			  start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;

			  var i = -1,
				  n = Math.max(0, Math.ceil((stop - start) / step)) | 0,
				  range = new Array(n);

			  while (++i < n) {
				range[i] = start + i * step;
			  }

			  return range;
			};

			var e10 = Math.sqrt(50);
			var e5 = Math.sqrt(10);
			var e2 = Math.sqrt(2);

			var ticks = function(start, stop, count) {
			  var step = tickStep(start, stop, count);
			  return range(
				Math.ceil(start / step) * step,
				Math.floor(stop / step) * step + step / 2, // inclusive
				step
			  );
			};

			function tickStep(start, stop, count) {
			  var step0 = Math.abs(stop - start) / Math.max(0, count),
				  step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
				  error = step0 / step1;
			  if (error >= e10) step1 *= 10;
			  else if (error >= e5) step1 *= 5;
			  else if (error >= e2) step1 *= 2;
			  return stop < start ? -step1 : step1;
			}

			var sturges = function(values) {
			  return Math.ceil(Math.log(values.length) / Math.LN2) + 1;
			};

			var histogram = function() {
			  var value = identity,
				  domain = extent,
				  threshold = sturges;

			  function histogram(data) {
				var i,
					n = data.length,
					x,
					values = new Array(n);

				for (i = 0; i < n; ++i) {
				  values[i] = value(data[i], i, data);
				}

				var xz = domain(values),
					x0 = xz[0],
					x1 = xz[1],
					tz = threshold(values, x0, x1);

				// Convert number of thresholds into uniform thresholds.
				if (!Array.isArray(tz)) tz = ticks(x0, x1, tz);

				// Remove any thresholds outside the domain.
				var m = tz.length;
				while (tz[0] <= x0) tz.shift(), --m;
				while (tz[m - 1] >= x1) tz.pop(), --m;

				var bins = new Array(m + 1),
					bin;

				// Initialize bins.
				for (i = 0; i <= m; ++i) {
				  bin = bins[i] = [];
				  bin.x0 = i > 0 ? tz[i - 1] : x0;
				  bin.x1 = i < m ? tz[i] : x1;
				}

				// Assign data to bins by value, ignoring any outside the domain.
				for (i = 0; i < n; ++i) {
				  x = values[i];
				  if (x0 <= x && x <= x1) {
					bins[bisectRight(tz, x, 0, m)].push(data[i]);
				  }
				}

				return bins;
			  }

			  histogram.value = function(_) {
				return arguments.length ? (value = typeof _ === "function" ? _ : constant$1(_), histogram) : value;
			  };

			  histogram.domain = function(_) {
				return arguments.length ? (domain = typeof _ === "function" ? _ : constant$1([_[0], _[1]]), histogram) : domain;
			  };

			  histogram.thresholds = function(_) {
				return arguments.length ? (threshold = typeof _ === "function" ? _ : Array.isArray(_) ? constant$1(slice.call(_)) : constant$1(_), histogram) : threshold;
			  };

			  return histogram;
			};

			var threshold = function(array, p, f) {
			  if (f == null) f = number;
			  if (!(n = array.length)) return;
			  if ((p = +p) <= 0 || n < 2) return +f(array[0], 0, array);
			  if (p >= 1) return +f(array[n - 1], n - 1, array);
			  var n,
				  h = (n - 1) * p,
				  i = Math.floor(h),
				  a = +f(array[i], i, array),
				  b = +f(array[i + 1], i + 1, array);
			  return a + (b - a) * (h - i);
			};

			var freedmanDiaconis = function(values, min, max) {
			  values = map.call(values, number).sort(ascending);
			  return Math.ceil((max - min) / (2 * (threshold(values, 0.75) - threshold(values, 0.25)) * Math.pow(values.length, -1 / 3)));
			};

			var scott = function(values, min, max) {
			  return Math.ceil((max - min) / (3.5 * deviation(values) * Math.pow(values.length, -1 / 3)));
			};

			var max = function(array, f) {
			  var i = -1,
				  n = array.length,
				  a,
				  b;

			  if (f == null) {
				while (++i < n) if ((b = array[i]) != null && b >= b) { a = b; break; }
				while (++i < n) if ((b = array[i]) != null && b > a) a = b;
			  }

			  else {
				while (++i < n) if ((b = f(array[i], i, array)) != null && b >= b) { a = b; break; }
				while (++i < n) if ((b = f(array[i], i, array)) != null && b > a) a = b;
			  }

			  return a;
			};

			var mean = function(array, f) {
			  var s = 0,
				  n = array.length,
				  a,
				  i = -1,
				  j = n;

			  if (f == null) {
				while (++i < n) if (!isNaN(a = number(array[i]))) s += a; else --j;
			  }

			  else {
				while (++i < n) if (!isNaN(a = number(f(array[i], i, array)))) s += a; else --j;
			  }

			  if (j) return s / j;
			};

			var median = function(array, f) {
			  var numbers = [],
				  n = array.length,
				  a,
				  i = -1;

			  if (f == null) {
				while (++i < n) if (!isNaN(a = number(array[i]))) numbers.push(a);
			  }

			  else {
				while (++i < n) if (!isNaN(a = number(f(array[i], i, array)))) numbers.push(a);
			  }

			  return threshold(numbers.sort(ascending), 0.5);
			};

			var merge = function(arrays) {
			  var n = arrays.length,
				  m,
				  i = -1,
				  j = 0,
				  merged,
				  array;

			  while (++i < n) j += arrays[i].length;
			  merged = new Array(j);

			  while (--n >= 0) {
				array = arrays[n];
				m = array.length;
				while (--m >= 0) {
				  merged[--j] = array[m];
				}
			  }

			  return merged;
			};

			var min = function(array, f) {
			  var i = -1,
				  n = array.length,
				  a,
				  b;

			  if (f == null) {
				while (++i < n) if ((b = array[i]) != null && b >= b) { a = b; break; }
				while (++i < n) if ((b = array[i]) != null && a > b) a = b;
			  }

			  else {
				while (++i < n) if ((b = f(array[i], i, array)) != null && b >= b) { a = b; break; }
				while (++i < n) if ((b = f(array[i], i, array)) != null && a > b) a = b;
			  }

			  return a;
			};

			var pairs = function(array) {
			  var i = 0, n = array.length - 1, p = array[0], pairs = new Array(n < 0 ? 0 : n);
			  while (i < n) pairs[i] = [p, p = array[++i]];
			  return pairs;
			};

			var permute = function(array, indexes) {
			  var i = indexes.length, permutes = new Array(i);
			  while (i--) permutes[i] = array[indexes[i]];
			  return permutes;
			};

			var scan = function(array, compare) {
			  if (!(n = array.length)) return;
			  var i = 0,
				  n,
				  j = 0,
				  xi,
				  xj = array[j];

			  if (!compare) compare = ascending;

			  while (++i < n) if (compare(xi = array[i], xj) < 0 || compare(xj, xj) !== 0) xj = xi, j = i;

			  if (compare(xj, xj) === 0) return j;
			};

			var shuffle = function(array, i0, i1) {
			  var m = (i1 == null ? array.length : i1) - (i0 = i0 == null ? 0 : +i0),
				  t,
				  i;

			  while (m) {
				i = Math.random() * m-- | 0;
				t = array[m + i0];
				array[m + i0] = array[i + i0];
				array[i + i0] = t;
			  }

			  return array;
			};

			var sum = function(array, f) {
			  var s = 0,
				  n = array.length,
				  a,
				  i = -1;

			  if (f == null) {
				while (++i < n) if (a = +array[i]) s += a; // Note: zero and null are equivalent.
			  }

			  else {
				while (++i < n) if (a = +f(array[i], i, array)) s += a;
			  }

			  return s;
			};

			var transpose = function(matrix) {
			  if (!(n = matrix.length)) return [];
			  for (var i = -1, m = min(matrix, length), transpose = new Array(m); ++i < m;) {
				for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n;) {
				  row[j] = matrix[j][i];
				}
			  }
			  return transpose;
			};

			function length(d) {
			  return d.length;
			}

			var zip = function() {
			  return transpose(arguments);
			};

			var prefix = "$";

			function Map() {}

			Map.prototype = map$1.prototype = {
			  constructor: Map,
			  has: function(key) {
				return (prefix + key) in this;
			  },
			  get: function(key) {
				return this[prefix + key];
			  },
			  set: function(key, value) {
				this[prefix + key] = value;
				return this;
			  },
			  remove: function(key) {
				var property = prefix + key;
				return property in this && delete this[property];
			  },
			  clear: function() {
				for (var property in this) if (property[0] === prefix) delete this[property];
			  },
			  keys: function() {
				var keys = [];
				for (var property in this) if (property[0] === prefix) keys.push(property.slice(1));
				return keys;
			  },
			  values: function() {
				var values = [];
				for (var property in this) if (property[0] === prefix) values.push(this[property]);
				return values;
			  },
			  entries: function() {
				var entries = [];
				for (var property in this) if (property[0] === prefix) entries.push({key: property.slice(1), value: this[property]});
				return entries;
			  },
			  size: function() {
				var size = 0;
				for (var property in this) if (property[0] === prefix) ++size;
				return size;
			  },
			  empty: function() {
				for (var property in this) if (property[0] === prefix) return false;
				return true;
			  },
			  each: function(f) {
				for (var property in this) if (property[0] === prefix) f(this[property], property.slice(1), this);
			  }
			};

			function map$1(object, f) {
			  var map = new Map;

			  // Copy constructor.
			  if (object instanceof Map) object.each(function(value, key) { map.set(key, value); });

			  // Index array by numeric index or specified key function.
			  else if (Array.isArray(object)) {
				var i = -1,
					n = object.length,
					o;

				if (f == null) while (++i < n) map.set(i, object[i]);
				else while (++i < n) map.set(f(o = object[i], i, object), o);
			  }

			  // Convert object to map.
			  else if (object) for (var key in object) map.set(key, object[key]);

			  return map;
			}

			var nest = function() {
			  var keys = [],
				  sortKeys = [],
				  sortValues,
				  rollup,
				  nest;

			  function apply(array, depth, createResult, setResult) {
				if (depth >= keys.length) return rollup != null
					? rollup(array) : (sortValues != null
					? array.sort(sortValues)
					: array);

				var i = -1,
					n = array.length,
					key = keys[depth++],
					keyValue,
					value,
					valuesByKey = map$1(),
					values,
					result = createResult();

				while (++i < n) {
				  if (values = valuesByKey.get(keyValue = key(value = array[i]) + "")) {
					values.push(value);
				  } else {
					valuesByKey.set(keyValue, [value]);
				  }
				}

				valuesByKey.each(function(values, key) {
				  setResult(result, key, apply(values, depth, createResult, setResult));
				});

				return result;
			  }

			  function entries(map, depth) {
				if (++depth > keys.length) return map;
				var array, sortKey = sortKeys[depth - 1];
				if (rollup != null && depth >= keys.length) array = map.entries();
				else array = [], map.each(function(v, k) { array.push({key: k, values: entries(v, depth)}); });
				return sortKey != null ? array.sort(function(a, b) { return sortKey(a.key, b.key); }) : array;
			  }

			  return nest = {
				object: function(array) { return apply(array, 0, createObject, setObject); },
				map: function(array) { return apply(array, 0, createMap, setMap); },
				entries: function(array) { return entries(apply(array, 0, createMap, setMap), 0); },
				key: function(d) { keys.push(d); return nest; },
				sortKeys: function(order) { sortKeys[keys.length - 1] = order; return nest; },
				sortValues: function(order) { sortValues = order; return nest; },
				rollup: function(f) { rollup = f; return nest; }
			  };
			};

			function createObject() {
			  return {};
			}

			function setObject(object, key, value) {
			  object[key] = value;
			}

			function createMap() {
			  return map$1();
			}

			function setMap(map, key, value) {
			  map.set(key, value);
			}

			function Set() {}

			var proto = map$1.prototype;

			Set.prototype = set.prototype = {
			  constructor: Set,
			  has: proto.has,
			  add: function(value) {
				value += "";
				this[prefix + value] = value;
				return this;
			  },
			  remove: proto.remove,
			  clear: proto.clear,
			  values: proto.keys,
			  size: proto.size,
			  empty: proto.empty,
			  each: proto.each
			};

			function set(object, f) {
			  var set = new Set;

			  // Copy constructor.
			  if (object instanceof Set) object.each(function(value) { set.add(value); });

			  // Otherwise, assume it’s an array.
			  else if (object) {
				var i = -1, n = object.length;
				if (f == null) while (++i < n) set.add(object[i]);
				else while (++i < n) set.add(f(object[i], i, object));
			  }

			  return set;
			}

			var keys = function(map) {
			  var keys = [];
			  for (var key in map) keys.push(key);
			  return keys;
			};

			var values = function(map) {
			  var values = [];
			  for (var key in map) values.push(map[key]);
			  return values;
			};

			var entries = function(map) {
			  var entries = [];
			  for (var key in map) entries.push({key: key, value: map[key]});
			  return entries;
			};

			var uniform = function(min, max) {
			  min = min == null ? 0 : +min;
			  max = max == null ? 1 : +max;
			  if (arguments.length === 1) max = min, min = 0;
			  else max -= min;
			  return function() {
				return Math.random() * max + min;
			  };
			};

			var normal = function(mu, sigma) {
			  var x, r;
			  mu = mu == null ? 0 : +mu;
			  sigma = sigma == null ? 1 : +sigma;
			  return function() {
				var y;

				// If available, use the second previously-generated uniform random.
				if (x != null) y = x, x = null;

				// Otherwise, generate a new x and y.
				else do {
				  x = Math.random() * 2 - 1;
				  y = Math.random() * 2 - 1;
				  r = x * x + y * y;
				} while (!r || r > 1);

				return mu + sigma * y * Math.sqrt(-2 * Math.log(r) / r);
			  };
			};

			var logNormal = function() {
			  var randomNormal = normal.apply(this, arguments);
			  return function() {
				return Math.exp(randomNormal());
			  };
			};

			var irwinHall = function(n) {
			  return function() {
				for (var sum = 0, i = 0; i < n; ++i) sum += Math.random();
				return sum;
			  };
			};

			var bates = function(n) {
			  var randomIrwinHall = irwinHall(n);
			  return function() {
				return randomIrwinHall() / n;
			  };
			};

			var exponential = function(lambda) {
			  return function() {
				return -Math.log(1 - Math.random()) / lambda;
			  };
			};

			function linear(t) {
			  return +t;
			}

			function quadIn(t) {
			  return t * t;
			}

			function quadOut(t) {
			  return t * (2 - t);
			}

			function quadInOut(t) {
			  return ((t *= 2) <= 1 ? t * t : --t * (2 - t) + 1) / 2;
			}

			function cubicIn(t) {
			  return t * t * t;
			}

			function cubicOut(t) {
			  return --t * t * t + 1;
			}

			function cubicInOut(t) {
			  return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;
			}

			var exponent = 3;

			var polyIn = (function custom(e) {
			  e = +e;

			  function polyIn(t) {
				return Math.pow(t, e);
			  }

			  polyIn.exponent = custom;

			  return polyIn;
			})(exponent);

			var polyOut = (function custom(e) {
			  e = +e;

			  function polyOut(t) {
				return 1 - Math.pow(1 - t, e);
			  }

			  polyOut.exponent = custom;

			  return polyOut;
			})(exponent);

			var polyInOut = (function custom(e) {
			  e = +e;

			  function polyInOut(t) {
				return ((t *= 2) <= 1 ? Math.pow(t, e) : 2 - Math.pow(2 - t, e)) / 2;
			  }

			  polyInOut.exponent = custom;

			  return polyInOut;
			})(exponent);

			var pi = Math.PI;
			var halfPi = pi / 2;

			function sinIn(t) {
			  return 1 - Math.cos(t * halfPi);
			}

			function sinOut(t) {
			  return Math.sin(t * halfPi);
			}

			function sinInOut(t) {
			  return (1 - Math.cos(pi * t)) / 2;
			}

			function expIn(t) {
			  return Math.pow(2, 10 * t - 10);
			}

			function expOut(t) {
			  return 1 - Math.pow(2, -10 * t);
			}

			function expInOut(t) {
			  return ((t *= 2) <= 1 ? Math.pow(2, 10 * t - 10) : 2 - Math.pow(2, 10 - 10 * t)) / 2;
			}

			function circleIn(t) {
			  return 1 - Math.sqrt(1 - t * t);
			}

			function circleOut(t) {
			  return Math.sqrt(1 - --t * t);
			}

			function circleInOut(t) {
			  return ((t *= 2) <= 1 ? 1 - Math.sqrt(1 - t * t) : Math.sqrt(1 - (t -= 2) * t) + 1) / 2;
			}

			var b1 = 4 / 11;
			var b2 = 6 / 11;
			var b3 = 8 / 11;
			var b4 = 3 / 4;
			var b5 = 9 / 11;
			var b6 = 10 / 11;
			var b7 = 15 / 16;
			var b8 = 21 / 22;
			var b9 = 63 / 64;
			var b0 = 1 / b1 / b1;

			function bounceIn(t) {
			  return 1 - bounceOut(1 - t);
			}

			function bounceOut(t) {
			  return (t = +t) < b1 ? b0 * t * t : t < b3 ? b0 * (t -= b2) * t + b4 : t < b6 ? b0 * (t -= b5) * t + b7 : b0 * (t -= b8) * t + b9;
			}

			function bounceInOut(t) {
			  return ((t *= 2) <= 1 ? 1 - bounceOut(1 - t) : bounceOut(t - 1) + 1) / 2;
			}

			var overshoot = 1.70158;

			var backIn = (function custom(s) {
			  s = +s;

			  function backIn(t) {
				return t * t * ((s + 1) * t - s);
			  }

			  backIn.overshoot = custom;

			  return backIn;
			})(overshoot);

			var backOut = (function custom(s) {
			  s = +s;

			  function backOut(t) {
				return --t * t * ((s + 1) * t + s) + 1;
			  }

			  backOut.overshoot = custom;

			  return backOut;
			})(overshoot);

			var backInOut = (function custom(s) {
			  s = +s;

			  function backInOut(t) {
				return ((t *= 2) < 1 ? t * t * ((s + 1) * t - s) : (t -= 2) * t * ((s + 1) * t + s) + 2) / 2;
			  }

			  backInOut.overshoot = custom;

			  return backInOut;
			})(overshoot);

			var tau = 2 * Math.PI;
			var amplitude = 1;
			var period = 0.3;

			var elasticIn = (function custom(a, p) {
			  var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);

			  function elasticIn(t) {
				return a * Math.pow(2, 10 * --t) * Math.sin((s - t) / p);
			  }

			  elasticIn.amplitude = function(a) { return custom(a, p * tau); };
			  elasticIn.period = function(p) { return custom(a, p); };

			  return elasticIn;
			})(amplitude, period);

			var elasticOut = (function custom(a, p) {
			  var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);

			  function elasticOut(t) {
				return 1 - a * Math.pow(2, -10 * (t = +t)) * Math.sin((t + s) / p);
			  }

			  elasticOut.amplitude = function(a) { return custom(a, p * tau); };
			  elasticOut.period = function(p) { return custom(a, p); };

			  return elasticOut;
			})(amplitude, period);

			var elasticInOut = (function custom(a, p) {
			  var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);

			  function elasticInOut(t) {
				return ((t = t * 2 - 1) < 0
					? a * Math.pow(2, 10 * t) * Math.sin((s - t) / p)
					: 2 - a * Math.pow(2, -10 * t) * Math.sin((s + t) / p)) / 2;
			  }

			  elasticInOut.amplitude = function(a) { return custom(a, p * tau); };
			  elasticInOut.period = function(p) { return custom(a, p); };

			  return elasticInOut;
			})(amplitude, period);

			var area = function(polygon) {
			  var i = -1,
				  n = polygon.length,
				  a,
				  b = polygon[n - 1],
				  area = 0;

			  while (++i < n) {
				a = b;
				b = polygon[i];
				area += a[1] * b[0] - a[0] * b[1];
			  }

			  return area / 2;
			};

			var centroid = function(polygon) {
			  var i = -1,
				  n = polygon.length,
				  x = 0,
				  y = 0,
				  a,
				  b = polygon[n - 1],
				  c,
				  k = 0;

			  while (++i < n) {
				a = b;
				b = polygon[i];
				k += c = a[0] * b[1] - b[0] * a[1];
				x += (a[0] + b[0]) * c;
				y += (a[1] + b[1]) * c;
			  }

			  return k *= 3, [x / k, y / k];
			};

			// Returns the 2D cross product of AB and AC vectors, i.e., the z-component of
			// the 3D cross product in a quadrant I Cartesian coordinate system (+x is
			// right, +y is up). Returns a positive value if ABC is counter-clockwise,
			// negative if clockwise, and zero if the points are collinear.
			var cross = function(a, b, c) {
			  return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
			};

			function lexicographicOrder(a, b) {
			  return a[0] - b[0] || a[1] - b[1];
			}

			// Computes the upper convex hull per the monotone chain algorithm.
			// Assumes points.length >= 3, is sorted by x, unique in y.
			// Returns an array of indices into points in left-to-right order.
			function computeUpperHullIndexes(points) {
			  var n = points.length,
				  indexes = [0, 1],
				  size = 2;

			  for (var i = 2; i < n; ++i) {
				while (size > 1 && cross(points[indexes[size - 2]], points[indexes[size - 1]], points[i]) <= 0) --size;
				indexes[size++] = i;
			  }

			  return indexes.slice(0, size); // remove popped points
			}

			var hull = function(points) {
			  if ((n = points.length) < 3) return null;

			  var i,
				  n,
				  sortedPoints = new Array(n),
				  flippedPoints = new Array(n);

			  for (i = 0; i < n; ++i) sortedPoints[i] = [+points[i][0], +points[i][1], i];
			  sortedPoints.sort(lexicographicOrder);
			  for (i = 0; i < n; ++i) flippedPoints[i] = [sortedPoints[i][0], -sortedPoints[i][1]];

			  var upperIndexes = computeUpperHullIndexes(sortedPoints),
				  lowerIndexes = computeUpperHullIndexes(flippedPoints);

			  // Construct the hull polygon, removing possible duplicate endpoints.
			  var skipLeft = lowerIndexes[0] === upperIndexes[0],
				  skipRight = lowerIndexes[lowerIndexes.length - 1] === upperIndexes[upperIndexes.length - 1],
				  hull = [];

			  // Add upper hull in right-to-l order.
			  // Then add lower hull in left-to-right order.
			  for (i = upperIndexes.length - 1; i >= 0; --i) hull.push(points[sortedPoints[upperIndexes[i]][2]]);
			  for (i = +skipLeft; i < lowerIndexes.length - skipRight; ++i) hull.push(points[sortedPoints[lowerIndexes[i]][2]]);

			  return hull;
			};

			var contains = function(polygon, point) {
			  var n = polygon.length,
				  p = polygon[n - 1],
				  x = point[0], y = point[1],
				  x0 = p[0], y0 = p[1],
				  x1, y1,
				  inside = false;

			  for (var i = 0; i < n; ++i) {
				p = polygon[i], x1 = p[0], y1 = p[1];
				if (((y1 > y) !== (y0 > y)) && (x < (x0 - x1) * (y - y1) / (y0 - y1) + x1)) inside = !inside;
				x0 = x1, y0 = y1;
			  }

			  return inside;
			};

			var length$1 = function(polygon) {
			  var i = -1,
				  n = polygon.length,
				  b = polygon[n - 1],
				  xa,
				  ya,
				  xb = b[0],
				  yb = b[1],
				  perimeter = 0;

			  while (++i < n) {
				xa = xb;
				ya = yb;
				b = polygon[i];
				xb = b[0];
				yb = b[1];
				xa -= xb;
				ya -= yb;
				perimeter += Math.sqrt(xa * xa + ya * ya);
			  }

			  return perimeter;
			};

			var pi$1 = Math.PI;
			var tau$1 = 2 * pi$1;
			var epsilon = 1e-6;
			var tauEpsilon = tau$1 - epsilon;

			function Path() {
			  this._x0 = this._y0 = // start of current subpath
			  this._x1 = this._y1 = null; // end of current subpath
			  this._ = [];
			}

			function path() {
			  return new Path;
			}

			Path.prototype = path.prototype = {
			  constructor: Path,
			  moveTo: function(x, y) {
				this._.push("M", this._x0 = this._x1 = +x, ",", this._y0 = this._y1 = +y);
			  },
			  closePath: function() {
				if (this._x1 !== null) {
				  this._x1 = this._x0, this._y1 = this._y0;
				  this._.push("Z");
				}
			  },
			  lineTo: function(x, y) {
				this._.push("L", this._x1 = +x, ",", this._y1 = +y);
			  },
			  quadraticCurveTo: function(x1, y1, x, y) {
				this._.push("Q", +x1, ",", +y1, ",", this._x1 = +x, ",", this._y1 = +y);
			  },
			  bezierCurveTo: function(x1, y1, x2, y2, x, y) {
				this._.push("C", +x1, ",", +y1, ",", +x2, ",", +y2, ",", this._x1 = +x, ",", this._y1 = +y);
			  },
			  arcTo: function(x1, y1, x2, y2, r) {
				x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2, r = +r;
				var x0 = this._x1,
					y0 = this._y1,
					x21 = x2 - x1,
					y21 = y2 - y1,
					x01 = x0 - x1,
					y01 = y0 - y1,
					l01_2 = x01 * x01 + y01 * y01;

				// Is the radius negative? Error.
				if (r < 0) throw new Error("negative radius: " + r);

				// Is this path empty? Move to (x1,y1).
				if (this._x1 === null) {
				  this._.push(
					"M", this._x1 = x1, ",", this._y1 = y1
				  );
				}

				// Or, is (x1,y1) coincident with (x0,y0)? Do nothing.
				else if (!(l01_2 > epsilon)) {}

				// Or, are (x0,y0), (x1,y1) and (x2,y2) collinear?
				// Equivalently, is (x1,y1) coincident with (x2,y2)?
				// Or, is the radius zero? Line to (x1,y1).
				else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon) || !r) {
				  this._.push(
					"L", this._x1 = x1, ",", this._y1 = y1
				  );
				}

				// Otherwise, draw an arc!
				else {
				  var x20 = x2 - x0,
					  y20 = y2 - y0,
					  l21_2 = x21 * x21 + y21 * y21,
					  l20_2 = x20 * x20 + y20 * y20,
					  l21 = Math.sqrt(l21_2),
					  l01 = Math.sqrt(l01_2),
					  l = r * Math.tan((pi$1 - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2),
					  t01 = l / l01,
					  t21 = l / l21;

				  // If the start tangent is not coincident with (x0,y0), line to.
				  if (Math.abs(t01 - 1) > epsilon) {
					this._.push(
					  "L", x1 + t01 * x01, ",", y1 + t01 * y01
					);
				  }

				  this._.push(
					"A", r, ",", r, ",0,0,", +(y01 * x20 > x01 * y20), ",", this._x1 = x1 + t21 * x21, ",", this._y1 = y1 + t21 * y21
				  );
				}
			  },
			  arc: function(x, y, r, a0, a1, ccw) {
				x = +x, y = +y, r = +r;
				var dx = r * Math.cos(a0),
					dy = r * Math.sin(a0),
					x0 = x + dx,
					y0 = y + dy,
					cw = 1 ^ ccw,
					da = ccw ? a0 - a1 : a1 - a0;

				// Is the radius negative? Error.
				if (r < 0) throw new Error("negative radius: " + r);

				// Is this path empty? Move to (x0,y0).
				if (this._x1 === null) {
				  this._.push(
					"M", x0, ",", y0
				  );
				}

				// Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0).
				else if (Math.abs(this._x1 - x0) > epsilon || Math.abs(this._y1 - y0) > epsilon) {
				  this._.push(
					"L", x0, ",", y0
				  );
				}

				// Is this arc empty? We’re done.
				if (!r) return;

				// Is this a complete circle? Draw two arcs to complete the circle.
				if (da > tauEpsilon) {
				  this._.push(
					"A", r, ",", r, ",0,1,", cw, ",", x - dx, ",", y - dy,
					"A", r, ",", r, ",0,1,", cw, ",", this._x1 = x0, ",", this._y1 = y0
				  );
				}

				// Otherwise, draw an arc!
				else {
				  if (da < 0) da = da % tau$1 + tau$1;
				  this._.push(
					"A", r, ",", r, ",0,", +(da >= pi$1), ",", cw, ",", this._x1 = x + r * Math.cos(a1), ",", this._y1 = y + r * Math.sin(a1)
				  );
				}
			  },
			  rect: function(x, y, w, h) {
				this._.push("M", this._x0 = this._x1 = +x, ",", this._y0 = this._y1 = +y, "h", +w, "v", +h, "h", -w, "Z");
			  },
			  toString: function() {
				return this._.join("");
			  }
			};

			var tree_add = function(d) {
			  var x = +this._x.call(null, d),
				  y = +this._y.call(null, d);
			  return add(this.cover(x, y), x, y, d);
			};

			function add(tree, x, y, d) {
			  if (isNaN(x) || isNaN(y)) return tree; // ignore invalid points

			  var parent,
				  node = tree._root,
				  leaf = {data: d},
				  x0 = tree._x0,
				  y0 = tree._y0,
				  x1 = tree._x1,
				  y1 = tree._y1,
				  xm,
				  ym,
				  xp,
				  yp,
				  right,
				  bottom,
				  i,
				  j;

			  // If the tree is empty, initialize the root as a leaf.
			  if (!node) return tree._root = leaf, tree;

			  // Find the existing leaf for the new point, or add it.
			  while (node.length) {
				if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
				if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;
				if (parent = node, !(node = node[i = bottom << 1 | right])) return parent[i] = leaf, tree;
			  }

			  // Is the new point is exactly coincident with the existing point?
			  xp = +tree._x.call(null, node.data);
			  yp = +tree._y.call(null, node.data);
			  if (x === xp && y === yp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree;

			  // Otherwise, split the leaf node until the old and new point are separated.
			  do {
				parent = parent ? parent[i] = new Array(4) : tree._root = new Array(4);
				if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
				if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;
			  } while ((i = bottom << 1 | right) === (j = (yp >= ym) << 1 | (xp >= xm)));
			  return parent[j] = node, parent[i] = leaf, tree;
			}

			function addAll(data) {
			  var d, i, n = data.length,
				  x,
				  y,
				  xz = new Array(n),
				  yz = new Array(n),
				  x0 = Infinity,
				  y0 = Infinity,
				  x1 = -Infinity,
				  y1 = -Infinity;

			  // Compute the points and their extent.
			  for (i = 0; i < n; ++i) {
				if (isNaN(x = +this._x.call(null, d = data[i])) || isNaN(y = +this._y.call(null, d))) continue;
				xz[i] = x;
				yz[i] = y;
				if (x < x0) x0 = x;
				if (x > x1) x1 = x;
				if (y < y0) y0 = y;
				if (y > y1) y1 = y;
			  }

			  // If there were no (valid) points, inherit the existing extent.
			  if (x1 < x0) x0 = this._x0, x1 = this._x1;
			  if (y1 < y0) y0 = this._y0, y1 = this._y1;

			  // Expand the tree to cover the new points.
			  this.cover(x0, y0).cover(x1, y1);

			  // Add the new points.
			  for (i = 0; i < n; ++i) {
				add(this, xz[i], yz[i], data[i]);
			  }

			  return this;
			}

			var tree_cover = function(x, y) {
			  if (isNaN(x = +x) || isNaN(y = +y)) return this; // ignore invalid points

			  var x0 = this._x0,
				  y0 = this._y0,
				  x1 = this._x1,
				  y1 = this._y1;

			  // If the quadtree has no extent, initialize them.
			  // Integer extent are necessary so that if we later double the extent,
			  // the existing quadrant boundaries don’t change due to floating point error!
			  if (isNaN(x0)) {
				x1 = (x0 = Math.floor(x)) + 1;
				y1 = (y0 = Math.floor(y)) + 1;
			  }

			  // Otherwise, double repeatedly to cover.
			  else if (x0 > x || x > x1 || y0 > y || y > y1) {
				var z = x1 - x0,
					node = this._root,
					parent,
					i;

				switch (i = (y < (y0 + y1) / 2) << 1 | (x < (x0 + x1) / 2)) {
				  case 0: {
					do parent = new Array(4), parent[i] = node, node = parent;
					while (z *= 2, x1 = x0 + z, y1 = y0 + z, x > x1 || y > y1);
					break;
				  }
				  case 1: {
					do parent = new Array(4), parent[i] = node, node = parent;
					while (z *= 2, x0 = x1 - z, y1 = y0 + z, x0 > x || y > y1);
					break;
				  }
				  case 2: {
					do parent = new Array(4), parent[i] = node, node = parent;
					while (z *= 2, x1 = x0 + z, y0 = y1 - z, x > x1 || y0 > y);
					break;
				  }
				  case 3: {
					do parent = new Array(4), parent[i] = node, node = parent;
					while (z *= 2, x0 = x1 - z, y0 = y1 - z, x0 > x || y0 > y);
					break;
				  }
				}

				if (this._root && this._root.length) this._root = node;
			  }

			  // If the quadtree covers the point already, just return.
			  else return this;

			  this._x0 = x0;
			  this._y0 = y0;
			  this._x1 = x1;
			  this._y1 = y1;
			  return this;
			};

			var tree_data = function() {
			  var data = [];
			  this.visit(function(node) {
				if (!node.length) do data.push(node.data); while (node = node.next)
			  });
			  return data;
			};

			var tree_extent = function(_) {
			  return arguments.length
				  ? this.cover(+_[0][0], +_[0][1]).cover(+_[1][0], +_[1][1])
				  : isNaN(this._x0) ? undefined : [[this._x0, this._y0], [this._x1, this._y1]];
			};

			var Quad = function(node, x0, y0, x1, y1) {
			  this.node = node;
			  this.x0 = x0;
			  this.y0 = y0;
			  this.x1 = x1;
			  this.y1 = y1;
			};

			var tree_find = function(x, y, radius) {
			  var data,
				  x0 = this._x0,
				  y0 = this._y0,
				  x1,
				  y1,
				  x2,
				  y2,
				  x3 = this._x1,
				  y3 = this._y1,
				  quads = [],
				  node = this._root,
				  q,
				  i;

			  if (node) quads.push(new Quad(node, x0, y0, x3, y3));
			  if (radius == null) radius = Infinity;
			  else {
				x0 = x - radius, y0 = y - radius;
				x3 = x + radius, y3 = y + radius;
				radius *= radius;
			  }

			  while (q = quads.pop()) {

				// Stop searching if this quadrant can’t contain a closer node.
				if (!(node = q.node)
					|| (x1 = q.x0) > x3
					|| (y1 = q.y0) > y3
					|| (x2 = q.x1) < x0
					|| (y2 = q.y1) < y0) continue;

				// Bisect the current quadrant.
				if (node.length) {
				  var xm = (x1 + x2) / 2,
					  ym = (y1 + y2) / 2;

				  quads.push(
					new Quad(node[3], xm, ym, x2, y2),
					new Quad(node[2], x1, ym, xm, y2),
					new Quad(node[1], xm, y1, x2, ym),
					new Quad(node[0], x1, y1, xm, ym)
				  );

				  // Visit the closest quadrant first.
				  if (i = (y >= ym) << 1 | (x >= xm)) {
					q = quads[quads.length - 1];
					quads[quads.length - 1] = quads[quads.length - 1 - i];
					quads[quads.length - 1 - i] = q;
				  }
				}

				// Visit this point. (Visiting coincident points isn’t necessary!)
				else {
				  var dx = x - +this._x.call(null, node.data),
					  dy = y - +this._y.call(null, node.data),
					  d2 = dx * dx + dy * dy;
				  if (d2 < radius) {
					var d = Math.sqrt(radius = d2);
					x0 = x - d, y0 = y - d;
					x3 = x + d, y3 = y + d;
					data = node.data;
				  }
				}
			  }

			  return data;
			};

			var tree_remove = function(d) {
			  if (isNaN(x = +this._x.call(null, d)) || isNaN(y = +this._y.call(null, d))) return this; // ignore invalid points

			  var parent,
				  node = this._root,
				  retainer,
				  previous,
				  next,
				  x0 = this._x0,
				  y0 = this._y0,
				  x1 = this._x1,
				  y1 = this._y1,
				  x,
				  y,
				  xm,
				  ym,
				  right,
				  bottom,
				  i,
				  j;

			  // If the tree is empty, initialize the root as a leaf.
			  if (!node) return this;

			  // Find the leaf node for the point.
			  // While descending, also retain the deepest parent with a non-removed sibling.
			  if (node.length) while (true) {
				if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
				if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;
				if (!(parent = node, node = node[i = bottom << 1 | right])) return this;
				if (!node.length) break;
				if (parent[(i + 1) & 3] || parent[(i + 2) & 3] || parent[(i + 3) & 3]) retainer = parent, j = i;
			  }

			  // Find the point to remove.
			  while (node.data !== d) if (!(previous = node, node = node.next)) return this;
			  if (next = node.next) delete node.next;

			  // If there are multiple coincident points, remove just the point.
			  if (previous) return (next ? previous.next = next : delete previous.next), this;

			  // If this is the root point, remove it.
			  if (!parent) return this._root = next, this;

			  // Remove this leaf.
			  next ? parent[i] = next : delete parent[i];

			  // If the parent now contains exactly one leaf, collapse superfluous parents.
			  if ((node = parent[0] || parent[1] || parent[2] || parent[3])
				  && node === (parent[3] || parent[2] || parent[1] || parent[0])
				  && !node.length) {
				if (retainer) retainer[j] = node;
				else this._root = node;
			  }

			  return this;
			};

			function removeAll(data) {
			  for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]);
			  return this;
			}

			var tree_root = function() {
			  return this._root;
			};

			var tree_size = function() {
			  var size = 0;
			  this.visit(function(node) {
				if (!node.length) do ++size; while (node = node.next)
			  });
			  return size;
			};

			var tree_visit = function(callback) {
			  var quads = [], q, node = this._root, child, x0, y0, x1, y1;
			  if (node) quads.push(new Quad(node, this._x0, this._y0, this._x1, this._y1));
			  while (q = quads.pop()) {
				if (!callback(node = q.node, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1) && node.length) {
				  var xm = (x0 + x1) / 2, ym = (y0 + y1) / 2;
				  if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1));
				  if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1));
				  if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym));
				  if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym));
				}
			  }
			  return this;
			};

			var tree_visitAfter = function(callback) {
			  var quads = [], next = [], q;
			  if (this._root) quads.push(new Quad(this._root, this._x0, this._y0, this._x1, this._y1));
			  while (q = quads.pop()) {
				var node = q.node;
				if (node.length) {
				  var child, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1, xm = (x0 + x1) / 2, ym = (y0 + y1) / 2;
				  if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym));
				  if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym));
				  if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1));
				  if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1));
				}
				next.push(q);
			  }
			  while (q = next.pop()) {
				callback(q.node, q.x0, q.y0, q.x1, q.y1);
			  }
			  return this;
			};

			function defaultX(d) {
			  return d[0];
			}

			var tree_x = function(_) {
			  return arguments.length ? (this._x = _, this) : this._x;
			};

			function defaultY(d) {
			  return d[1];
			}

			var tree_y = function(_) {
			  return arguments.length ? (this._y = _, this) : this._y;
			};

			function quadtree(nodes, x, y) {
			  var tree = new Quadtree(x == null ? defaultX : x, y == null ? defaultY : y, NaN, NaN, NaN, NaN);
			  return nodes == null ? tree : tree.addAll(nodes);
			}

			function Quadtree(x, y, x0, y0, x1, y1) {
			  this._x = x;
			  this._y = y;
			  this._x0 = x0;
			  this._y0 = y0;
			  this._x1 = x1;
			  this._y1 = y1;
			  this._root = undefined;
			}

			function leaf_copy(leaf) {
			  var copy = {data: leaf.data}, next = copy;
			  while (leaf = leaf.next) next = next.next = {data: leaf.data};
			  return copy;
			}

			var treeProto = quadtree.prototype = Quadtree.prototype;

			treeProto.copy = function() {
			  var copy = new Quadtree(this._x, this._y, this._x0, this._y0, this._x1, this._y1),
				  node = this._root,
				  nodes,
				  child;

			  if (!node) return copy;

			  if (!node.length) return copy._root = leaf_copy(node), copy;

			  nodes = [{source: node, target: copy._root = new Array(4)}];
			  while (node = nodes.pop()) {
				for (var i = 0; i < 4; ++i) {
				  if (child = node.source[i]) {
					if (child.length) nodes.push({source: child, target: node.target[i] = new Array(4)});
					else node.target[i] = leaf_copy(child);
				  }
				}
			  }

			  return copy;
			};

			treeProto.add = tree_add;
			treeProto.addAll = addAll;
			treeProto.cover = tree_cover;
			treeProto.data = tree_data;
			treeProto.extent = tree_extent;
			treeProto.find = tree_find;
			treeProto.remove = tree_remove;
			treeProto.removeAll = removeAll;
			treeProto.root = tree_root;
			treeProto.size = tree_size;
			treeProto.visit = tree_visit;
			treeProto.visitAfter = tree_visitAfter;
			treeProto.x = tree_x;
			treeProto.y = tree_y;

			var slice$1 = [].slice;

			var noabort = {};

			function Queue(size) {
			  if (!(size >= 1)) throw new Error;
			  this._size = size;
			  this._call =
			  this._error = null;
			  this._tasks = [];
			  this._data = [];
			  this._waiting =
			  this._active =
			  this._ended =
			  this._start = 0; // inside a synchronous task callback?
			}

			Queue.prototype = queue.prototype = {
			  constructor: Queue,
			  defer: function(callback) {
				if (typeof callback !== "function" || this._call) throw new Error;
				if (this._error != null) return this;
				var t = slice$1.call(arguments, 1);
				t.push(callback);
				++this._waiting, this._tasks.push(t);
				poke(this);
				return this;
			  },
			  abort: function() {
				if (this._error == null) abort(this, new Error("abort"));
				return this;
			  },
			  await: function(callback) {
				if (typeof callback !== "function" || this._call) throw new Error;
				this._call = function(error, results) { callback.apply(null, [error].concat(results)); };
				maybeNotify(this);
				return this;
			  },
			  awaitAll: function(callback) {
				if (typeof callback !== "function" || this._call) throw new Error;
				this._call = callback;
				maybeNotify(this);
				return this;
			  }
			};

			function poke(q) {
			  if (!q._start) {
				try { start(q); } // let the current task complete
				catch (e) {
				  if (q._tasks[q._ended + q._active - 1]) abort(q, e); // task errored synchronously
				  else if (!q._data) throw e; // await callback errored synchronously
				}
			  }
			}

			function start(q) {
			  while (q._start = q._waiting && q._active < q._size) {
				var i = q._ended + q._active,
					t = q._tasks[i],
					j = t.length - 1,
					c = t[j];
				t[j] = end(q, i);
				--q._waiting, ++q._active;
				t = c.apply(null, t);
				if (!q._tasks[i]) continue; // task finished synchronously
				q._tasks[i] = t || noabort;
			  }
			}

			function end(q, i) {
			  return function(e, r) {
				if (!q._tasks[i]) return; // ignore multiple callbacks
				--q._active, ++q._ended;
				q._tasks[i] = null;
				if (q._error != null) return; // ignore secondary errors
				if (e != null) {
				  abort(q, e);
				} else {
				  q._data[i] = r;
				  if (q._waiting) poke(q);
				  else maybeNotify(q);
				}
			  };
			}

			function abort(q, e) {
			  var i = q._tasks.length, t;
			  q._error = e; // ignore active callbacks
			  q._data = undefined; // allow gc
			  q._waiting = NaN; // prevent starting

			  while (--i >= 0) {
				if (t = q._tasks[i]) {
				  q._tasks[i] = null;
				  if (t.abort) {
					try { t.abort(); }
					catch (e) { /* ignore */ }
				  }
				}
			  }

			  q._active = NaN; // allow notification
			  maybeNotify(q);
			}

			function maybeNotify(q) {
			  if (!q._active && q._call) {
				var d = q._data;
				q._data = undefined; // allow gc
				q._call(q._error, d);
			  }
			}

			function queue(concurrency) {
			  return new Queue(arguments.length ? +concurrency : Infinity);
			}

			var constant$2 = function(x) {
			  return function constant() {
				return x;
			  };
			};

			var epsilon$1 = 1e-12;
			var pi$2 = Math.PI;
			var halfPi$1 = pi$2 / 2;
			var tau$2 = 2 * pi$2;

			function arcInnerRadius(d) {
			  return d.innerRadius;
			}

			function arcOuterRadius(d) {
			  return d.outerRadius;
			}

			function arcStartAngle(d) {
			  return d.startAngle;
			}

			function arcEndAngle(d) {
			  return d.endAngle;
			}

			function arcPadAngle(d) {
			  return d && d.padAngle; // Note: optional!
			}

			function asin(x) {
			  return x >= 1 ? halfPi$1 : x <= -1 ? -halfPi$1 : Math.asin(x);
			}

			function intersect(x0, y0, x1, y1, x2, y2, x3, y3) {
			  var x10 = x1 - x0, y10 = y1 - y0,
				  x32 = x3 - x2, y32 = y3 - y2,
				  t = (x32 * (y0 - y2) - y32 * (x0 - x2)) / (y32 * x10 - x32 * y10);
			  return [x0 + t * x10, y0 + t * y10];
			}

			// Compute perpendicular offset line of length rc.
			// http://mathworld.wolfram.com/Circle-LineIntersection.html
			function cornerTangents(x0, y0, x1, y1, r1, rc, cw) {
			  var x01 = x0 - x1,
				  y01 = y0 - y1,
				  lo = (cw ? rc : -rc) / Math.sqrt(x01 * x01 + y01 * y01),
				  ox = lo * y01,
				  oy = -lo * x01,
				  x11 = x0 + ox,
				  y11 = y0 + oy,
				  x10 = x1 + ox,
				  y10 = y1 + oy,
				  x00 = (x11 + x10) / 2,
				  y00 = (y11 + y10) / 2,
				  dx = x10 - x11,
				  dy = y10 - y11,
				  d2 = dx * dx + dy * dy,
				  r = r1 - rc,
				  D = x11 * y10 - x10 * y11,
				  d = (dy < 0 ? -1 : 1) * Math.sqrt(Math.max(0, r * r * d2 - D * D)),
				  cx0 = (D * dy - dx * d) / d2,
				  cy0 = (-D * dx - dy * d) / d2,
				  cx1 = (D * dy + dx * d) / d2,
				  cy1 = (-D * dx + dy * d) / d2,
				  dx0 = cx0 - x00,
				  dy0 = cy0 - y00,
				  dx1 = cx1 - x00,
				  dy1 = cy1 - y00;

			  // Pick the closer of the two intersection points.
			  // TODO Is there a faster way to determine which intersection to use?
			  if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1;

			  return {
				cx: cx0,
				cy: cy0,
				x01: -ox,
				y01: -oy,
				x11: cx0 * (r1 / r - 1),
				y11: cy0 * (r1 / r - 1)
			  };
			}

			var arc = function() {
			  var innerRadius = arcInnerRadius,
				  outerRadius = arcOuterRadius,
				  cornerRadius = constant$2(0),
				  padRadius = null,
				  startAngle = arcStartAngle,
				  endAngle = arcEndAngle,
				  padAngle = arcPadAngle,
				  context = null;

			  function arc() {
				var buffer,
					r,
					r0 = +innerRadius.apply(this, arguments),
					r1 = +outerRadius.apply(this, arguments),
					a0 = startAngle.apply(this, arguments) - halfPi$1,
					a1 = endAngle.apply(this, arguments) - halfPi$1,
					da = Math.abs(a1 - a0),
					cw = a1 > a0;

				if (!context) context = buffer = path();

				// Ensure that the outer radius is always larger than the inner radius.
				if (r1 < r0) r = r1, r1 = r0, r0 = r;

				// Is it a point?
				if (!(r1 > epsilon$1)) context.moveTo(0, 0);

				// Or is it a circle or annulus?
				else if (da > tau$2 - epsilon$1) {
				  context.moveTo(r1 * Math.cos(a0), r1 * Math.sin(a0));
				  context.arc(0, 0, r1, a0, a1, !cw);
				  if (r0 > epsilon$1) {
					context.moveTo(r0 * Math.cos(a1), r0 * Math.sin(a1));
					context.arc(0, 0, r0, a1, a0, cw);
				  }
				}

				// Or is it a circular or annular sector?
				else {
				  var a01 = a0,
					  a11 = a1,
					  a00 = a0,
					  a10 = a1,
					  da0 = da,
					  da1 = da,
					  ap = padAngle.apply(this, arguments) / 2,
					  rp = (ap > epsilon$1) && (padRadius ? +padRadius.apply(this, arguments) : Math.sqrt(r0 * r0 + r1 * r1)),
					  rc = Math.min(Math.abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments)),
					  rc0 = rc,
					  rc1 = rc,
					  t0,
					  t1;

				  // Apply padding? Note that since r1 ≥ r0, da1 ≥ da0.
				  if (rp > epsilon$1) {
					var p0 = asin(rp / r0 * Math.sin(ap)),
						p1 = asin(rp / r1 * Math.sin(ap));
					if ((da0 -= p0 * 2) > epsilon$1) p0 *= (cw ? 1 : -1), a00 += p0, a10 -= p0;
					else da0 = 0, a00 = a10 = (a0 + a1) / 2;
					if ((da1 -= p1 * 2) > epsilon$1) p1 *= (cw ? 1 : -1), a01 += p1, a11 -= p1;
					else da1 = 0, a01 = a11 = (a0 + a1) / 2;
				  }

				  var x01 = r1 * Math.cos(a01),
					  y01 = r1 * Math.sin(a01),
					  x10 = r0 * Math.cos(a10),
					  y10 = r0 * Math.sin(a10);

				  // Apply rounded corners?
				  if (rc > epsilon$1) {
					var x11 = r1 * Math.cos(a11),
						y11 = r1 * Math.sin(a11),
						x00 = r0 * Math.cos(a00),
						y00 = r0 * Math.sin(a00);

					// Restrict the corner radius according to the sector angle.
					if (da < pi$2) {
					  var oc = da0 > epsilon$1 ? intersect(x01, y01, x00, y00, x11, y11, x10, y10) : [x10, y10],
						  ax = x01 - oc[0],
						  ay = y01 - oc[1],
						  bx = x11 - oc[0],
						  by = y11 - oc[1],
						  kc = 1 / Math.sin(Math.acos((ax * bx + ay * by) / (Math.sqrt(ax * ax + ay * ay) * Math.sqrt(bx * bx + by * by))) / 2),
						  lc = Math.sqrt(oc[0] * oc[0] + oc[1] * oc[1]);
					  rc0 = Math.min(rc, (r0 - lc) / (kc - 1));
					  rc1 = Math.min(rc, (r1 - lc) / (kc + 1));
					}
				  }

				  // Is the sector collapsed to a line?
				  if (!(da1 > epsilon$1)) context.moveTo(x01, y01);

				  // Does the sector’s outer ring have rounded corners?
				  else if (rc1 > epsilon$1) {
					t0 = cornerTangents(x00, y00, x01, y01, r1, rc1, cw);
					t1 = cornerTangents(x11, y11, x10, y10, r1, rc1, cw);

					context.moveTo(t0.cx + t0.x01, t0.cy + t0.y01);

					// Have the corners merged?
					if (rc1 < rc) context.arc(t0.cx, t0.cy, rc1, Math.atan2(t0.y01, t0.x01), Math.atan2(t1.y01, t1.x01), !cw);

					// Otherwise, draw the two corners and the ring.
					else {
					  context.arc(t0.cx, t0.cy, rc1, Math.atan2(t0.y01, t0.x01), Math.atan2(t0.y11, t0.x11), !cw);
					  context.arc(0, 0, r1, Math.atan2(t0.cy + t0.y11, t0.cx + t0.x11), Math.atan2(t1.cy + t1.y11, t1.cx + t1.x11), !cw);
					  context.arc(t1.cx, t1.cy, rc1, Math.atan2(t1.y11, t1.x11), Math.atan2(t1.y01, t1.x01), !cw);
					}
				  }

				  // Or is the outer ring just a circular arc?
				  else context.moveTo(x01, y01), context.arc(0, 0, r1, a01, a11, !cw);

				  // Is there no inner ring, and it’s a circular sector?
				  // Or perhaps it’s an annular sector collapsed due to padding?
				  if (!(r0 > epsilon$1) || !(da0 > epsilon$1)) context.lineTo(x10, y10);

				  // Does the sector’s inner ring (or point) have rounded corners?
				  else if (rc0 > epsilon$1) {
					t0 = cornerTangents(x10, y10, x11, y11, r0, -rc0, cw);
					t1 = cornerTangents(x01, y01, x00, y00, r0, -rc0, cw);

					context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01);

					// Have the corners merged?
					if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, Math.atan2(t0.y01, t0.x01), Math.atan2(t1.y01, t1.x01), !cw);

					// Otherwise, draw the two corners and the ring.
					else {
					  context.arc(t0.cx, t0.cy, rc0, Math.atan2(t0.y01, t0.x01), Math.atan2(t0.y11, t0.x11), !cw);
					  context.arc(0, 0, r0, Math.atan2(t0.cy + t0.y11, t0.cx + t0.x11), Math.atan2(t1.cy + t1.y11, t1.cx + t1.x11), cw);
					  context.arc(t1.cx, t1.cy, rc0, Math.atan2(t1.y11, t1.x11), Math.atan2(t1.y01, t1.x01), !cw);
					}
				  }

				  // Or is the inner ring just a circular arc?
				  else context.arc(0, 0, r0, a10, a00, cw);
				}

				context.closePath();

				if (buffer) return context = null, buffer + "" || null;
			  }

			  arc.centroid = function() {
				var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2,
					a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - pi$2 / 2;
				return [Math.cos(a) * r, Math.sin(a) * r];
			  };

			  arc.innerRadius = function(_) {
				return arguments.length ? (innerRadius = typeof _ === "function" ? _ : constant$2(+_), arc) : innerRadius;
			  };

			  arc.outerRadius = function(_) {
				return arguments.length ? (outerRadius = typeof _ === "function" ? _ : constant$2(+_), arc) : outerRadius;
			  };

			  arc.cornerRadius = function(_) {
				return arguments.length ? (cornerRadius = typeof _ === "function" ? _ : constant$2(+_), arc) : cornerRadius;
			  };

			  arc.padRadius = function(_) {
				return arguments.length ? (padRadius = _ == null ? null : typeof _ === "function" ? _ : constant$2(+_), arc) : padRadius;
			  };

			  arc.startAngle = function(_) {
				return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$2(+_), arc) : startAngle;
			  };

			  arc.endAngle = function(_) {
				return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$2(+_), arc) : endAngle;
			  };

			  arc.padAngle = function(_) {
				return arguments.length ? (padAngle = typeof _ === "function" ? _ : constant$2(+_), arc) : padAngle;
			  };

			  arc.context = function(_) {
				return arguments.length ? ((context = _ == null ? null : _), arc) : context;
			  };

			  return arc;
			};

			function Linear(context) {
			  this._context = context;
			}

			Linear.prototype = {
			  areaStart: function() {
				this._line = 0;
			  },
			  areaEnd: function() {
				this._line = NaN;
			  },
			  lineStart: function() {
				this._point = 0;
			  },
			  lineEnd: function() {
				if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
				this._line = 1 - this._line;
			  },
			  point: function(x, y) {
				x = +x, y = +y;
				switch (this._point) {
				  case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
				  case 1: this._point = 2; // proceed
				  default: this._context.lineTo(x, y); break;
				}
			  }
			};

			var curveLinear = function(context) {
			  return new Linear(context);
			};

			function x(p) {
			  return p[0];
			}

			function y(p) {
			  return p[1];
			}

			var line = function() {
			  var x$$1 = x,
				  y$$1 = y,
				  defined = constant$2(true),
				  context = null,
				  curve = curveLinear,
				  output = null;

			  function line(data) {
				var i,
					n = data.length,
					d,
					defined0 = false,
					buffer;

				if (context == null) output = curve(buffer = path());

				for (i = 0; i <= n; ++i) {
				  if (!(i < n && defined(d = data[i], i, data)) === defined0) {
					if (defined0 = !defined0) output.lineStart();
					else output.lineEnd();
				  }
				  if (defined0) output.point(+x$$1(d, i, data), +y$$1(d, i, data));
				}

				if (buffer) return output = null, buffer + "" || null;
			  }

			  line.x = function(_) {
				return arguments.length ? (x$$1 = typeof _ === "function" ? _ : constant$2(+_), line) : x$$1;
			  };

			  line.y = function(_) {
				return arguments.length ? (y$$1 = typeof _ === "function" ? _ : constant$2(+_), line) : y$$1;
			  };

			  line.defined = function(_) {
				return arguments.length ? (defined = typeof _ === "function" ? _ : constant$2(!!_), line) : defined;
			  };

			  line.curve = function(_) {
				return arguments.length ? (curve = _, context != null && (output = curve(context)), line) : curve;
			  };

			  line.context = function(_) {
				return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), line) : context;
			  };

			  return line;
			};

			var area$1 = function() {
			  var x0 = x,
				  x1 = null,
				  y0 = constant$2(0),
				  y1 = y,
				  defined = constant$2(true),
				  context = null,
				  curve = curveLinear,
				  output = null;

			  function area(data) {
				var i,
					j,
					k,
					n = data.length,
					d,
					defined0 = false,
					buffer,
					x0z = new Array(n),
					y0z = new Array(n);

				if (context == null) output = curve(buffer = path());

				for (i = 0; i <= n; ++i) {
				  if (!(i < n && defined(d = data[i], i, data)) === defined0) {
					if (defined0 = !defined0) {
					  j = i;
					  output.areaStart();
					  output.lineStart();
					} else {
					  output.lineEnd();
					  output.lineStart();
					  for (k = i - 1; k >= j; --k) {
						output.point(x0z[k], y0z[k]);
					  }
					  output.lineEnd();
					  output.areaEnd();
					}
				  }
				  if (defined0) {
					x0z[i] = +x0(d, i, data), y0z[i] = +y0(d, i, data);
					output.point(x1 ? +x1(d, i, data) : x0z[i], y1 ? +y1(d, i, data) : y0z[i]);
				  }
				}

				if (buffer) return output = null, buffer + "" || null;
			  }

			  function arealine() {
				return line().defined(defined).curve(curve).context(context);
			  }

			  area.x = function(_) {
				return arguments.length ? (x0 = typeof _ === "function" ? _ : constant$2(+_), x1 = null, area) : x0;
			  };

			  area.x0 = function(_) {
				return arguments.length ? (x0 = typeof _ === "function" ? _ : constant$2(+_), area) : x0;
			  };

			  area.x1 = function(_) {
				return arguments.length ? (x1 = _ == null ? null : typeof _ === "function" ? _ : constant$2(+_), area) : x1;
			  };

			  area.y = function(_) {
				return arguments.length ? (y0 = typeof _ === "function" ? _ : constant$2(+_), y1 = null, area) : y0;
			  };

			  area.y0 = function(_) {
				return arguments.length ? (y0 = typeof _ === "function" ? _ : constant$2(+_), area) : y0;
			  };

			  area.y1 = function(_) {
				return arguments.length ? (y1 = _ == null ? null : typeof _ === "function" ? _ : constant$2(+_), area) : y1;
			  };

			  area.lineX0 =
			  area.lineY0 = function() {
				return arealine().x(x0).y(y0);
			  };

			  area.lineY1 = function() {
				return arealine().x(x0).y(y1);
			  };

			  area.lineX1 = function() {
				return arealine().x(x1).y(y0);
			  };

			  area.defined = function(_) {
				return arguments.length ? (defined = typeof _ === "function" ? _ : constant$2(!!_), area) : defined;
			  };

			  area.curve = function(_) {
				return arguments.length ? (curve = _, context != null && (output = curve(context)), area) : curve;
			  };

			  area.context = function(_) {
				return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), area) : context;
			  };

			  return area;
			};

			var descending$1 = function(a, b) {
			  return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
			};

			var identity$1 = function(d) {
			  return d;
			};

			var pie = function() {
			  var value = identity$1,
				  sortValues = descending$1,
				  sort = null,
				  startAngle = constant$2(0),
				  endAngle = constant$2(tau$2),
				  padAngle = constant$2(0);

			  function pie(data) {
				var i,
					n = data.length,
					j,
					k,
					sum = 0,
					index = new Array(n),
					arcs = new Array(n),
					a0 = +startAngle.apply(this, arguments),
					da = Math.min(tau$2, Math.max(-tau$2, endAngle.apply(this, arguments) - a0)),
					a1,
					p = Math.min(Math.abs(da) / n, padAngle.apply(this, arguments)),
					pa = p * (da < 0 ? -1 : 1),
					v;

				for (i = 0; i < n; ++i) {
				  if ((v = arcs[index[i] = i] = +value(data[i], i, data)) > 0) {
					sum += v;
				  }
				}

				// Optionally sort the arcs by previously-computed values or by data.
				if (sortValues != null) index.sort(function(i, j) { return sortValues(arcs[i], arcs[j]); });
				else if (sort != null) index.sort(function(i, j) { return sort(data[i], data[j]); });

				// Compute the arcs! They are stored in the original data's order.
				for (i = 0, k = sum ? (da - n * pa) / sum : 0; i < n; ++i, a0 = a1) {
				  j = index[i], v = arcs[j], a1 = a0 + (v > 0 ? v * k : 0) + pa, arcs[j] = {
					data: data[j],
					index: i,
					value: v,
					startAngle: a0,
					endAngle: a1,
					padAngle: p
				  };
				}

				return arcs;
			  }

			  pie.value = function(_) {
				return arguments.length ? (value = typeof _ === "function" ? _ : constant$2(+_), pie) : value;
			  };

			  pie.sortValues = function(_) {
				return arguments.length ? (sortValues = _, sort = null, pie) : sortValues;
			  };

			  pie.sort = function(_) {
				return arguments.length ? (sort = _, sortValues = null, pie) : sort;
			  };

			  pie.startAngle = function(_) {
				return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$2(+_), pie) : startAngle;
			  };

			  pie.endAngle = function(_) {
				return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$2(+_), pie) : endAngle;
			  };

			  pie.padAngle = function(_) {
				return arguments.length ? (padAngle = typeof _ === "function" ? _ : constant$2(+_), pie) : padAngle;
			  };

			  return pie;
			};

			var curveRadialLinear = curveRadial(curveLinear);

			function Radial(curve) {
			  this._curve = curve;
			}

			Radial.prototype = {
			  areaStart: function() {
				this._curve.areaStart();
			  },
			  areaEnd: function() {
				this._curve.areaEnd();
			  },
			  lineStart: function() {
				this._curve.lineStart();
			  },
			  lineEnd: function() {
				this._curve.lineEnd();
			  },
			  point: function(a, r) {
				this._curve.point(r * Math.sin(a), r * -Math.cos(a));
			  }
			};

			function curveRadial(curve) {

			  function radial(context) {
				return new Radial(curve(context));
			  }

			  radial._curve = curve;

			  return radial;
			}

			function radialLine(l) {
			  var c = l.curve;

			  l.angle = l.x, delete l.x;
			  l.radius = l.y, delete l.y;

			  l.curve = function(_) {
				return arguments.length ? c(curveRadial(_)) : c()._curve;
			  };

			  return l;
			}

			var radialLine$1 = function() {
			  return radialLine(line().curve(curveRadialLinear));
			};

			var radialArea = function() {
			  var a = area$1().curve(curveRadialLinear),
				  c = a.curve,
				  x0 = a.lineX0,
				  x1 = a.lineX1,
				  y0 = a.lineY0,
				  y1 = a.lineY1;

			  a.angle = a.x, delete a.x;
			  a.startAngle = a.x0, delete a.x0;
			  a.endAngle = a.x1, delete a.x1;
			  a.radius = a.y, delete a.y;
			  a.innerRadius = a.y0, delete a.y0;
			  a.outerRadius = a.y1, delete a.y1;
			  a.lineStartAngle = function() { return radialLine(x0()); }, delete a.lineX0;
			  a.lineEndAngle = function() { return radialLine(x1()); }, delete a.lineX1;
			  a.lineInnerRadius = function() { return radialLine(y0()); }, delete a.lineY0;
			  a.lineOuterRadius = function() { return radialLine(y1()); }, delete a.lineY1;

			  a.curve = function(_) {
				return arguments.length ? c(curveRadial(_)) : c()._curve;
			  };

			  return a;
			};

			var circle = {
			  draw: function(context, size) {
				var r = Math.sqrt(size / pi$2);
				context.moveTo(r, 0);
				context.arc(0, 0, r, 0, tau$2);
			  }
			};

			var cross$1 = {
			  draw: function(context, size) {
				var r = Math.sqrt(size / 5) / 2;
				context.moveTo(-3 * r, -r);
				context.lineTo(-r, -r);
				context.lineTo(-r, -3 * r);
				context.lineTo(r, -3 * r);
				context.lineTo(r, -r);
				context.lineTo(3 * r, -r);
				context.lineTo(3 * r, r);
				context.lineTo(r, r);
				context.lineTo(r, 3 * r);
				context.lineTo(-r, 3 * r);
				context.lineTo(-r, r);
				context.lineTo(-3 * r, r);
				context.closePath();
			  }
			};

			var tan30 = Math.sqrt(1 / 3);
			var tan30_2 = tan30 * 2;

			var diamond = {
			  draw: function(context, size) {
				var y = Math.sqrt(size / tan30_2),
					x = y * tan30;
				context.moveTo(0, -y);
				context.lineTo(x, 0);
				context.lineTo(0, y);
				context.lineTo(-x, 0);
				context.closePath();
			  }
			};

			var ka = 0.89081309152928522810;
			var kr = Math.sin(pi$2 / 10) / Math.sin(7 * pi$2 / 10);
			var kx = Math.sin(tau$2 / 10) * kr;
			var ky = -Math.cos(tau$2 / 10) * kr;

			var star = {
			  draw: function(context, size) {
				var r = Math.sqrt(size * ka),
					x = kx * r,
					y = ky * r;
				context.moveTo(0, -r);
				context.lineTo(x, y);
				for (var i = 1; i < 5; ++i) {
				  var a = tau$2 * i / 5,
					  c = Math.cos(a),
					  s = Math.sin(a);
				  context.lineTo(s * r, -c * r);
				  context.lineTo(c * x - s * y, s * x + c * y);
				}
				context.closePath();
			  }
			};

			var square = {
			  draw: function(context, size) {
				var w = Math.sqrt(size),
					x = -w / 2;
				context.rect(x, x, w, w);
			  }
			};

			var sqrt3 = Math.sqrt(3);

			var triangle = {
			  draw: function(context, size) {
				var y = -Math.sqrt(size / (sqrt3 * 3));
				context.moveTo(0, y * 2);
				context.lineTo(-sqrt3 * y, -y);
				context.lineTo(sqrt3 * y, -y);
				context.closePath();
			  }
			};

			var c = -0.5;
			var s = Math.sqrt(3) / 2;
			var k = 1 / Math.sqrt(12);
			var a = (k / 2 + 1) * 3;

			var wye = {
			  draw: function(context, size) {
				var r = Math.sqrt(size / a),
					x0 = r / 2,
					y0 = r * k,
					x1 = x0,
					y1 = r * k + r,
					x2 = -x1,
					y2 = y1;
				context.moveTo(x0, y0);
				context.lineTo(x1, y1);
				context.lineTo(x2, y2);
				context.lineTo(c * x0 - s * y0, s * x0 + c * y0);
				context.lineTo(c * x1 - s * y1, s * x1 + c * y1);
				context.lineTo(c * x2 - s * y2, s * x2 + c * y2);
				context.lineTo(c * x0 + s * y0, c * y0 - s * x0);
				context.lineTo(c * x1 + s * y1, c * y1 - s * x1);
				context.lineTo(c * x2 + s * y2, c * y2 - s * x2);
				context.closePath();
			  }
			};

			var symbols = [
			  circle,
			  cross$1,
			  diamond,
			  square,
			  star,
			  triangle,
			  wye
			];

			var symbol = function() {
			  var type = constant$2(circle),
				  size = constant$2(64),
				  context = null;

			  function symbol() {
				var buffer;
				if (!context) context = buffer = path();
				type.apply(this, arguments).draw(context, +size.apply(this, arguments));
				if (buffer) return context = null, buffer + "" || null;
			  }

			  symbol.type = function(_) {
				return arguments.length ? (type = typeof _ === "function" ? _ : constant$2(_), symbol) : type;
			  };

			  symbol.size = function(_) {
				return arguments.length ? (size = typeof _ === "function" ? _ : constant$2(+_), symbol) : size;
			  };

			  symbol.context = function(_) {
				return arguments.length ? (context = _ == null ? null : _, symbol) : context;
			  };

			  return symbol;
			};

			var noop = function() {};

			function point(that, x, y) {
			  that._context.bezierCurveTo(
				(2 * that._x0 + that._x1) / 3,
				(2 * that._y0 + that._y1) / 3,
				(that._x0 + 2 * that._x1) / 3,
				(that._y0 + 2 * that._y1) / 3,
				(that._x0 + 4 * that._x1 + x) / 6,
				(that._y0 + 4 * that._y1 + y) / 6
			  );
			}

			function Basis(context) {
			  this._context = context;
			}

			Basis.prototype = {
			  areaStart: function() {
				this._line = 0;
			  },
			  areaEnd: function() {
				this._line = NaN;
			  },
			  lineStart: function() {
				this._x0 = this._x1 =
				this._y0 = this._y1 = NaN;
				this._point = 0;
			  },
			  lineEnd: function() {
				switch (this._point) {
				  case 3: point(this, this._x1, this._y1); // proceed
				  case 2: this._context.lineTo(this._x1, this._y1); break;
				}
				if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
				this._line = 1 - this._line;
			  },
			  point: function(x, y) {
				x = +x, y = +y;
				switch (this._point) {
				  case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
				  case 1: this._point = 2; break;
				  case 2: this._point = 3; this._context.lineTo((5 * this._x0 + this._x1) / 6, (5 * this._y0 + this._y1) / 6); // proceed
				  default: point(this, x, y); break;
				}
				this._x0 = this._x1, this._x1 = x;
				this._y0 = this._y1, this._y1 = y;
			  }
			};

			var basis = function(context) {
			  return new Basis(context);
			};

			function BasisClosed(context) {
			  this._context = context;
			}

			BasisClosed.prototype = {
			  areaStart: noop,
			  areaEnd: noop,
			  lineStart: function() {
				this._x0 = this._x1 = this._x2 = this._x3 = this._x4 =
				this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = NaN;
				this._point = 0;
			  },
			  lineEnd: function() {
				switch (this._point) {
				  case 1: {
					this._context.moveTo(this._x2, this._y2);
					this._context.closePath();
					break;
				  }
				  case 2: {
					this._context.moveTo((this._x2 + 2 * this._x3) / 3, (this._y2 + 2 * this._y3) / 3);
					this._context.lineTo((this._x3 + 2 * this._x2) / 3, (this._y3 + 2 * this._y2) / 3);
					this._context.closePath();
					break;
				  }
				  case 3: {
					this.point(this._x2, this._y2);
					this.point(this._x3, this._y3);
					this.point(this._x4, this._y4);
					break;
				  }
				}
			  },
			  point: function(x, y) {
				x = +x, y = +y;
				switch (this._point) {
				  case 0: this._point = 1; this._x2 = x, this._y2 = y; break;
				  case 1: this._point = 2; this._x3 = x, this._y3 = y; break;
				  case 2: this._point = 3; this._x4 = x, this._y4 = y; this._context.moveTo((this._x0 + 4 * this._x1 + x) / 6, (this._y0 + 4 * this._y1 + y) / 6); break;
				  default: point(this, x, y); break;
				}
				this._x0 = this._x1, this._x1 = x;
				this._y0 = this._y1, this._y1 = y;
			  }
			};

			var basisClosed = function(context) {
			  return new BasisClosed(context);
			};

			function BasisOpen(context) {
			  this._context = context;
			}

			BasisOpen.prototype = {
			  areaStart: function() {
				this._line = 0;
			  },
			  areaEnd: function() {
				this._line = NaN;
			  },
			  lineStart: function() {
				this._x0 = this._x1 =
				this._y0 = this._y1 = NaN;
				this._point = 0;
			  },
			  lineEnd: function() {
				if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();
				this._line = 1 - this._line;
			  },
			  point: function(x, y) {
				x = +x, y = +y;
				switch (this._point) {
				  case 0: this._point = 1; break;
				  case 1: this._point = 2; break;
				  case 2: this._point = 3; var x0 = (this._x0 + 4 * this._x1 + x) / 6, y0 = (this._y0 + 4 * this._y1 + y) / 6; this._line ? this._context.lineTo(x0, y0) : this._context.moveTo(x0, y0); break;
				  case 3: this._point = 4; // proceed
				  default: point(this, x, y); break;
				}
				this._x0 = this._x1, this._x1 = x;
				this._y0 = this._y1, this._y1 = y;
			  }
			};

			var basisOpen = function(context) {
			  return new BasisOpen(context);
			};

			function Bundle(context, beta) {
			  this._basis = new Basis(context);
			  this._beta = beta;
			}

			Bundle.prototype = {
			  lineStart: function() {
				this._x = [];
				this._y = [];
				this._basis.lineStart();
			  },
			  lineEnd: function() {
				var x = this._x,
					y = this._y,
					j = x.length - 1;

				if (j > 0) {
				  var x0 = x[0],
					  y0 = y[0],
					  dx = x[j] - x0,
					  dy = y[j] - y0,
					  i = -1,
					  t;

				  while (++i <= j) {
					t = i / j;
					this._basis.point(
					  this._beta * x[i] + (1 - this._beta) * (x0 + t * dx),
					  this._beta * y[i] + (1 - this._beta) * (y0 + t * dy)
					);
				  }
				}

				this._x = this._y = null;
				this._basis.lineEnd();
			  },
			  point: function(x, y) {
				this._x.push(+x);
				this._y.push(+y);
			  }
			};

			var bundle = (function custom(beta) {

			  function bundle(context) {
				return beta === 1 ? new Basis(context) : new Bundle(context, beta);
			  }

			  bundle.beta = function(beta) {
				return custom(+beta);
			  };

			  return bundle;
			})(0.85);

			function point$1(that, x, y) {
			  that._context.bezierCurveTo(
				that._x1 + that._k * (that._x2 - that._x0),
				that._y1 + that._k * (that._y2 - that._y0),
				that._x2 + that._k * (that._x1 - x),
				that._y2 + that._k * (that._y1 - y),
				that._x2,
				that._y2
			  );
			}

			function Cardinal(context, tension) {
			  this._context = context;
			  this._k = (1 - tension) / 6;
			}

			Cardinal.prototype = {
			  areaStart: function() {
				this._line = 0;
			  },
			  areaEnd: function() {
				this._line = NaN;
			  },
			  lineStart: function() {
				this._x0 = this._x1 = this._x2 =
				this._y0 = this._y1 = this._y2 = NaN;
				this._point = 0;
			  },
			  lineEnd: function() {
				switch (this._point) {
				  case 2: this._context.lineTo(this._x2, this._y2); break;
				  case 3: point$1(this, this._x1, this._y1); break;
				}
				if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
				this._line = 1 - this._line;
			  },
			  point: function(x, y) {
				x = +x, y = +y;
				switch (this._point) {
				  case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
				  case 1: this._point = 2; this._x1 = x, this._y1 = y; break;
				  case 2: this._point = 3; // proceed
				  default: point$1(this, x, y); break;
				}
				this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
				this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
			  }
			};

			var cardinal = (function custom(tension) {

			  function cardinal(context) {
				return new Cardinal(context, tension);
			  }

			  cardinal.tension = function(tension) {
				return custom(+tension);
			  };

			  return cardinal;
			})(0);

			function CardinalClosed(context, tension) {
			  this._context = context;
			  this._k = (1 - tension) / 6;
			}

			CardinalClosed.prototype = {
			  areaStart: noop,
			  areaEnd: noop,
			  lineStart: function() {
				this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 =
				this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;
				this._point = 0;
			  },
			  lineEnd: function() {
				switch (this._point) {
				  case 1: {
					this._context.moveTo(this._x3, this._y3);
					this._context.closePath();
					break;
				  }
				  case 2: {
					this._context.lineTo(this._x3, this._y3);
					this._context.closePath();
					break;
				  }
				  case 3: {
					this.point(this._x3, this._y3);
					this.point(this._x4, this._y4);
					this.point(this._x5, this._y5);
					break;
				  }
				}
			  },
			  point: function(x, y) {
				x = +x, y = +y;
				switch (this._point) {
				  case 0: this._point = 1; this._x3 = x, this._y3 = y; break;
				  case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break;
				  case 2: this._point = 3; this._x5 = x, this._y5 = y; break;
				  default: point$1(this, x, y); break;
				}
				this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
				this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
			  }
			};

			var cardinalClosed = (function custom(tension) {

			  function cardinal(context) {
				return new CardinalClosed(context, tension);
			  }

			  cardinal.tension = function(tension) {
				return custom(+tension);
			  };

			  return cardinal;
			})(0);

			function CardinalOpen(context, tension) {
			  this._context = context;
			  this._k = (1 - tension) / 6;
			}

			CardinalOpen.prototype = {
			  areaStart: function() {
				this._line = 0;
			  },
			  areaEnd: function() {
				this._line = NaN;
			  },
			  lineStart: function() {
				this._x0 = this._x1 = this._x2 =
				this._y0 = this._y1 = this._y2 = NaN;
				this._point = 0;
			  },
			  lineEnd: function() {
				if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();
				this._line = 1 - this._line;
			  },
			  point: function(x, y) {
				x = +x, y = +y;
				switch (this._point) {
				  case 0: this._point = 1; break;
				  case 1: this._point = 2; break;
				  case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break;
				  case 3: this._point = 4; // proceed
				  default: point$1(this, x, y); break;
				}
				this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
				this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
			  }
			};

			var cardinalOpen = (function custom(tension) {

			  function cardinal(context) {
				return new CardinalOpen(context, tension);
			  }

			  cardinal.tension = function(tension) {
				return custom(+tension);
			  };

			  return cardinal;
			})(0);

			function point$2(that, x, y) {
			  var x1 = that._x1,
				  y1 = that._y1,
				  x2 = that._x2,
				  y2 = that._y2;

			  if (that._l01_a > epsilon$1) {
				var a = 2 * that._l01_2a + 3 * that._l01_a * that._l12_a + that._l12_2a,
					n = 3 * that._l01_a * (that._l01_a + that._l12_a);
				x1 = (x1 * a - that._x0 * that._l12_2a + that._x2 * that._l01_2a) / n;
				y1 = (y1 * a - that._y0 * that._l12_2a + that._y2 * that._l01_2a) / n;
			  }

			  if (that._l23_a > epsilon$1) {
				var b = 2 * that._l23_2a + 3 * that._l23_a * that._l12_a + that._l12_2a,
					m = 3 * that._l23_a * (that._l23_a + that._l12_a);
				x2 = (x2 * b + that._x1 * that._l23_2a - x * that._l12_2a) / m;
				y2 = (y2 * b + that._y1 * that._l23_2a - y * that._l12_2a) / m;
			  }

			  that._context.bezierCurveTo(x1, y1, x2, y2, that._x2, that._y2);
			}

			function CatmullRom(context, alpha) {
			  this._context = context;
			  this._alpha = alpha;
			}

			CatmullRom.prototype = {
			  areaStart: function() {
				this._line = 0;
			  },
			  areaEnd: function() {
				this._line = NaN;
			  },
			  lineStart: function() {
				this._x0 = this._x1 = this._x2 =
				this._y0 = this._y1 = this._y2 = NaN;
				this._l01_a = this._l12_a = this._l23_a =
				this._l01_2a = this._l12_2a = this._l23_2a =
				this._point = 0;
			  },
			  lineEnd: function() {
				switch (this._point) {
				  case 2: this._context.lineTo(this._x2, this._y2); break;
				  case 3: this.point(this._x2, this._y2); break;
				}
				if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
				this._line = 1 - this._line;
			  },
			  point: function(x, y) {
				x = +x, y = +y;

				if (this._point) {
				  var x23 = this._x2 - x,
					  y23 = this._y2 - y;
				  this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));
				}

				switch (this._point) {
				  case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
				  case 1: this._point = 2; break;
				  case 2: this._point = 3; // proceed
				  default: point$2(this, x, y); break;
				}

				this._l01_a = this._l12_a, this._l12_a = this._l23_a;
				this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;
				this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
				this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
			  }
			};

			var catmullRom = (function custom(alpha) {

			  function catmullRom(context) {
				return alpha ? new CatmullRom(context, alpha) : new Cardinal(context, 0);
			  }

			  catmullRom.alpha = function(alpha) {
				return custom(+alpha);
			  };

			  return catmullRom;
			})(0.5);

			function CatmullRomClosed(context, alpha) {
			  this._context = context;
			  this._alpha = alpha;
			}

			CatmullRomClosed.prototype = {
			  areaStart: noop,
			  areaEnd: noop,
			  lineStart: function() {
				this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 =
				this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;
				this._l01_a = this._l12_a = this._l23_a =
				this._l01_2a = this._l12_2a = this._l23_2a =
				this._point = 0;
			  },
			  lineEnd: function() {
				switch (this._point) {
				  case 1: {
					this._context.moveTo(this._x3, this._y3);
					this._context.closePath();
					break;
				  }
				  case 2: {
					this._context.lineTo(this._x3, this._y3);
					this._context.closePath();
					break;
				  }
				  case 3: {
					this.point(this._x3, this._y3);
					this.point(this._x4, this._y4);
					this.point(this._x5, this._y5);
					break;
				  }
				}
			  },
			  point: function(x, y) {
				x = +x, y = +y;

				if (this._point) {
				  var x23 = this._x2 - x,
					  y23 = this._y2 - y;
				  this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));
				}

				switch (this._point) {
				  case 0: this._point = 1; this._x3 = x, this._y3 = y; break;
				  case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break;
				  case 2: this._point = 3; this._x5 = x, this._y5 = y; break;
				  default: point$2(this, x, y); break;
				}

				this._l01_a = this._l12_a, this._l12_a = this._l23_a;
				this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;
				this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
				this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
			  }
			};

			var catmullRomClosed = (function custom(alpha) {

			  function catmullRom(context) {
				return alpha ? new CatmullRomClosed(context, alpha) : new CardinalClosed(context, 0);
			  }

			  catmullRom.alpha = function(alpha) {
				return custom(+alpha);
			  };

			  return catmullRom;
			})(0.5);

			function CatmullRomOpen(context, alpha) {
			  this._context = context;
			  this._alpha = alpha;
			}

			CatmullRomOpen.prototype = {
			  areaStart: function() {
				this._line = 0;
			  },
			  areaEnd: function() {
				this._line = NaN;
			  },
			  lineStart: function() {
				this._x0 = this._x1 = this._x2 =
				this._y0 = this._y1 = this._y2 = NaN;
				this._l01_a = this._l12_a = this._l23_a =
				this._l01_2a = this._l12_2a = this._l23_2a =
				this._point = 0;
			  },
			  lineEnd: function() {
				if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();
				this._line = 1 - this._line;
			  },
			  point: function(x, y) {
				x = +x, y = +y;

				if (this._point) {
				  var x23 = this._x2 - x,
					  y23 = this._y2 - y;
				  this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));
				}

				switch (this._point) {
				  case 0: this._point = 1; break;
				  case 1: this._point = 2; break;
				  case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break;
				  case 3: this._point = 4; // proceed
				  default: point$2(this, x, y); break;
				}

				this._l01_a = this._l12_a, this._l12_a = this._l23_a;
				this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;
				this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
				this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
			  }
			};

			var catmullRomOpen = (function custom(alpha) {

			  function catmullRom(context) {
				return alpha ? new CatmullRomOpen(context, alpha) : new CardinalOpen(context, 0);
			  }

			  catmullRom.alpha = function(alpha) {
				return custom(+alpha);
			  };

			  return catmullRom;
			})(0.5);

			function LinearClosed(context) {
			  this._context = context;
			}

			LinearClosed.prototype = {
			  areaStart: noop,
			  areaEnd: noop,
			  lineStart: function() {
				this._point = 0;
			  },
			  lineEnd: function() {
				if (this._point) this._context.closePath();
			  },
			  point: function(x, y) {
				x = +x, y = +y;
				if (this._point) this._context.lineTo(x, y);
				else this._point = 1, this._context.moveTo(x, y);
			  }
			};

			var linearClosed = function(context) {
			  return new LinearClosed(context);
			};

			function sign(x) {
			  return x < 0 ? -1 : 1;
			}

			// Calculate the slopes of the tangents (Hermite-type interpolation) based on
			// the following paper: Steffen, M. 1990. A Simple Method for Monotonic
			// Interpolation in One Dimension. Astronomy and Astrophysics, Vol. 239, NO.
			// NOV(II), P. 443, 1990.
			function slope3(that, x2, y2) {
			  var h0 = that._x1 - that._x0,
				  h1 = x2 - that._x1,
				  s0 = (that._y1 - that._y0) / (h0 || h1 < 0 && -0),
				  s1 = (y2 - that._y1) / (h1 || h0 < 0 && -0),
				  p = (s0 * h1 + s1 * h0) / (h0 + h1);
			  return (sign(s0) + sign(s1)) * Math.min(Math.abs(s0), Math.abs(s1), 0.5 * Math.abs(p)) || 0;
			}

			// Calculate a one-sided slope.
			function slope2(that, t) {
			  var h = that._x1 - that._x0;
			  return h ? (3 * (that._y1 - that._y0) / h - t) / 2 : t;
			}

			// According to https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations
			// "you can express cubic Hermite interpolation in terms of cubic Bézier curves
			// with respect to the four values p0, p0 + m0 / 3, p1 - m1 / 3, p1".
			function point$3(that, t0, t1) {
			  var x0 = that._x0,
				  y0 = that._y0,
				  x1 = that._x1,
				  y1 = that._y1,
				  dx = (x1 - x0) / 3;
			  that._context.bezierCurveTo(x0 + dx, y0 + dx * t0, x1 - dx, y1 - dx * t1, x1, y1);
			}

			function MonotoneX(context) {
			  this._context = context;
			}

			MonotoneX.prototype = {
			  areaStart: function() {
				this._line = 0;
			  },
			  areaEnd: function() {
				this._line = NaN;
			  },
			  lineStart: function() {
				this._x0 = this._x1 =
				this._y0 = this._y1 =
				this._t0 = NaN;
				this._point = 0;
			  },
			  lineEnd: function() {
				switch (this._point) {
				  case 2: this._context.lineTo(this._x1, this._y1); break;
				  case 3: point$3(this, this._t0, slope2(this, this._t0)); break;
				}
				if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
				this._line = 1 - this._line;
			  },
			  point: function(x, y) {
				var t1 = NaN;

				x = +x, y = +y;
				if (x === this._x1 && y === this._y1) return; // Ignore coincident points.
				switch (this._point) {
				  case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
				  case 1: this._point = 2; break;
				  case 2: this._point = 3; point$3(this, slope2(this, t1 = slope3(this, x, y)), t1); break;
				  default: point$3(this, this._t0, t1 = slope3(this, x, y)); break;
				}

				this._x0 = this._x1, this._x1 = x;
				this._y0 = this._y1, this._y1 = y;
				this._t0 = t1;
			  }
			};

			function MonotoneY(context) {
			  this._context = new ReflectContext(context);
			}

			(MonotoneY.prototype = Object.create(MonotoneX.prototype)).point = function(x, y) {
			  MonotoneX.prototype.point.call(this, y, x);
			};

			function ReflectContext(context) {
			  this._context = context;
			}

			ReflectContext.prototype = {
			  moveTo: function(x, y) { this._context.moveTo(y, x); },
			  closePath: function() { this._context.closePath(); },
			  lineTo: function(x, y) { this._context.lineTo(y, x); },
			  bezierCurveTo: function(x1, y1, x2, y2, x, y) { this._context.bezierCurveTo(y1, x1, y2, x2, y, x); }
			};

			function monotoneX(context) {
			  return new MonotoneX(context);
			}

			function monotoneY(context) {
			  return new MonotoneY(context);
			}

			function Natural(context) {
			  this._context = context;
			}

			Natural.prototype = {
			  areaStart: function() {
				this._line = 0;
			  },
			  areaEnd: function() {
				this._line = NaN;
			  },
			  lineStart: function() {
				this._x = [];
				this._y = [];
			  },
			  lineEnd: function() {
				var x = this._x,
					y = this._y,
					n = x.length;

				if (n) {
				  this._line ? this._context.lineTo(x[0], y[0]) : this._context.moveTo(x[0], y[0]);
				  if (n === 2) {
					this._context.lineTo(x[1], y[1]);
				  } else {
					var px = controlPoints(x),
						py = controlPoints(y);
					for (var i0 = 0, i1 = 1; i1 < n; ++i0, ++i1) {
					  this._context.bezierCurveTo(px[0][i0], py[0][i0], px[1][i0], py[1][i0], x[i1], y[i1]);
					}
				  }
				}

				if (this._line || (this._line !== 0 && n === 1)) this._context.closePath();
				this._line = 1 - this._line;
				this._x = this._y = null;
			  },
			  point: function(x, y) {
				this._x.push(+x);
				this._y.push(+y);
			  }
			};

			// See https://www.particleincell.com/2012/bezier-splines/ for derivation.
			function controlPoints(x) {
			  var i,
				  n = x.length - 1,
				  m,
				  a = new Array(n),
				  b = new Array(n),
				  r = new Array(n);
			  a[0] = 0, b[0] = 2, r[0] = x[0] + 2 * x[1];
			  for (i = 1; i < n - 1; ++i) a[i] = 1, b[i] = 4, r[i] = 4 * x[i] + 2 * x[i + 1];
			  a[n - 1] = 2, b[n - 1] = 7, r[n - 1] = 8 * x[n - 1] + x[n];
			  for (i = 1; i < n; ++i) m = a[i] / b[i - 1], b[i] -= m, r[i] -= m * r[i - 1];
			  a[n - 1] = r[n - 1] / b[n - 1];
			  for (i = n - 2; i >= 0; --i) a[i] = (r[i] - a[i + 1]) / b[i];
			  b[n - 1] = (x[n] + a[n - 1]) / 2;
			  for (i = 0; i < n - 1; ++i) b[i] = 2 * x[i + 1] - a[i + 1];
			  return [a, b];
			}

			var natural = function(context) {
			  return new Natural(context);
			};

			function Step(context, t) {
			  this._context = context;
			  this._t = t;
			}

			Step.prototype = {
			  areaStart: function() {
				this._line = 0;
			  },
			  areaEnd: function() {
				this._line = NaN;
			  },
			  lineStart: function() {
				this._x = this._y = NaN;
				this._point = 0;
			  },
			  lineEnd: function() {
				if (0 < this._t && this._t < 1 && this._point === 2) this._context.lineTo(this._x, this._y);
				if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
				if (this._line >= 0) this._t = 1 - this._t, this._line = 1 - this._line;
			  },
			  point: function(x, y) {
				x = +x, y = +y;
				switch (this._point) {
				  case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
				  case 1: this._point = 2; // proceed
				  default: {
					if (this._t <= 0) {
					  this._context.lineTo(this._x, y);
					  this._context.lineTo(x, y);
					} else {
					  var x1 = this._x * (1 - this._t) + x * this._t;
					  this._context.lineTo(x1, this._y);
					  this._context.lineTo(x1, y);
					}
					break;
				  }
				}
				this._x = x, this._y = y;
			  }
			};

			var step = function(context) {
			  return new Step(context, 0.5);
			};

			function stepBefore(context) {
			  return new Step(context, 0);
			}

			function stepAfter(context) {
			  return new Step(context, 1);
			}

			var slice$2 = Array.prototype.slice;

			var none = function(series, order) {
			  if (!((n = series.length) > 1)) return;
			  for (var i = 1, s0, s1 = series[order[0]], n, m = s1.length; i < n; ++i) {
				s0 = s1, s1 = series[order[i]];
				for (var j = 0; j < m; ++j) {
				  s1[j][1] += s1[j][0] = isNaN(s0[j][1]) ? s0[j][0] : s0[j][1];
				}
			  }
			};

			var none$1 = function(series) {
			  var n = series.length, o = new Array(n);
			  while (--n >= 0) o[n] = n;
			  return o;
			};

			function stackValue(d, key) {
			  return d[key];
			}

			var stack = function() {
			  var keys = constant$2([]),
				  order = none$1,
				  offset = none,
				  value = stackValue;

			  function stack(data) {
				var kz = keys.apply(this, arguments),
					i,
					m = data.length,
					n = kz.length,
					sz = new Array(n),
					oz;

				for (i = 0; i < n; ++i) {
				  for (var ki = kz[i], si = sz[i] = new Array(m), j = 0, sij; j < m; ++j) {
					si[j] = sij = [0, +value(data[j], ki, j, data)];
					sij.data = data[j];
				  }
				  si.key = ki;
				}

				for (i = 0, oz = order(sz); i < n; ++i) {
				  sz[oz[i]].index = i;
				}

				offset(sz, oz);
				return sz;
			  }

			  stack.keys = function(_) {
				return arguments.length ? (keys = typeof _ === "function" ? _ : constant$2(slice$2.call(_)), stack) : keys;
			  };

			  stack.value = function(_) {
				return arguments.length ? (value = typeof _ === "function" ? _ : constant$2(+_), stack) : value;
			  };

			  stack.order = function(_) {
				return arguments.length ? (order = _ == null ? none$1 : typeof _ === "function" ? _ : constant$2(slice$2.call(_)), stack) : order;
			  };

			  stack.offset = function(_) {
				return arguments.length ? (offset = _ == null ? none : _, stack) : offset;
			  };

			  return stack;
			};

			var expand = function(series, order) {
			  if (!((n = series.length) > 0)) return;
			  for (var i, n, j = 0, m = series[0].length, y; j < m; ++j) {
				for (y = i = 0; i < n; ++i) y += series[i][j][1] || 0;
				if (y) for (i = 0; i < n; ++i) series[i][j][1] /= y;
			  }
			  none(series, order);
			};

			var silhouette = function(series, order) {
			  if (!((n = series.length) > 0)) return;
			  for (var j = 0, s0 = series[order[0]], n, m = s0.length; j < m; ++j) {
				for (var i = 0, y = 0; i < n; ++i) y += series[i][j][1] || 0;
				s0[j][1] += s0[j][0] = -y / 2;
			  }
			  none(series, order);
			};

			var wiggle = function(series, order) {
			  if (!((n = series.length) > 0) || !((m = (s0 = series[order[0]]).length) > 0)) return;
			  for (var y = 0, j = 1, s0, m, n; j < m; ++j) {
				for (var i = 0, s1 = 0, s2 = 0; i < n; ++i) {
				  var si = series[order[i]],
					  sij0 = si[j][1] || 0,
					  sij1 = si[j - 1][1] || 0,
					  s3 = (sij0 - sij1) / 2;
				  for (var k = 0; k < i; ++k) {
					var sk = series[order[k]],
						skj0 = sk[j][1] || 0,
						skj1 = sk[j - 1][1] || 0;
					s3 += skj0 - skj1;
				  }
				  s1 += sij0, s2 += s3 * sij0;
				}
				s0[j - 1][1] += s0[j - 1][0] = y;
				if (s1) y -= s2 / s1;
			  }
			  s0[j - 1][1] += s0[j - 1][0] = y;
			  none(series, order);
			};

			var ascending$1 = function(series) {
			  var sums = series.map(sum$1);
			  return none$1(series).sort(function(a, b) { return sums[a] - sums[b]; });
			};

			function sum$1(series) {
			  var s = 0, i = -1, n = series.length, v;
			  while (++i < n) if (v = +series[i][1]) s += v;
			  return s;
			}

			var descending$2 = function(series) {
			  return ascending$1(series).reverse();
			};

			var insideOut = function(series) {
			  var n = series.length,
				  i,
				  j,
				  sums = series.map(sum$1),
				  order = none$1(series).sort(function(a, b) { return sums[b] - sums[a]; }),
				  top = 0,
				  bottom = 0,
				  tops = [],
				  bottoms = [];

			  for (i = 0; i < n; ++i) {
				j = order[i];
				if (top < bottom) {
				  top += sums[j];
				  tops.push(j);
				} else {
				  bottom += sums[j];
				  bottoms.push(j);
				}
			  }

			  return bottoms.reverse().concat(tops);
			};

			var reverse = function(series) {
			  return none$1(series).reverse();
			};

			var define = function(constructor, factory, prototype) {
			  constructor.prototype = factory.prototype = prototype;
			  prototype.constructor = constructor;
			};

			function extend(parent, definition) {
			  var prototype = Object.create(parent.prototype);
			  for (var key in definition) prototype[key] = definition[key];
			  return prototype;
			}

			function Color() {}

			var darker = 0.7;
			var brighter = 1 / darker;

			var reHex3 = /^#([0-9a-f]{3})$/;
			var reHex6 = /^#([0-9a-f]{6})$/;
			var reRgbInteger = /^rgb\(\s*([-+]?\d+)\s*,\s*([-+]?\d+)\s*,\s*([-+]?\d+)\s*\)$/;
			var reRgbPercent = /^rgb\(\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*\)$/;
			var reRgbaInteger = /^rgba\(\s*([-+]?\d+)\s*,\s*([-+]?\d+)\s*,\s*([-+]?\d+)\s*,\s*([-+]?\d+(?:\.\d+)?)\s*\)$/;
			var reRgbaPercent = /^rgba\(\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)\s*\)$/;
			var reHslPercent = /^hsl\(\s*([-+]?\d+(?:\.\d+)?)\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*\)$/;
			var reHslaPercent = /^hsla\(\s*([-+]?\d+(?:\.\d+)?)\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)%\s*,\s*([-+]?\d+(?:\.\d+)?)\s*\)$/;

			var named = {
			  aliceblue: 0xf0f8ff,
			  antiquewhite: 0xfaebd7,
			  aqua: 0x00ffff,
			  aquamarine: 0x7fffd4,
			  azure: 0xf0ffff,
			  beige: 0xf5f5dc,
			  bisque: 0xffe4c4,
			  black: 0x000000,
			  blanchedalmond: 0xffebcd,
			  blue: 0x0000ff,
			  blueviolet: 0x8a2be2,
			  brown: 0xa52a2a,
			  burlywood: 0xdeb887,
			  cadetblue: 0x5f9ea0,
			  chartreuse: 0x7fff00,
			  chocolate: 0xd2691e,
			  coral: 0xff7f50,
			  cornflowerblue: 0x6495ed,
			  cornsilk: 0xfff8dc,
			  crimson: 0xdc143c,
			  cyan: 0x00ffff,
			  darkblue: 0x00008b,
			  darkcyan: 0x008b8b,
			  darkgoldenrod: 0xb8860b,
			  darkgray: 0xa9a9a9,
			  darkgreen: 0x006400,
			  darkgrey: 0xa9a9a9,
			  darkkhaki: 0xbdb76b,
			  darkmagenta: 0x8b008b,
			  darkolivegreen: 0x556b2f,
			  darkorange: 0xff8c00,
			  darkorchid: 0x9932cc,
			  darkred: 0x8b0000,
			  darksalmon: 0xe9967a,
			  darkseagreen: 0x8fbc8f,
			  darkslateblue: 0x483d8b,
			  darkslategray: 0x2f4f4f,
			  darkslategrey: 0x2f4f4f,
			  darkturquoise: 0x00ced1,
			  darkviolet: 0x9400d3,
			  deeppink: 0xff1493,
			  deepskyblue: 0x00bfff,
			  dimgray: 0x696969,
			  dimgrey: 0x696969,
			  dodgerblue: 0x1e90ff,
			  firebrick: 0xb22222,
			  floralwhite: 0xfffaf0,
			  forestgreen: 0x228b22,
			  fuchsia: 0xff00ff,
			  gainsboro: 0xdcdcdc,
			  ghostwhite: 0xf8f8ff,
			  gold: 0xffd700,
			  goldenrod: 0xdaa520,
			  gray: 0x808080,
			  green: 0x008000,
			  greenyellow: 0xadff2f,
			  grey: 0x808080,
			  honeydew: 0xf0fff0,
			  hotpink: 0xff69b4,
			  indianred: 0xcd5c5c,
			  indigo: 0x4b0082,
			  ivory: 0xfffff0,
			  khaki: 0xf0e68c,
			  lavender: 0xe6e6fa,
			  lavenderblush: 0xfff0f5,
			  lawngreen: 0x7cfc00,
			  lemonchiffon: 0xfffacd,
			  lightblue: 0xadd8e6,
			  lightcoral: 0xf08080,
			  lightcyan: 0xe0ffff,
			  lightgoldenrodyellow: 0xfafad2,
			  lightgray: 0xd3d3d3,
			  lightgreen: 0x90ee90,
			  lightgrey: 0xd3d3d3,
			  lightpink: 0xffb6c1,
			  lightsalmon: 0xffa07a,
			  lightseagreen: 0x20b2aa,
			  lightskyblue: 0x87cefa,
			  lightslategray: 0x778899,
			  lightslategrey: 0x778899,
			  lightsteelblue: 0xb0c4de,
			  lightyellow: 0xffffe0,
			  lime: 0x00ff00,
			  limegreen: 0x32cd32,
			  linen: 0xfaf0e6,
			  magenta: 0xff00ff,
			  maroon: 0x800000,
			  mediumaquamarine: 0x66cdaa,
			  mediumblue: 0x0000cd,
			  mediumorchid: 0xba55d3,
			  mediumpurple: 0x9370db,
			  mediumseagreen: 0x3cb371,
			  mediumslateblue: 0x7b68ee,
			  mediumspringgreen: 0x00fa9a,
			  mediumturquoise: 0x48d1cc,
			  mediumvioletred: 0xc71585,
			  midnightblue: 0x191970,
			  mintcream: 0xf5fffa,
			  mistyrose: 0xffe4e1,
			  moccasin: 0xffe4b5,
			  navajowhite: 0xffdead,
			  navy: 0x000080,
			  oldlace: 0xfdf5e6,
			  olive: 0x808000,
			  olivedrab: 0x6b8e23,
			  orange: 0xffa500,
			  orangered: 0xff4500,
			  orchid: 0xda70d6,
			  palegoldenrod: 0xeee8aa,
			  palegreen: 0x98fb98,
			  paleturquoise: 0xafeeee,
			  palevioletred: 0xdb7093,
			  papayawhip: 0xffefd5,
			  peachpuff: 0xffdab9,
			  peru: 0xcd853f,
			  pink: 0xffc0cb,
			  plum: 0xdda0dd,
			  powderblue: 0xb0e0e6,
			  purple: 0x800080,
			  rebeccapurple: 0x663399,
			  red: 0xff0000,
			  rosybrown: 0xbc8f8f,
			  royalblue: 0x4169e1,
			  saddlebrown: 0x8b4513,
			  salmon: 0xfa8072,
			  sandybrown: 0xf4a460,
			  seagreen: 0x2e8b57,
			  seashell: 0xfff5ee,
			  sienna: 0xa0522d,
			  silver: 0xc0c0c0,
			  skyblue: 0x87ceeb,
			  slateblue: 0x6a5acd,
			  slategray: 0x708090,
			  slategrey: 0x708090,
			  snow: 0xfffafa,
			  springgreen: 0x00ff7f,
			  steelblue: 0x4682b4,
			  tan: 0xd2b48c,
			  teal: 0x008080,
			  thistle: 0xd8bfd8,
			  tomato: 0xff6347,
			  turquoise: 0x40e0d0,
			  violet: 0xee82ee,
			  wheat: 0xf5deb3,
			  white: 0xffffff,
			  whitesmoke: 0xf5f5f5,
			  yellow: 0xffff00,
			  yellowgreen: 0x9acd32
			};

			define(Color, color, {
			  displayable: function() {
				return this.rgb().displayable();
			  },
			  toString: function() {
				return this.rgb() + "";
			  }
			});

			function color(format) {
			  var m;
			  format = (format + "").trim().toLowerCase();
			  return (m = reHex3.exec(format)) ? (m = parseInt(m[1], 16), new Rgb((m >> 8 & 0xf) | (m >> 4 & 0x0f0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1)) // #f00
				  : (m = reHex6.exec(format)) ? rgbn(parseInt(m[1], 16)) // #ff0000
				  : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)
				  : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)
				  : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)
				  : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)
				  : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)
				  : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)
				  : named.hasOwnProperty(format) ? rgbn(named[format])
				  : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0)
				  : null;
			}

			function rgbn(n) {
			  return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);
			}

			function rgba(r, g, b, a) {
			  if (a <= 0) r = g = b = NaN;
			  return new Rgb(r, g, b, a);
			}

			function rgbConvert(o) {
			  if (!(o instanceof Color)) o = color(o);
			  if (!o) return new Rgb;
			  o = o.rgb();
			  return new Rgb(o.r, o.g, o.b, o.opacity);
			}

			function rgb(r, g, b, opacity) {
			  return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);
			}

			function Rgb(r, g, b, opacity) {
			  this.r = +r;
			  this.g = +g;
			  this.b = +b;
			  this.opacity = +opacity;
			}

			define(Rgb, rgb, extend(Color, {
			  brighter: function(k) {
				k = k == null ? brighter : Math.pow(brighter, k);
				return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
			  },
			  darker: function(k) {
				k = k == null ? darker : Math.pow(darker, k);
				return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
			  },
			  rgb: function() {
				return this;
			  },
			  displayable: function() {
				return (0 <= this.r && this.r <= 255)
					&& (0 <= this.g && this.g <= 255)
					&& (0 <= this.b && this.b <= 255)
					&& (0 <= this.opacity && this.opacity <= 1);
			  },
			  toString: function() {
				var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
				return (a === 1 ? "rgb(" : "rgba(")
					+ Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", "
					+ Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", "
					+ Math.max(0, Math.min(255, Math.round(this.b) || 0))
					+ (a === 1 ? ")" : ", " + a + ")");
			  }
			}));

			function hsla(h, s, l, a) {
			  if (a <= 0) h = s = l = NaN;
			  else if (l <= 0 || l >= 1) h = s = NaN;
			  else if (s <= 0) h = NaN;
			  return new Hsl(h, s, l, a);
			}

			function hslConvert(o) {
			  if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);
			  if (!(o instanceof Color)) o = color(o);
			  if (!o) return new Hsl;
			  if (o instanceof Hsl) return o;
			  o = o.rgb();
			  var r = o.r / 255,
				  g = o.g / 255,
				  b = o.b / 255,
				  min = Math.min(r, g, b),
				  max = Math.max(r, g, b),
				  h = NaN,
				  s = max - min,
				  l = (max + min) / 2;
			  if (s) {
				if (r === max) h = (g - b) / s + (g < b) * 6;
				else if (g === max) h = (b - r) / s + 2;
				else h = (r - g) / s + 4;
				s /= l < 0.5 ? max + min : 2 - max - min;
				h *= 60;
			  } else {
				s = l > 0 && l < 1 ? 0 : h;
			  }
			  return new Hsl(h, s, l, o.opacity);
			}

			function hsl(h, s, l, opacity) {
			  return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);
			}

			function Hsl(h, s, l, opacity) {
			  this.h = +h;
			  this.s = +s;
			  this.l = +l;
			  this.opacity = +opacity;
			}

			define(Hsl, hsl, extend(Color, {
			  brighter: function(k) {
				k = k == null ? brighter : Math.pow(brighter, k);
				return new Hsl(this.h, this.s, this.l * k, this.opacity);
			  },
			  darker: function(k) {
				k = k == null ? darker : Math.pow(darker, k);
				return new Hsl(this.h, this.s, this.l * k, this.opacity);
			  },
			  rgb: function() {
				var h = this.h % 360 + (this.h < 0) * 360,
					s = isNaN(h) || isNaN(this.s) ? 0 : this.s,
					l = this.l,
					m2 = l + (l < 0.5 ? l : 1 - l) * s,
					m1 = 2 * l - m2;
				return new Rgb(
				  hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),
				  hsl2rgb(h, m1, m2),
				  hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),
				  this.opacity
				);
			  },
			  displayable: function() {
				return (0 <= this.s && this.s <= 1 || isNaN(this.s))
					&& (0 <= this.l && this.l <= 1)
					&& (0 <= this.opacity && this.opacity <= 1);
			  }
			}));

			/* From FvD 13.37, CSS Color Module Level 3 */
			function hsl2rgb(h, m1, m2) {
			  return (h < 60 ? m1 + (m2 - m1) * h / 60
				  : h < 180 ? m2
				  : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60
				  : m1) * 255;
			}

			var deg2rad = Math.PI / 180;
			var rad2deg = 180 / Math.PI;

			var Kn = 18;
			var Xn = 0.950470;
			var Yn = 1;
			var Zn = 1.088830;
			var t0 = 4 / 29;
			var t1 = 6 / 29;
			var t2 = 3 * t1 * t1;
			var t3 = t1 * t1 * t1;

			function labConvert(o) {
			  if (o instanceof Lab) return new Lab(o.l, o.a, o.b, o.opacity);
			  if (o instanceof Hcl) {
				var h = o.h * deg2rad;
				return new Lab(o.l, Math.cos(h) * o.c, Math.sin(h) * o.c, o.opacity);
			  }
			  if (!(o instanceof Rgb)) o = rgbConvert(o);
			  var b = rgb2xyz(o.r),
				  a = rgb2xyz(o.g),
				  l = rgb2xyz(o.b),
				  x = xyz2lab((0.4124564 * b + 0.3575761 * a + 0.1804375 * l) / Xn),
				  y = xyz2lab((0.2126729 * b + 0.7151522 * a + 0.0721750 * l) / Yn),
				  z = xyz2lab((0.0193339 * b + 0.1191920 * a + 0.9503041 * l) / Zn);
			  return new Lab(116 * y - 16, 500 * (x - y), 200 * (y - z), o.opacity);
			}

			function lab(l, a, b, opacity) {
			  return arguments.length === 1 ? labConvert(l) : new Lab(l, a, b, opacity == null ? 1 : opacity);
			}

			function Lab(l, a, b, opacity) {
			  this.l = +l;
			  this.a = +a;
			  this.b = +b;
			  this.opacity = +opacity;
			}

			define(Lab, lab, extend(Color, {
			  brighter: function(k) {
				return new Lab(this.l + Kn * (k == null ? 1 : k), this.a, this.b, this.opacity);
			  },
			  darker: function(k) {
				return new Lab(this.l - Kn * (k == null ? 1 : k), this.a, this.b, this.opacity);
			  },
			  rgb: function() {
				var y = (this.l + 16) / 116,
					x = isNaN(this.a) ? y : y + this.a / 500,
					z = isNaN(this.b) ? y : y - this.b / 200;
				y = Yn * lab2xyz(y);
				x = Xn * lab2xyz(x);
				z = Zn * lab2xyz(z);
				return new Rgb(
				  xyz2rgb( 3.2404542 * x - 1.5371385 * y - 0.4985314 * z), // D65 -> sRGB
				  xyz2rgb(-0.9692660 * x + 1.8760108 * y + 0.0415560 * z),
				  xyz2rgb( 0.0556434 * x - 0.2040259 * y + 1.0572252 * z),
				  this.opacity
				);
			  }
			}));

			function xyz2lab(t) {
			  return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0;
			}

			function lab2xyz(t) {
			  return t > t1 ? t * t * t : t2 * (t - t0);
			}

			function xyz2rgb(x) {
			  return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055);
			}

			function rgb2xyz(x) {
			  return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);
			}

			function hclConvert(o) {
			  if (o instanceof Hcl) return new Hcl(o.h, o.c, o.l, o.opacity);
			  if (!(o instanceof Lab)) o = labConvert(o);
			  var h = Math.atan2(o.b, o.a) * rad2deg;
			  return new Hcl(h < 0 ? h + 360 : h, Math.sqrt(o.a * o.a + o.b * o.b), o.l, o.opacity);
			}

			function hcl(h, c, l, opacity) {
			  return arguments.length === 1 ? hclConvert(h) : new Hcl(h, c, l, opacity == null ? 1 : opacity);
			}

			function Hcl(h, c, l, opacity) {
			  this.h = +h;
			  this.c = +c;
			  this.l = +l;
			  this.opacity = +opacity;
			}

			define(Hcl, hcl, extend(Color, {
			  brighter: function(k) {
				return new Hcl(this.h, this.c, this.l + Kn * (k == null ? 1 : k), this.opacity);
			  },
			  darker: function(k) {
				return new Hcl(this.h, this.c, this.l - Kn * (k == null ? 1 : k), this.opacity);
			  },
			  rgb: function() {
				return labConvert(this).rgb();
			  }
			}));

			var A = -0.14861;
			var B = +1.78277;
			var C = -0.29227;
			var D = -0.90649;
			var E = +1.97294;
			var ED = E * D;
			var EB = E * B;
			var BC_DA = B * C - D * A;

			function cubehelixConvert(o) {
			  if (o instanceof Cubehelix) return new Cubehelix(o.h, o.s, o.l, o.opacity);
			  if (!(o instanceof Rgb)) o = rgbConvert(o);
			  var r = o.r / 255,
				  g = o.g / 255,
				  b = o.b / 255,
				  l = (BC_DA * b + ED * r - EB * g) / (BC_DA + ED - EB),
				  bl = b - l,
				  k = (E * (g - l) - C * bl) / D,
				  s = Math.sqrt(k * k + bl * bl) / (E * l * (1 - l)), // NaN if l=0 or l=1
				  h = s ? Math.atan2(k, bl) * rad2deg - 120 : NaN;
			  return new Cubehelix(h < 0 ? h + 360 : h, s, l, o.opacity);
			}

			function cubehelix(h, s, l, opacity) {
			  return arguments.length === 1 ? cubehelixConvert(h) : new Cubehelix(h, s, l, opacity == null ? 1 : opacity);
			}

			function Cubehelix(h, s, l, opacity) {
			  this.h = +h;
			  this.s = +s;
			  this.l = +l;
			  this.opacity = +opacity;
			}

			define(Cubehelix, cubehelix, extend(Color, {
			  brighter: function(k) {
				k = k == null ? brighter : Math.pow(brighter, k);
				return new Cubehelix(this.h, this.s, this.l * k, this.opacity);
			  },
			  darker: function(k) {
				k = k == null ? darker : Math.pow(darker, k);
				return new Cubehelix(this.h, this.s, this.l * k, this.opacity);
			  },
			  rgb: function() {
				var h = isNaN(this.h) ? 0 : (this.h + 120) * deg2rad,
					l = +this.l,
					a = isNaN(this.s) ? 0 : this.s * l * (1 - l),
					cosh = Math.cos(h),
					sinh = Math.sin(h);
				return new Rgb(
				  255 * (l + a * (A * cosh + B * sinh)),
				  255 * (l + a * (C * cosh + D * sinh)),
				  255 * (l + a * (E * cosh)),
				  this.opacity
				);
			  }
			}));

			function basis$1(t1, v0, v1, v2, v3) {
			  var t2 = t1 * t1, t3 = t2 * t1;
			  return ((1 - 3 * t1 + 3 * t2 - t3) * v0
				  + (4 - 6 * t2 + 3 * t3) * v1
				  + (1 + 3 * t1 + 3 * t2 - 3 * t3) * v2
				  + t3 * v3) / 6;
			}

			var basis$2 = function(values) {
			  var n = values.length - 1;
			  return function(t) {
				var i = t <= 0 ? (t = 0) : t >= 1 ? (t = 1, n - 1) : Math.floor(t * n),
					v1 = values[i],
					v2 = values[i + 1],
					v0 = i > 0 ? values[i - 1] : 2 * v1 - v2,
					v3 = i < n - 1 ? values[i + 2] : 2 * v2 - v1;
				return basis$1((t - i / n) * n, v0, v1, v2, v3);
			  };
			};

			var basisClosed$1 = function(values) {
			  var n = values.length;
			  return function(t) {
				var i = Math.floor(((t %= 1) < 0 ? ++t : t) * n),
					v0 = values[(i + n - 1) % n],
					v1 = values[i % n],
					v2 = values[(i + 1) % n],
					v3 = values[(i + 2) % n];
				return basis$1((t - i / n) * n, v0, v1, v2, v3);
			  };
			};

			var constant$3 = function(x) {
			  return function() {
				return x;
			  };
			};

			function linear$1(a, d) {
			  return function(t) {
				return a + t * d;
			  };
			}

			function exponential$1(a, b, y) {
			  return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) {
				return Math.pow(a + t * b, y);
			  };
			}

			function hue(a, b) {
			  var d = b - a;
			  return d ? linear$1(a, d > 180 || d < -180 ? d - 360 * Math.round(d / 360) : d) : constant$3(isNaN(a) ? b : a);
			}

			function gamma(y) {
			  return (y = +y) === 1 ? nogamma : function(a, b) {
				return b - a ? exponential$1(a, b, y) : constant$3(isNaN(a) ? b : a);
			  };
			}

			function nogamma(a, b) {
			  var d = b - a;
			  return d ? linear$1(a, d) : constant$3(isNaN(a) ? b : a);
			}

			var interpolateRgb = (function rgbGamma(y) {
			  var color$$1 = gamma(y);

			  function rgb$$1(start, end) {
				var r = color$$1((start = rgb(start)).r, (end = rgb(end)).r),
					g = color$$1(start.g, end.g),
					b = color$$1(start.b, end.b),
					opacity = color$$1(start.opacity, end.opacity);
				return function(t) {
				  start.r = r(t);
				  start.g = g(t);
				  start.b = b(t);
				  start.opacity = opacity(t);
				  return start + "";
				};
			  }

			  rgb$$1.gamma = rgbGamma;

			  return rgb$$1;
			})(1);

			function rgbSpline(spline) {
			  return function(colors) {
				var n = colors.length,
					r = new Array(n),
					g = new Array(n),
					b = new Array(n),
					i, color$$1;
				for (i = 0; i < n; ++i) {
				  color$$1 = rgb(colors[i]);
				  r[i] = color$$1.r || 0;
				  g[i] = color$$1.g || 0;
				  b[i] = color$$1.b || 0;
				}
				r = spline(r);
				g = spline(g);
				b = spline(b);
				color$$1.opacity = 1;
				return function(t) {
				  color$$1.r = r(t);
				  color$$1.g = g(t);
				  color$$1.b = b(t);
				  return color$$1 + "";
				};
			  };
			}

			var rgbBasis = rgbSpline(basis$2);
			var rgbBasisClosed = rgbSpline(basisClosed$1);

			var array$1 = function(a, b) {
			  var nb = b ? b.length : 0,
				  na = a ? Math.min(nb, a.length) : 0,
				  x = new Array(nb),
				  c = new Array(nb),
				  i;

			  for (i = 0; i < na; ++i) x[i] = interpolate(a[i], b[i]);
			  for (; i < nb; ++i) c[i] = b[i];

			  return function(t) {
				for (i = 0; i < na; ++i) c[i] = x[i](t);
				return c;
			  };
			};

			var date = function(a, b) {
			  var d = new Date;
			  return a = +a, b -= a, function(t) {
				return d.setTime(a + b * t), d;
			  };
			};

			var interpolateNumber = function(a, b) {
			  return a = +a, b -= a, function(t) {
				return a + b * t;
			  };
			};

			var object = function(a, b) {
			  var i = {},
				  c = {},
				  k;

			  if (a === null || typeof a !== "object") a = {};
			  if (b === null || typeof b !== "object") b = {};

			  for (k in b) {
				if (k in a) {
				  i[k] = interpolate(a[k], b[k]);
				} else {
				  c[k] = b[k];
				}
			  }

			  return function(t) {
				for (k in i) c[k] = i[k](t);
				return c;
			  };
			};

			var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g;
			var reB = new RegExp(reA.source, "g");

			function zero(b) {
			  return function() {
				return b;
			  };
			}

			function one(b) {
			  return function(t) {
				return b(t) + "";
			  };
			}

			var interpolateString = function(a, b) {
			  var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b
				  am, // current match in a
				  bm, // current match in b
				  bs, // string preceding current number in b, if any
				  i = -1, // index in s
				  s = [], // string constants and placeholders
				  q = []; // number interpolators

			  // Coerce inputs to strings.
			  a = a + "", b = b + "";

			  // Interpolate pairs of numbers in a & b.
			  while ((am = reA.exec(a))
				  && (bm = reB.exec(b))) {
				if ((bs = bm.index) > bi) { // a string precedes the next number in b
				  bs = b.slice(bi, bs);
				  if (s[i]) s[i] += bs; // coalesce with previous string
				  else s[++i] = bs;
				}
				if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match
				  if (s[i]) s[i] += bm; // coalesce with previous string
				  else s[++i] = bm;
				} else { // interpolate non-matching numbers
				  s[++i] = null;
				  q.push({i: i, x: interpolateNumber(am, bm)});
				}
				bi = reB.lastIndex;
			  }

			  // Add remains of b.
			  if (bi < b.length) {
				bs = b.slice(bi);
				if (s[i]) s[i] += bs; // coalesce with previous string
				else s[++i] = bs;
			  }

			  // Special optimization for only a single match.
			  // Otherwise, interpolate each of the numbers and rejoin the string.
			  return s.length < 2 ? (q[0]
				  ? one(q[0].x)
				  : zero(b))
				  : (b = q.length, function(t) {
					  for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);
					  return s.join("");
					});
			};

			var interpolate = function(a, b) {
			  var t = typeof b, c;
			  return b == null || t === "boolean" ? constant$3(b)
				  : (t === "number" ? interpolateNumber
				  : t === "string" ? ((c = color(b)) ? (b = c, interpolateRgb) : interpolateString)
				  : b instanceof color ? interpolateRgb
				  : b instanceof Date ? date
				  : Array.isArray(b) ? array$1
				  : isNaN(b) ? object
				  : interpolateNumber)(a, b);
			};

			var interpolateRound = function(a, b) {
			  return a = +a, b -= a, function(t) {
				return Math.round(a + b * t);
			  };
			};

			var degrees = 180 / Math.PI;

			var identity$2 = {
			  translateX: 0,
			  translateY: 0,
			  rotate: 0,
			  skewX: 0,
			  scaleX: 1,
			  scaleY: 1
			};

			var decompose = function(a, b, c, d, e, f) {
			  var scaleX, scaleY, skewX;
			  if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;
			  if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;
			  if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;
			  if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;
			  return {
				translateX: e,
				translateY: f,
				rotate: Math.atan2(b, a) * degrees,
				skewX: Math.atan(skewX) * degrees,
				scaleX: scaleX,
				scaleY: scaleY
			  };
			};

			var cssNode;
			var cssRoot;
			var cssView;
			var svgNode;

			function parseCss(value) {
			  if (value === "none") return identity$2;
			  if (!cssNode) cssNode = document.createElement("DIV"), cssRoot = document.documentElement, cssView = document.defaultView;
			  cssNode.style.transform = value;
			  value = cssView.getComputedStyle(cssRoot.appendChild(cssNode), null).getPropertyValue("transform");
			  cssRoot.removeChild(cssNode);
			  value = value.slice(7, -1).split(",");
			  return decompose(+value[0], +value[1], +value[2], +value[3], +value[4], +value[5]);
			}

			function parseSvg(value) {
			  if (value == null) return identity$2;
			  if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g");
			  svgNode.setAttribute("transform", value);
			  if (!(value = svgNode.transform.baseVal.consolidate())) return identity$2;
			  value = value.matrix;
			  return decompose(value.a, value.b, value.c, value.d, value.e, value.f);
			}

			function interpolateTransform(parse, pxComma, pxParen, degParen) {

			  function pop(s) {
				return s.length ? s.pop() + " " : "";
			  }

			  function translate(xa, ya, xb, yb, s, q) {
				if (xa !== xb || ya !== yb) {
				  var i = s.push("translate(", null, pxComma, null, pxParen);
				  q.push({i: i - 4, x: interpolateNumber(xa, xb)}, {i: i - 2, x: interpolateNumber(ya, yb)});
				} else if (xb || yb) {
				  s.push("translate(" + xb + pxComma + yb + pxParen);
				}
			  }

			  function rotate(a, b, s, q) {
				if (a !== b) {
				  if (a - b > 180) b += 360; else if (b - a > 180) a += 360; // shortest path
				  q.push({i: s.push(pop(s) + "rotate(", null, degParen) - 2, x: interpolateNumber(a, b)});
				} else if (b) {
				  s.push(pop(s) + "rotate(" + b + degParen);
				}
			  }

			  function skewX(a, b, s, q) {
				if (a !== b) {
				  q.push({i: s.push(pop(s) + "skewX(", null, degParen) - 2, x: interpolateNumber(a, b)});
				} else if (b) {
				  s.push(pop(s) + "skewX(" + b + degParen);
				}
			  }

			  function scale(xa, ya, xb, yb, s, q) {
				if (xa !== xb || ya !== yb) {
				  var i = s.push(pop(s) + "scale(", null, ",", null, ")");
				  q.push({i: i - 4, x: interpolateNumber(xa, xb)}, {i: i - 2, x: interpolateNumber(ya, yb)});
				} else if (xb !== 1 || yb !== 1) {
				  s.push(pop(s) + "scale(" + xb + "," + yb + ")");
				}
			  }

			  return function(a, b) {
				var s = [], // string constants and placeholders
					q = []; // number interpolators
				a = parse(a), b = parse(b);
				translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q);
				rotate(a.rotate, b.rotate, s, q);
				skewX(a.skewX, b.skewX, s, q);
				scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q);
				a = b = null; // gc
				return function(t) {
				  var i = -1, n = q.length, o;
				  while (++i < n) s[(o = q[i]).i] = o.x(t);
				  return s.join("");
				};
			  };
			}

			var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)");
			var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")");

			var rho = Math.SQRT2;
			var rho2 = 2;
			var rho4 = 4;
			var epsilon2 = 1e-12;

			function cosh(x) {
			  return ((x = Math.exp(x)) + 1 / x) / 2;
			}

			function sinh(x) {
			  return ((x = Math.exp(x)) - 1 / x) / 2;
			}

			function tanh(x) {
			  return ((x = Math.exp(2 * x)) - 1) / (x + 1);
			}

			// p0 = [ux0, uy0, w0]
			// p1 = [ux1, uy1, w1]
			var interpolateZoom = function(p0, p1) {
			  var ux0 = p0[0], uy0 = p0[1], w0 = p0[2],
				  ux1 = p1[0], uy1 = p1[1], w1 = p1[2],
				  dx = ux1 - ux0,
				  dy = uy1 - uy0,
				  d2 = dx * dx + dy * dy,
				  i,
				  S;

			  // Special case for u0 ≅ u1.
			  if (d2 < epsilon2) {
				S = Math.log(w1 / w0) / rho;
				i = function(t) {
				  return [
					ux0 + t * dx,
					uy0 + t * dy,
					w0 * Math.exp(rho * t * S)
				  ];
				};
			  }

			  // General case.
			  else {
				var d1 = Math.sqrt(d2),
					b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1),
					b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1),
					r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),
					r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);
				S = (r1 - r0) / rho;
				i = function(t) {
				  var s = t * S,
					  coshr0 = cosh(r0),
					  u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));
				  return [
					ux0 + u * dx,
					uy0 + u * dy,
					w0 * coshr0 / cosh(rho * s + r0)
				  ];
				};
			  }

			  i.duration = S * 1000;

			  return i;
			};

			function hsl$1(hue$$1) {
			  return function(start, end) {
				var h = hue$$1((start = hsl(start)).h, (end = hsl(end)).h),
					s = nogamma(start.s, end.s),
					l = nogamma(start.l, end.l),
					opacity = nogamma(start.opacity, end.opacity);
				return function(t) {
				  start.h = h(t);
				  start.s = s(t);
				  start.l = l(t);
				  start.opacity = opacity(t);
				  return start + "";
				};
			  }
			}

			var hsl$2 = hsl$1(hue);
			var hslLong = hsl$1(nogamma);

			function lab$1(start, end) {
			  var l = nogamma((start = lab(start)).l, (end = lab(end)).l),
				  a = nogamma(start.a, end.a),
				  b = nogamma(start.b, end.b),
				  opacity = nogamma(start.opacity, end.opacity);
			  return function(t) {
				start.l = l(t);
				start.a = a(t);
				start.b = b(t);
				start.opacity = opacity(t);
				return start + "";
			  };
			}

			function hcl$1(hue$$1) {
			  return function(start, end) {
				var h = hue$$1((start = hcl(start)).h, (end = hcl(end)).h),
					c = nogamma(start.c, end.c),
					l = nogamma(start.l, end.l),
					opacity = nogamma(start.opacity, end.opacity);
				return function(t) {
				  start.h = h(t);
				  start.c = c(t);
				  start.l = l(t);
				  start.opacity = opacity(t);
				  return start + "";
				};
			  }
			}

			var hcl$2 = hcl$1(hue);
			var hclLong = hcl$1(nogamma);

			function cubehelix$1(hue$$1) {
			  return (function cubehelixGamma(y) {
				y = +y;

				function cubehelix$$1(start, end) {
				  var h = hue$$1((start = cubehelix(start)).h, (end = cubehelix(end)).h),
					  s = nogamma(start.s, end.s),
					  l = nogamma(start.l, end.l),
					  opacity = nogamma(start.opacity, end.opacity);
				  return function(t) {
					start.h = h(t);
					start.s = s(t);
					start.l = l(Math.pow(t, y));
					start.opacity = opacity(t);
					return start + "";
				  };
				}

				cubehelix$$1.gamma = cubehelixGamma;

				return cubehelix$$1;
			  })(1);
			}

			var cubehelix$2 = cubehelix$1(hue);
			var cubehelixLong = cubehelix$1(nogamma);

			var quantize = function(interpolator, n) {
			  var samples = new Array(n);
			  for (var i = 0; i < n; ++i) samples[i] = interpolator(i / (n - 1));
			  return samples;
			};

			var noop$1 = {value: function() {}};

			function dispatch() {
			  for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {
				if (!(t = arguments[i] + "") || (t in _)) throw new Error("illegal type: " + t);
				_[t] = [];
			  }
			  return new Dispatch(_);
			}

			function Dispatch(_) {
			  this._ = _;
			}

			function parseTypenames(typenames, types) {
			  return typenames.trim().split(/^|\s+/).map(function(t) {
				var name = "", i = t.indexOf(".");
				if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
				if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t);
				return {type: t, name: name};
			  });
			}

			Dispatch.prototype = dispatch.prototype = {
			  constructor: Dispatch,
			  on: function(typename, callback) {
				var _ = this._,
					T = parseTypenames(typename + "", _),
					t,
					i = -1,
					n = T.length;

				// If no callback was specified, return the callback of the given type and name.
				if (arguments.length < 2) {
				  while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t;
				  return;
				}

				// If a type was specified, set the callback for the given type and name.
				// Otherwise, if a null callback was specified, remove callbacks of the given name.
				if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback);
				while (++i < n) {
				  if (t = (typename = T[i]).type) _[t] = set$2(_[t], typename.name, callback);
				  else if (callback == null) for (t in _) _[t] = set$2(_[t], typename.name, null);
				}

				return this;
			  },
			  copy: function() {
				var copy = {}, _ = this._;
				for (var t in _) copy[t] = _[t].slice();
				return new Dispatch(copy);
			  },
			  call: function(type, that) {
				if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2];
				if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
				for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);
			  },
			  apply: function(type, that, args) {
				if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
				for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);
			  }
			};

			function get(type, name) {
			  for (var i = 0, n = type.length, c; i < n; ++i) {
				if ((c = type[i]).name === name) {
				  return c.value;
				}
			  }
			}

			function set$2(type, name, callback) {
			  for (var i = 0, n = type.length; i < n; ++i) {
				if (type[i].name === name) {
				  type[i] = noop$1, type = type.slice(0, i).concat(type.slice(i + 1));
				  break;
				}
			  }
			  if (callback != null) type.push({name: name, value: callback});
			  return type;
			}

			function objectConverter(columns) {
			  return new Function("d", "return {" + columns.map(function(name, i) {
				return JSON.stringify(name) + ": d[" + i + "]";
			  }).join(",") + "}");
			}

			function customConverter(columns, f) {
			  var object = objectConverter(columns);
			  return function(row, i) {
				return f(object(row), i, columns);
			  };
			}

			// Compute unique columns in order of discovery.
			function inferColumns(rows) {
			  var columnSet = Object.create(null),
				  columns = [];

			  rows.forEach(function(row) {
				for (var column in row) {
				  if (!(column in columnSet)) {
					columns.push(columnSet[column] = column);
				  }
				}
			  });

			  return columns;
			}

			var dsv = function(delimiter) {
			  var reFormat = new RegExp("[\"" + delimiter + "\n]"),
				  delimiterCode = delimiter.charCodeAt(0);

			  function parse(text, f) {
				var convert, columns, rows = parseRows(text, function(row, i) {
				  if (convert) return convert(row, i - 1);
				  columns = row, convert = f ? customConverter(row, f) : objectConverter(row);
				});
				rows.columns = columns;
				return rows;
			  }

			  function parseRows(text, f) {
				var EOL = {}, // sentinel value for end-of-line
					EOF = {}, // sentinel value for end-of-file
					rows = [], // output rows
					N = text.length,
					I = 0, // current character index
					n = 0, // the current line number
					t, // the current token
					eol; // is the current token followed by EOL?

				function token() {
				  if (I >= N) return EOF; // special case: end of file
				  if (eol) return eol = false, EOL; // special case: end of line

				  // special case: quotes
				  var j = I, c;
				  if (text.charCodeAt(j) === 34) {
					var i = j;
					while (i++ < N) {
					  if (text.charCodeAt(i) === 34) {
						if (text.charCodeAt(i + 1) !== 34) break;
						++i;
					  }
					}
					I = i + 2;
					c = text.charCodeAt(i + 1);
					if (c === 13) {
					  eol = true;
					  if (text.charCodeAt(i + 2) === 10) ++I;
					} else if (c === 10) {
					  eol = true;
					}
					return text.slice(j + 1, i).replace(/""/g, "\"");
				  }

				  // common case: find next delimiter or newline
				  while (I < N) {
					var k = 1;
					c = text.charCodeAt(I++);
					if (c === 10) eol = true; // \n
					else if (c === 13) { eol = true; if (text.charCodeAt(I) === 10) ++I, ++k; } // \r|\r\n
					else if (c !== delimiterCode) continue;
					return text.slice(j, I - k);
				  }

				  // special case: last token before EOF
				  return text.slice(j);
				}

				while ((t = token()) !== EOF) {
				  var a = [];
				  while (t !== EOL && t !== EOF) {
					a.push(t);
					t = token();
				  }
				  if (f && (a = f(a, n++)) == null) continue;
				  rows.push(a);
				}

				return rows;
			  }

			  function format(rows, columns) {
				if (columns == null) columns = inferColumns(rows);
				return [columns.map(formatValue).join(delimiter)].concat(rows.map(function(row) {
				  return columns.map(function(column) {
					return formatValue(row[column]);
				  }).join(delimiter);
				})).join("\n");
			  }

			  function formatRows(rows) {
				return rows.map(formatRow).join("\n");
			  }

			  function formatRow(row) {
				return row.map(formatValue).join(delimiter);
			  }

			  function formatValue(text) {
				return text == null ? ""
					: reFormat.test(text += "") ? "\"" + text.replace(/\"/g, "\"\"") + "\""
					: text;
			  }

			  return {
				parse: parse,
				parseRows: parseRows,
				format: format,
				formatRows: formatRows
			  };
			};

			var csv = dsv(",");

			var csvParse = csv.parse;
			var csvParseRows = csv.parseRows;
			var csvFormat = csv.format;
			var csvFormatRows = csv.formatRows;

			var tsv = dsv("\t");

			var tsvParse = tsv.parse;
			var tsvParseRows = tsv.parseRows;
			var tsvFormat = tsv.format;
			var tsvFormatRows = tsv.formatRows;

			var request = function(url, callback) {
			  var request,
				  event = dispatch("beforesend", "progress", "load", "error"),
				  mimeType,
				  headers = map$1(),
				  xhr = new XMLHttpRequest,
				  user = null,
				  password = null,
				  response,
				  responseType,
				  timeout = 0;

			  // If IE does not support CORS, use XDomainRequest.
			  if (typeof XDomainRequest !== "undefined"
				  && !("withCredentials" in xhr)
				  && /^(http(s)?:)?\/\//.test(url)) xhr = new XDomainRequest;

			  "onload" in xhr
				  ? xhr.onload = xhr.onerror = xhr.ontimeout = respond
				  : xhr.onreadystatechange = function(o) { xhr.readyState > 3 && respond(o); };

			  function respond(o) {
				var status = xhr.status, result;
				if (!status && hasResponse(xhr)
					|| status >= 200 && status < 300
					|| status === 304) {
				  if (response) {
					try {
					  result = response.call(request, xhr);
					} catch (e) {
					  event.call("error", request, e);
					  return;
					}
				  } else {
					result = xhr;
				  }
				  event.call("load", request, result);
				} else {
				  event.call("error", request, o);
				}
			  }

			  xhr.onprogress = function(e) {
				event.call("progress", request, e);
			  };

			  request = {
				header: function(name, value) {
				  name = (name + "").toLowerCase();
				  if (arguments.length < 2) return headers.get(name);
				  if (value == null) headers.remove(name);
				  else headers.set(name, value + "");
				  return request;
				},

				// If mimeType is non-null and no Accept header is set, a default is used.
				mimeType: function(value) {
				  if (!arguments.length) return mimeType;
				  mimeType = value == null ? null : value + "";
				  return request;
				},

				// Specifies what type the response value should take;
				// for instance, arraybuffer, blob, document, or text.
				responseType: function(value) {
				  if (!arguments.length) return responseType;
				  responseType = value;
				  return request;
				},

				timeout: function(value) {
				  if (!arguments.length) return timeout;
				  timeout = +value;
				  return request;
				},

				user: function(value) {
				  return arguments.length < 1 ? user : (user = value == null ? null : value + "", request);
				},

				password: function(value) {
				  return arguments.length < 1 ? password : (password = value == null ? null : value + "", request);
				},

				// Specify how to convert the response content to a specific type;
				// changes the callback value on "load" events.
				response: function(value) {
				  response = value;
				  return request;
				},

				// Alias for send("GET", …).
				get: function(data, callback) {
				  return request.send("GET", data, callback);
				},

				// Alias for send("POST", …).
				post: function(data, callback) {
				  return request.send("POST", data, callback);
				},

				// If callback is non-null, it will be used for error and load events.
				send: function(method, data, callback) {
				  xhr.open(method, url, true, user, password);
				  if (mimeType != null && !headers.has("accept")) headers.set("accept", mimeType + ",*/*");
				  if (xhr.setRequestHeader) headers.each(function(value, name) { xhr.setRequestHeader(name, value); });
				  if (mimeType != null && xhr.overrideMimeType) xhr.overrideMimeType(mimeType);
				  if (responseType != null) xhr.responseType = responseType;
				  if (timeout > 0) xhr.timeout = timeout;
				  if (callback == null && typeof data === "function") callback = data, data = null;
				  if (callback != null && callback.length === 1) callback = fixCallback(callback);
				  if (callback != null) request.on("error", callback).on("load", function(xhr) { callback(null, xhr); });
				  event.call("beforesend", request, xhr);
				  xhr.send(data == null ? null : data);
				  return request;
				},

				abort: function() {
				  xhr.abort();
				  return request;
				},

				on: function() {
				  var value = event.on.apply(event, arguments);
				  return value === event ? request : value;
				}
			  };

			  if (callback != null) {
				if (typeof callback !== "function") throw new Error("invalid callback: " + callback);
				return request.get(callback);
			  }

			  return request;
			};

			function fixCallback(callback) {
			  return function(error, xhr) {
				callback(error == null ? xhr : null);
			  };
			}

			function hasResponse(xhr) {
			  var type = xhr.responseType;
			  return type && type !== "text"
				  ? xhr.response // null on error
				  : xhr.responseText; // "" on error
			}

			var type = function(defaultMimeType, response) {
			  return function(url, callback) {
				var r = request(url).mimeType(defaultMimeType).response(response);
				if (callback != null) {
				  if (typeof callback !== "function") throw new Error("invalid callback: " + callback);
				  return r.get(callback);
				}
				return r;
			  };
			};

			var html = type("text/html", function(xhr) {
			  return document.createRange().createContextualFragment(xhr.responseText);
			});

			var json = type("application/json", function(xhr) {
			  return JSON.parse(xhr.responseText);
			});

			var text = type("text/plain", function(xhr) {
			  return xhr.responseText;
			});

			var xml = type("application/xml", function(xhr) {
			  var xml = xhr.responseXML;
			  if (!xml) throw new Error("parse error");
			  return xml;
			});

			var dsv$1 = function(defaultMimeType, parse) {
			  return function(url, row, callback) {
				if (arguments.length < 3) callback = row, row = null;
				var r = request(url).mimeType(defaultMimeType);
				r.row = function(_) { return arguments.length ? r.response(responseOf(parse, row = _)) : row; };
				r.row(row);
				return callback ? r.get(callback) : r;
			  };
			};

			function responseOf(parse, row) {
			  return function(request$$1) {
				return parse(request$$1.responseText, row);
			  };
			}

			var csv$1 = dsv$1("text/csv", csvParse);

			var tsv$1 = dsv$1("text/tab-separated-values", tsvParse);

			var frame = 0;
			var timeout = 0;
			var interval = 0;
			var pokeDelay = 1000;
			var taskHead;
			var taskTail;
			var clockLast = 0;
			var clockNow = 0;
			var clockSkew = 0;
			var clock = typeof performance === "object" && performance.now ? performance : Date;
			var setFrame = typeof requestAnimationFrame === "function" ? requestAnimationFrame : function(f) { setTimeout(f, 17); };

			function now() {
			  return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);
			}

			function clearNow() {
			  clockNow = 0;
			}

			function Timer() {
			  this._call =
			  this._time =
			  this._next = null;
			}

			Timer.prototype = timer.prototype = {
			  constructor: Timer,
			  restart: function(callback, delay, time) {
				if (typeof callback !== "function") throw new TypeError("callback is not a function");
				time = (time == null ? now() : +time) + (delay == null ? 0 : +delay);
				if (!this._next && taskTail !== this) {
				  if (taskTail) taskTail._next = this;
				  else taskHead = this;
				  taskTail = this;
				}
				this._call = callback;
				this._time = time;
				sleep();
			  },
			  stop: function() {
				if (this._call) {
				  this._call = null;
				  this._time = Infinity;
				  sleep();
				}
			  }
			};

			function timer(callback, delay, time) {
			  var t = new Timer;
			  t.restart(callback, delay, time);
			  return t;
			}

			function timerFlush() {
			  now(); // Get the current time, if not already set.
			  ++frame; // Pretend we’ve set an alarm, if we haven’t already.
			  var t = taskHead, e;
			  while (t) {
				if ((e = clockNow - t._time) >= 0) t._call.call(null, e);
				t = t._next;
			  }
			  --frame;
			}

			function wake() {
			  clockNow = (clockLast = clock.now()) + clockSkew;
			  frame = timeout = 0;
			  try {
				timerFlush();
			  } finally {
				frame = 0;
				nap();
				clockNow = 0;
			  }
			}

			function poke$1() {
			  var now = clock.now(), delay = now - clockLast;
			  if (delay > pokeDelay) clockSkew -= delay, clockLast = now;
			}

			function nap() {
			  var t0, t1 = taskHead, t2, time = Infinity;
			  while (t1) {
				if (t1._call) {
				  if (time > t1._time) time = t1._time;
				  t0 = t1, t1 = t1._next;
				} else {
				  t2 = t1._next, t1._next = null;
				  t1 = t0 ? t0._next = t2 : taskHead = t2;
				}
			  }
			  taskTail = t0;
			  sleep(time);
			}

			function sleep(time) {
			  if (frame) return; // Soonest alarm already set, or will be.
			  if (timeout) timeout = clearTimeout(timeout);
			  var delay = time - clockNow;
			  if (delay > 24) {
				if (time < Infinity) timeout = setTimeout(wake, delay);
				if (interval) interval = clearInterval(interval);
			  } else {
				if (!interval) interval = setInterval(poke$1, pokeDelay);
				frame = 1, setFrame(wake);
			  }
			}

			var timeout$1 = function(callback, delay, time) {
			  var t = new Timer;
			  delay = delay == null ? 0 : +delay;
			  t.restart(function(elapsed) {
				t.stop();
				callback(elapsed + delay);
			  }, delay, time);
			  return t;
			};

			var interval$1 = function(callback, delay, time) {
			  var t = new Timer, total = delay;
			  if (delay == null) return t.restart(callback, delay, time), t;
			  delay = +delay, time = time == null ? now() : +time;
			  t.restart(function tick(elapsed) {
				elapsed += total;
				t.restart(tick, total += delay, time);
				callback(elapsed);
			  }, delay, time);
			  return t;
			};

			var t0$1 = new Date;
			var t1$1 = new Date;

			function newInterval(floori, offseti, count, field) {

			  function interval(date) {
				return floori(date = new Date(+date)), date;
			  }

			  interval.floor = interval;

			  interval.ceil = function(date) {
				return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date;
			  };

			  interval.round = function(date) {
				var d0 = interval(date),
					d1 = interval.ceil(date);
				return date - d0 < d1 - date ? d0 : d1;
			  };

			  interval.offset = function(date, step) {
				return offseti(date = new Date(+date), step == null ? 1 : Math.floor(step)), date;
			  };

			  interval.range = function(start, stop, step) {
				var range = [];
				start = interval.ceil(start);
				step = step == null ? 1 : Math.floor(step);
				if (!(start < stop) || !(step > 0)) return range; // also handles Invalid Date
				do range.push(new Date(+start)); while (offseti(start, step), floori(start), start < stop)
				return range;
			  };

			  interval.filter = function(test) {
				return newInterval(function(date) {
				  if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1);
				}, function(date, step) {
				  if (date >= date) while (--step >= 0) while (offseti(date, 1), !test(date)) {} // eslint-disable-line no-empty
				});
			  };

			  if (count) {
				interval.count = function(start, end) {
				  t0$1.setTime(+start), t1$1.setTime(+end);
				  floori(t0$1), floori(t1$1);
				  return Math.floor(count(t0$1, t1$1));
				};

				interval.every = function(step) {
				  step = Math.floor(step);
				  return !isFinite(step) || !(step > 0) ? null
					  : !(step > 1) ? interval
					  : interval.filter(field
						  ? function(d) { return field(d) % step === 0; }
						  : function(d) { return interval.count(0, d) % step === 0; });
				};
			  }

			  return interval;
			}

			var millisecond = newInterval(function() {
			  // noop
			}, function(date, step) {
			  date.setTime(+date + step);
			}, function(start, end) {
			  return end - start;
			});

			// An optimized implementation for this simple case.
			millisecond.every = function(k) {
			  k = Math.floor(k);
			  if (!isFinite(k) || !(k > 0)) return null;
			  if (!(k > 1)) return millisecond;
			  return newInterval(function(date) {
				date.setTime(Math.floor(date / k) * k);
			  }, function(date, step) {
				date.setTime(+date + step * k);
			  }, function(start, end) {
				return (end - start) / k;
			  });
			};

			var milliseconds = millisecond.range;

			var durationSecond = 1e3;
			var durationMinute = 6e4;
			var durationHour = 36e5;
			var durationDay = 864e5;
			var durationWeek = 6048e5;

			var second = newInterval(function(date) {
			  date.setTime(Math.floor(date / durationSecond) * durationSecond);
			}, function(date, step) {
			  date.setTime(+date + step * durationSecond);
			}, function(start, end) {
			  return (end - start) / durationSecond;
			}, function(date) {
			  return date.getUTCSeconds();
			});

			var seconds = second.range;

			var minute = newInterval(function(date) {
			  date.setTime(Math.floor(date / durationMinute) * durationMinute);
			}, function(date, step) {
			  date.setTime(+date + step * durationMinute);
			}, function(start, end) {
			  return (end - start) / durationMinute;
			}, function(date) {
			  return date.getMinutes();
			});

			var minutes = minute.range;

			var hour = newInterval(function(date) {
			  var offset = date.getTimezoneOffset() * durationMinute % durationHour;
			  if (offset < 0) offset += durationHour;
			  date.setTime(Math.floor((+date - offset) / durationHour) * durationHour + offset);
			}, function(date, step) {
			  date.setTime(+date + step * durationHour);
			}, function(start, end) {
			  return (end - start) / durationHour;
			}, function(date) {
			  return date.getHours();
			});

			var hours = hour.range;

			var day = newInterval(function(date) {
			  date.setHours(0, 0, 0, 0);
			}, function(date, step) {
			  date.setDate(date.getDate() + step);
			}, function(start, end) {
			  return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationDay;
			}, function(date) {
			  return date.getDate() - 1;
			});

			var days = day.range;

			function weekday(i) {
			  return newInterval(function(date) {
				date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7);
				date.setHours(0, 0, 0, 0);
			  }, function(date, step) {
				date.setDate(date.getDate() + step * 7);
			  }, function(start, end) {
				return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationWeek;
			  });
			}

			var sunday = weekday(0);
			var monday = weekday(1);
			var tuesday = weekday(2);
			var wednesday = weekday(3);
			var thursday = weekday(4);
			var friday = weekday(5);
			var saturday = weekday(6);

			var sundays = sunday.range;
			var mondays = monday.range;
			var tuesdays = tuesday.range;
			var wednesdays = wednesday.range;
			var thursdays = thursday.range;
			var fridays = friday.range;
			var saturdays = saturday.range;

			var month = newInterval(function(date) {
			  date.setDate(1);
			  date.setHours(0, 0, 0, 0);
			}, function(date, step) {
			  date.setMonth(date.getMonth() + step);
			}, function(start, end) {
			  return end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12;
			}, function(date) {
			  return date.getMonth();
			});

			var months = month.range;

			var year = newInterval(function(date) {
			  date.setMonth(0, 1);
			  date.setHours(0, 0, 0, 0);
			}, function(date, step) {
			  date.setFullYear(date.getFullYear() + step);
			}, function(start, end) {
			  return end.getFullYear() - start.getFullYear();
			}, function(date) {
			  return date.getFullYear();
			});

			// An optimized implementation for this simple case.
			year.every = function(k) {
			  return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) {
				date.setFullYear(Math.floor(date.getFullYear() / k) * k);
				date.setMonth(0, 1);
				date.setHours(0, 0, 0, 0);
			  }, function(date, step) {
				date.setFullYear(date.getFullYear() + step * k);
			  });
			};

			var years = year.range;

			var utcMinute = newInterval(function(date) {
			  date.setUTCSeconds(0, 0);
			}, function(date, step) {
			  date.setTime(+date + step * durationMinute);
			}, function(start, end) {
			  return (end - start) / durationMinute;
			}, function(date) {
			  return date.getUTCMinutes();
			});

			var utcMinutes = utcMinute.range;

			var utcHour = newInterval(function(date) {
			  date.setUTCMinutes(0, 0, 0);
			}, function(date, step) {
			  date.setTime(+date + step * durationHour);
			}, function(start, end) {
			  return (end - start) / durationHour;
			}, function(date) {
			  return date.getUTCHours();
			});

			var utcHours = utcHour.range;

			var utcDay = newInterval(function(date) {
			  date.setUTCHours(0, 0, 0, 0);
			}, function(date, step) {
			  date.setUTCDate(date.getUTCDate() + step);
			}, function(start, end) {
			  return (end - start) / durationDay;
			}, function(date) {
			  return date.getUTCDate() - 1;
			});

			var utcDays = utcDay.range;

			function utcWeekday(i) {
			  return newInterval(function(date) {
				date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7);
				date.setUTCHours(0, 0, 0, 0);
			  }, function(date, step) {
				date.setUTCDate(date.getUTCDate() + step * 7);
			  }, function(start, end) {
				return (end - start) / durationWeek;
			  });
			}

			var utcSunday = utcWeekday(0);
			var utcMonday = utcWeekday(1);
			var utcTuesday = utcWeekday(2);
			var utcWednesday = utcWeekday(3);
			var utcThursday = utcWeekday(4);
			var utcFriday = utcWeekday(5);
			var utcSaturday = utcWeekday(6);

			var utcSundays = utcSunday.range;
			var utcMondays = utcMonday.range;
			var utcTuesdays = utcTuesday.range;
			var utcWednesdays = utcWednesday.range;
			var utcThursdays = utcThursday.range;
			var utcFridays = utcFriday.range;
			var utcSaturdays = utcSaturday.range;

			var utcMonth = newInterval(function(date) {
			  date.setUTCDate(1);
			  date.setUTCHours(0, 0, 0, 0);
			}, function(date, step) {
			  date.setUTCMonth(date.getUTCMonth() + step);
			}, function(start, end) {
			  return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12;
			}, function(date) {
			  return date.getUTCMonth();
			});

			var utcMonths = utcMonth.range;

			var utcYear = newInterval(function(date) {
			  date.setUTCMonth(0, 1);
			  date.setUTCHours(0, 0, 0, 0);
			}, function(date, step) {
			  date.setUTCFullYear(date.getUTCFullYear() + step);
			}, function(start, end) {
			  return end.getUTCFullYear() - start.getUTCFullYear();
			}, function(date) {
			  return date.getUTCFullYear();
			});

			// An optimized implementation for this simple case.
			utcYear.every = function(k) {
			  return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) {
				date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k);
				date.setUTCMonth(0, 1);
				date.setUTCHours(0, 0, 0, 0);
			  }, function(date, step) {
				date.setUTCFullYear(date.getUTCFullYear() + step * k);
			  });
			};

			var utcYears = utcYear.range;

			// Computes the decimal coefficient and exponent of the specified number x with
			// significant digits p, where x is positive and p is in [1, 21] or undefined.
			// For example, formatDecimal(1.23) returns ["123", 0].
			var formatDecimal = function(x, p) {
			  if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity
			  var i, coefficient = x.slice(0, i);

			  // The string returned by toExponential either has the form \d\.\d+e[-+]\d+
			  // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
			  return [
				coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient,
				+x.slice(i + 1)
			  ];
			};

			var exponent$1 = function(x) {
			  return x = formatDecimal(Math.abs(x)), x ? x[1] : NaN;
			};

			var formatGroup = function(grouping, thousands) {
			  return function(value, width) {
				var i = value.length,
					t = [],
					j = 0,
					g = grouping[0],
					length = 0;

				while (i > 0 && g > 0) {
				  if (length + g + 1 > width) g = Math.max(1, width - length);
				  t.push(value.substring(i -= g, i + g));
				  if ((length += g + 1) > width) break;
				  g = grouping[j = (j + 1) % grouping.length];
				}

				return t.reverse().join(thousands);
			  };
			};

			var formatDefault = function(x, p) {
			  x = x.toPrecision(p);

			  out: for (var n = x.length, i = 1, i0 = -1, i1; i < n; ++i) {
				switch (x[i]) {
				  case ".": i0 = i1 = i; break;
				  case "0": if (i0 === 0) i0 = i; i1 = i; break;
				  case "e": break out;
				  default: if (i0 > 0) i0 = 0; break;
				}
			  }

			  return i0 > 0 ? x.slice(0, i0) + x.slice(i1 + 1) : x;
			};

			var prefixExponent;

			var formatPrefixAuto = function(x, p) {
			  var d = formatDecimal(x, p);
			  if (!d) return x + "";
			  var coefficient = d[0],
				  exponent = d[1],
				  i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
				  n = coefficient.length;
			  return i === n ? coefficient
				  : i > n ? coefficient + new Array(i - n + 1).join("0")
				  : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i)
				  : "0." + new Array(1 - i).join("0") + formatDecimal(x, Math.max(0, p + i - 1))[0]; // less than 1y!
			};

			var formatRounded = function(x, p) {
			  var d = formatDecimal(x, p);
			  if (!d) return x + "";
			  var coefficient = d[0],
				  exponent = d[1];
			  return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient
				  : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1)
				  : coefficient + new Array(exponent - coefficient.length + 2).join("0");
			};

			var formatTypes = {
			  "": formatDefault,
			  "%": function(x, p) { return (x * 100).toFixed(p); },
			  "b": function(x) { return Math.round(x).toString(2); },
			  "c": function(x) { return x + ""; },
			  "d": function(x) { return Math.round(x).toString(10); },
			  "e": function(x, p) { return x.toExponential(p); },
			  "f": function(x, p) { return x.toFixed(p); },
			  "g": function(x, p) { return x.toPrecision(p); },
			  "o": function(x) { return Math.round(x).toString(8); },
			  "p": function(x, p) { return formatRounded(x * 100, p); },
			  "r": formatRounded,
			  "s": formatPrefixAuto,
			  "X": function(x) { return Math.round(x).toString(16).toUpperCase(); },
			  "x": function(x) { return Math.round(x).toString(16); }
			};

			// [[fill]align][sign][symbol][0][width][,][.precision][type]
			var re = /^(?:(.)?([<>=^]))?([+\-\( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?([a-z%])?$/i;

			var formatSpecifier = function(specifier) {
			  return new FormatSpecifier(specifier);
			};

			function FormatSpecifier(specifier) {
			  if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier);

			  var match,
				  fill = match[1] || " ",
				  align = match[2] || ">",
				  sign = match[3] || "-",
				  symbol = match[4] || "",
				  zero = !!match[5],
				  width = match[6] && +match[6],
				  comma = !!match[7],
				  precision = match[8] && +match[8].slice(1),
				  type = match[9] || "";

			  // The "n" type is an alias for ",g".
			  if (type === "n") comma = true, type = "g";

			  // Map invalid types to the default format.
			  else if (!formatTypes[type]) type = "";

			  // If zero fill is specified, padding goes after sign and before digits.
			  if (zero || (fill === "0" && align === "=")) zero = true, fill = "0", align = "=";

			  this.fill = fill;
			  this.align = align;
			  this.sign = sign;
			  this.symbol = symbol;
			  this.zero = zero;
			  this.width = width;
			  this.comma = comma;
			  this.precision = precision;
			  this.type = type;
			}

			FormatSpecifier.prototype.toString = function() {
			  return this.fill
				  + this.align
				  + this.sign
				  + this.symbol
				  + (this.zero ? "0" : "")
				  + (this.width == null ? "" : Math.max(1, this.width | 0))
				  + (this.comma ? "," : "")
				  + (this.precision == null ? "" : "." + Math.max(0, this.precision | 0))
				  + this.type;
			};

			var prefixes = ["y","z","a","f","p","n","\xB5","m","","k","M","G","T","P","E","Z","Y"];

			function identity$3(x) {
			  return x;
			}

			var formatLocale = function(locale) {
			  var group = locale.grouping && locale.thousands ? formatGroup(locale.grouping, locale.thousands) : identity$3,
				  currency = locale.currency,
				  decimal = locale.decimal;

			  function newFormat(specifier) {
				specifier = formatSpecifier(specifier);

				var fill = specifier.fill,
					align = specifier.align,
					sign = specifier.sign,
					symbol = specifier.symbol,
					zero = specifier.zero,
					width = specifier.width,
					comma = specifier.comma,
					precision = specifier.precision,
					type = specifier.type;

				// Compute the prefix and suffix.
				// For SI-prefix, the suffix is lazily computed.
				var prefix = symbol === "$" ? currency[0] : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "",
					suffix = symbol === "$" ? currency[1] : /[%p]/.test(type) ? "%" : "";

				// What format function should we use?
				// Is this an integer type?
				// Can this type generate exponential notation?
				var formatType = formatTypes[type],
					maybeSuffix = !type || /[defgprs%]/.test(type);

				// Set the default precision if not specified,
				// or clamp the specified precision to the supported range.
				// For significant precision, it must be in [1, 21].
				// For fixed precision, it must be in [0, 20].
				precision = precision == null ? (type ? 6 : 12)
					: /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision))
					: Math.max(0, Math.min(20, precision));

				function format(value) {
				  var valuePrefix = prefix,
					  valueSuffix = suffix,
					  i, n, c;

				  if (type === "c") {
					valueSuffix = formatType(value) + valueSuffix;
					value = "";
				  } else {
					value = +value;

					// Convert negative to positive, and compute the prefix.
					// Note that -0 is not less than 0, but 1 / -0 is!
					var valueNegative = (value < 0 || 1 / value < 0) && (value *= -1, true);

					// Perform the initial formatting.
					value = formatType(value, precision);

					// If the original value was negative, it may be rounded to zero during
					// formatting; treat this as (positive) zero.
					if (valueNegative) {
					  i = -1, n = value.length;
					  valueNegative = false;
					  while (++i < n) {
						if (c = value.charCodeAt(i), (48 < c && c < 58)
							|| (type === "x" && 96 < c && c < 103)
							|| (type === "X" && 64 < c && c < 71)) {
						  valueNegative = true;
						  break;
						}
					  }
					}

					// Compute the prefix and suffix.
					valuePrefix = (valueNegative ? (sign === "(" ? sign : "-") : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
					valueSuffix = valueSuffix + (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + (valueNegative && sign === "(" ? ")" : "");

					// Break the formatted value into the integer “value” part that can be
					// grouped, and fractional or exponential “suffix” part that is not.
					if (maybeSuffix) {
					  i = -1, n = value.length;
					  while (++i < n) {
						if (c = value.charCodeAt(i), 48 > c || c > 57) {
						  valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;
						  value = value.slice(0, i);
						  break;
						}
					  }
					}
				  }

				  // If the fill character is not "0", grouping is applied before padding.
				  if (comma && !zero) value = group(value, Infinity);

				  // Compute the padding.
				  var length = valuePrefix.length + value.length + valueSuffix.length,
					  padding = length < width ? new Array(width - length + 1).join(fill) : "";

				  // If the fill character is "0", grouping is applied after padding.
				  if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = "";

				  // Reconstruct the final output based on the desired alignment.
				  switch (align) {
					case "<": return valuePrefix + value + valueSuffix + padding;
					case "=": return valuePrefix + padding + value + valueSuffix;
					case "^": return padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length);
				  }
				  return padding + valuePrefix + value + valueSuffix;
				}

				format.toString = function() {
				  return specifier + "";
				};

				return format;
			  }

			  function formatPrefix(specifier, value) {
				var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
					e = Math.max(-8, Math.min(8, Math.floor(exponent$1(value) / 3))) * 3,
					k = Math.pow(10, -e),
					prefix = prefixes[8 + e / 3];
				return function(value) {
				  return f(k * value) + prefix;
				};
			  }

			  return {
				format: newFormat,
				formatPrefix: formatPrefix
			  };
			};

			var locale$1;



			defaultLocale({
			  decimal: ".",
			  thousands: ",",
			  grouping: [3],
			  currency: ["$", ""]
			});

			function defaultLocale(definition) {
			  locale$1 = formatLocale(definition);
			  exports.format = locale$1.format;
			  exports.formatPrefix = locale$1.formatPrefix;
			  return locale$1;
			}

			var precisionFixed = function(step) {
			  return Math.max(0, -exponent$1(Math.abs(step)));
			};

			var precisionPrefix = function(step, value) {
			  return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent$1(value) / 3))) * 3 - exponent$1(Math.abs(step)));
			};

			var precisionRound = function(step, max) {
			  step = Math.abs(step), max = Math.abs(max) - step;
			  return Math.max(0, exponent$1(max) - exponent$1(step)) + 1;
			};

			function localDate(d) {
			  if (0 <= d.y && d.y < 100) {
				var date = new Date(-1, d.m, d.d, d.H, d.M, d.S, d.L);
				date.setFullYear(d.y);
				return date;
			  }
			  return new Date(d.y, d.m, d.d, d.H, d.M, d.S, d.L);
			}

			function utcDate(d) {
			  if (0 <= d.y && d.y < 100) {
				var date = new Date(Date.UTC(-1, d.m, d.d, d.H, d.M, d.S, d.L));
				date.setUTCFullYear(d.y);
				return date;
			  }
			  return new Date(Date.UTC(d.y, d.m, d.d, d.H, d.M, d.S, d.L));
			}

			function newYear(y) {
			  return {y: y, m: 0, d: 1, H: 0, M: 0, S: 0, L: 0};
			}

			function formatLocale$1(locale) {
			  var locale_dateTime = locale.dateTime,
				  locale_date = locale.date,
				  locale_time = locale.time,
				  locale_periods = locale.periods,
				  locale_weekdays = locale.days,
				  locale_shortWeekdays = locale.shortDays,
				  locale_months = locale.months,
				  locale_shortMonths = locale.shortMonths;

			  var periodRe = formatRe(locale_periods),
				  periodLookup = formatLookup(locale_periods),
				  weekdayRe = formatRe(locale_weekdays),
				  weekdayLookup = formatLookup(locale_weekdays),
				  shortWeekdayRe = formatRe(locale_shortWeekdays),
				  shortWeekdayLookup = formatLookup(locale_shortWeekdays),
				  monthRe = formatRe(locale_months),
				  monthLookup = formatLookup(locale_months),
				  shortMonthRe = formatRe(locale_shortMonths),
				  shortMonthLookup = formatLookup(locale_shortMonths);

			  var formats = {
				"a": formatShortWeekday,
				"A": formatWeekday,
				"b": formatShortMonth,
				"B": formatMonth,
				"c": null,
				"d": formatDayOfMonth,
				"e": formatDayOfMonth,
				"H": formatHour24,
				"I": formatHour12,
				"j": formatDayOfYear,
				"L": formatMilliseconds,
				"m": formatMonthNumber,
				"M": formatMinutes,
				"p": formatPeriod,
				"S": formatSeconds,
				"U": formatWeekNumberSunday,
				"w": formatWeekdayNumber,
				"W": formatWeekNumberMonday,
				"x": null,
				"X": null,
				"y": formatYear,
				"Y": formatFullYear,
				"Z": formatZone,
				"%": formatLiteralPercent
			  };

			  var utcFormats = {
				"a": formatUTCShortWeekday,
				"A": formatUTCWeekday,
				"b": formatUTCShortMonth,
				"B": formatUTCMonth,
				"c": null,
				"d": formatUTCDayOfMonth,
				"e": formatUTCDayOfMonth,
				"H": formatUTCHour24,
				"I": formatUTCHour12,
				"j": formatUTCDayOfYear,
				"L": formatUTCMilliseconds,
				"m": formatUTCMonthNumber,
				"M": formatUTCMinutes,
				"p": formatUTCPeriod,
				"S": formatUTCSeconds,
				"U": formatUTCWeekNumberSunday,
				"w": formatUTCWeekdayNumber,
				"W": formatUTCWeekNumberMonday,
				"x": null,
				"X": null,
				"y": formatUTCYear,
				"Y": formatUTCFullYear,
				"Z": formatUTCZone,
				"%": formatLiteralPercent
			  };

			  var parses = {
				"a": parseShortWeekday,
				"A": parseWeekday,
				"b": parseShortMonth,
				"B": parseMonth,
				"c": parseLocaleDateTime,
				"d": parseDayOfMonth,
				"e": parseDayOfMonth,
				"H": parseHour24,
				"I": parseHour24,
				"j": parseDayOfYear,
				"L": parseMilliseconds,
				"m": parseMonthNumber,
				"M": parseMinutes,
				"p": parsePeriod,
				"S": parseSeconds,
				"U": parseWeekNumberSunday,
				"w": parseWeekdayNumber,
				"W": parseWeekNumberMonday,
				"x": parseLocaleDate,
				"X": parseLocaleTime,
				"y": parseYear,
				"Y": parseFullYear,
				"Z": parseZone,
				"%": parseLiteralPercent
			  };

			  // These recursive directive definitions must be deferred.
			  formats.x = newFormat(locale_date, formats);
			  formats.X = newFormat(locale_time, formats);
			  formats.c = newFormat(locale_dateTime, formats);
			  utcFormats.x = newFormat(locale_date, utcFormats);
			  utcFormats.X = newFormat(locale_time, utcFormats);
			  utcFormats.c = newFormat(locale_dateTime, utcFormats);

			  function newFormat(specifier, formats) {
				return function(date) {
				  var string = [],
					  i = -1,
					  j = 0,
					  n = specifier.length,
					  c,
					  pad,
					  format;

				  if (!(date instanceof Date)) date = new Date(+date);

				  while (++i < n) {
					if (specifier.charCodeAt(i) === 37) {
					  string.push(specifier.slice(j, i));
					  if ((pad = pads[c = specifier.charAt(++i)]) != null) c = specifier.charAt(++i);
					  else pad = c === "e" ? " " : "0";
					  if (format = formats[c]) c = format(date, pad);
					  string.push(c);
					  j = i + 1;
					}
				  }

				  string.push(specifier.slice(j, i));
				  return string.join("");
				};
			  }

			  function newParse(specifier, newDate) {
				return function(string) {
				  var d = newYear(1900),
					  i = parseSpecifier(d, specifier, string += "", 0);
				  if (i != string.length) return null;

				  // The am-pm flag is 0 for AM, and 1 for PM.
				  if ("p" in d) d.H = d.H % 12 + d.p * 12;

				  // Convert day-of-week and week-of-year to day-of-year.
				  if ("W" in d || "U" in d) {
					if (!("w" in d)) d.w = "W" in d ? 1 : 0;
					var day$$1 = "Z" in d ? utcDate(newYear(d.y)).getUTCDay() : newDate(newYear(d.y)).getDay();
					d.m = 0;
					d.d = "W" in d ? (d.w + 6) % 7 + d.W * 7 - (day$$1 + 5) % 7 : d.w + d.U * 7 - (day$$1 + 6) % 7;
				  }

				  // If a time zone is specified, all fields are interpreted as UTC and then
				  // offset according to the specified time zone.
				  if ("Z" in d) {
					d.H += d.Z / 100 | 0;
					d.M += d.Z % 100;
					return utcDate(d);
				  }

				  // Otherwise, all fields are in local time.
				  return newDate(d);
				};
			  }

			  function parseSpecifier(d, specifier, string, j) {
				var i = 0,
					n = specifier.length,
					m = string.length,
					c,
					parse;

				while (i < n) {
				  if (j >= m) return -1;
				  c = specifier.charCodeAt(i++);
				  if (c === 37) {
					c = specifier.charAt(i++);
					parse = parses[c in pads ? specifier.charAt(i++) : c];
					if (!parse || ((j = parse(d, string, j)) < 0)) return -1;
				  } else if (c != string.charCodeAt(j++)) {
					return -1;
				  }
				}

				return j;
			  }

			  function parsePeriod(d, string, i) {
				var n = periodRe.exec(string.slice(i));
				return n ? (d.p = periodLookup[n[0].toLowerCase()], i + n[0].length) : -1;
			  }

			  function parseShortWeekday(d, string, i) {
				var n = shortWeekdayRe.exec(string.slice(i));
				return n ? (d.w = shortWeekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;
			  }

			  function parseWeekday(d, string, i) {
				var n = weekdayRe.exec(string.slice(i));
				return n ? (d.w = weekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;
			  }

			  function parseShortMonth(d, string, i) {
				var n = shortMonthRe.exec(string.slice(i));
				return n ? (d.m = shortMonthLookup[n[0].toLowerCase()], i + n[0].length) : -1;
			  }

			  function parseMonth(d, string, i) {
				var n = monthRe.exec(string.slice(i));
				return n ? (d.m = monthLookup[n[0].toLowerCase()], i + n[0].length) : -1;
			  }

			  function parseLocaleDateTime(d, string, i) {
				return parseSpecifier(d, locale_dateTime, string, i);
			  }

			  function parseLocaleDate(d, string, i) {
				return parseSpecifier(d, locale_date, string, i);
			  }

			  function parseLocaleTime(d, string, i) {
				return parseSpecifier(d, locale_time, string, i);
			  }

			  function formatShortWeekday(d) {
				return locale_shortWeekdays[d.getDay()];
			  }

			  function formatWeekday(d) {
				return locale_weekdays[d.getDay()];
			  }

			  function formatShortMonth(d) {
				return locale_shortMonths[d.getMonth()];
			  }

			  function formatMonth(d) {
				return locale_months[d.getMonth()];
			  }

			  function formatPeriod(d) {
				return locale_periods[+(d.getHours() >= 12)];
			  }

			  function formatUTCShortWeekday(d) {
				return locale_shortWeekdays[d.getUTCDay()];
			  }

			  function formatUTCWeekday(d) {
				return locale_weekdays[d.getUTCDay()];
			  }

			  function formatUTCShortMonth(d) {
				return locale_shortMonths[d.getUTCMonth()];
			  }

			  function formatUTCMonth(d) {
				return locale_months[d.getUTCMonth()];
			  }

			  function formatUTCPeriod(d) {
				return locale_periods[+(d.getUTCHours() >= 12)];
			  }

			  return {
				format: function(specifier) {
				  var f = newFormat(specifier += "", formats);
				  f.toString = function() { return specifier; };
				  return f;
				},
				parse: function(specifier) {
				  var p = newParse(specifier += "", localDate);
				  p.toString = function() { return specifier; };
				  return p;
				},
				utcFormat: function(specifier) {
				  var f = newFormat(specifier += "", utcFormats);
				  f.toString = function() { return specifier; };
				  return f;
				},
				utcParse: function(specifier) {
				  var p = newParse(specifier, utcDate);
				  p.toString = function() { return specifier; };
				  return p;
				}
			  };
			}

			var pads = {"-": "", "_": " ", "0": "0"};
			var numberRe = /^\s*\d+/;
			var percentRe = /^%/;
			var requoteRe = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;

			function pad(value, fill, width) {
			  var sign = value < 0 ? "-" : "",
				  string = (sign ? -value : value) + "",
				  length = string.length;
			  return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);
			}

			function requote(s) {
			  return s.replace(requoteRe, "\\$&");
			}

			function formatRe(names) {
			  return new RegExp("^(?:" + names.map(requote).join("|") + ")", "i");
			}

			function formatLookup(names) {
			  var map = {}, i = -1, n = names.length;
			  while (++i < n) map[names[i].toLowerCase()] = i;
			  return map;
			}

			function parseWeekdayNumber(d, string, i) {
			  var n = numberRe.exec(string.slice(i, i + 1));
			  return n ? (d.w = +n[0], i + n[0].length) : -1;
			}

			function parseWeekNumberSunday(d, string, i) {
			  var n = numberRe.exec(string.slice(i));
			  return n ? (d.U = +n[0], i + n[0].length) : -1;
			}

			function parseWeekNumberMonday(d, string, i) {
			  var n = numberRe.exec(string.slice(i));
			  return n ? (d.W = +n[0], i + n[0].length) : -1;
			}

			function parseFullYear(d, string, i) {
			  var n = numberRe.exec(string.slice(i, i + 4));
			  return n ? (d.y = +n[0], i + n[0].length) : -1;
			}

			function parseYear(d, string, i) {
			  var n = numberRe.exec(string.slice(i, i + 2));
			  return n ? (d.y = +n[0] + (+n[0] > 68 ? 1900 : 2000), i + n[0].length) : -1;
			}

			function parseZone(d, string, i) {
			  var n = /^(Z)|([+-]\d\d)(?:\:?(\d\d))?/.exec(string.slice(i, i + 6));
			  return n ? (d.Z = n[1] ? 0 : -(n[2] + (n[3] || "00")), i + n[0].length) : -1;
			}

			function parseMonthNumber(d, string, i) {
			  var n = numberRe.exec(string.slice(i, i + 2));
			  return n ? (d.m = n[0] - 1, i + n[0].length) : -1;
			}

			function parseDayOfMonth(d, string, i) {
			  var n = numberRe.exec(string.slice(i, i + 2));
			  return n ? (d.d = +n[0], i + n[0].length) : -1;
			}

			function parseDayOfYear(d, string, i) {
			  var n = numberRe.exec(string.slice(i, i + 3));
			  return n ? (d.m = 0, d.d = +n[0], i + n[0].length) : -1;
			}

			function parseHour24(d, string, i) {
			  var n = numberRe.exec(string.slice(i, i + 2));
			  return n ? (d.H = +n[0], i + n[0].length) : -1;
			}

			function parseMinutes(d, string, i) {
			  var n = numberRe.exec(string.slice(i, i + 2));
			  return n ? (d.M = +n[0], i + n[0].length) : -1;
			}

			function parseSeconds(d, string, i) {
			  var n = numberRe.exec(string.slice(i, i + 2));
			  return n ? (d.S = +n[0], i + n[0].length) : -1;
			}

			function parseMilliseconds(d, string, i) {
			  var n = numberRe.exec(string.slice(i, i + 3));
			  return n ? (d.L = +n[0], i + n[0].length) : -1;
			}

			function parseLiteralPercent(d, string, i) {
			  var n = percentRe.exec(string.slice(i, i + 1));
			  return n ? i + n[0].length : -1;
			}

			function formatDayOfMonth(d, p) {
			  return pad(d.getDate(), p, 2);
			}

			function formatHour24(d, p) {
			  return pad(d.getHours(), p, 2);
			}

			function formatHour12(d, p) {
			  return pad(d.getHours() % 12 || 12, p, 2);
			}

			function formatDayOfYear(d, p) {
			  return pad(1 + day.count(year(d), d), p, 3);
			}

			function formatMilliseconds(d, p) {
			  return pad(d.getMilliseconds(), p, 3);
			}

			function formatMonthNumber(d, p) {
			  return pad(d.getMonth() + 1, p, 2);
			}

			function formatMinutes(d, p) {
			  return pad(d.getMinutes(), p, 2);
			}

			function formatSeconds(d, p) {
			  return pad(d.getSeconds(), p, 2);
			}

			function formatWeekNumberSunday(d, p) {
			  return pad(sunday.count(year(d), d), p, 2);
			}

			function formatWeekdayNumber(d) {
			  return d.getDay();
			}

			function formatWeekNumberMonday(d, p) {
			  return pad(monday.count(year(d), d), p, 2);
			}

			function formatYear(d, p) {
			  return pad(d.getFullYear() % 100, p, 2);
			}

			function formatFullYear(d, p) {
			  return pad(d.getFullYear() % 10000, p, 4);
			}

			function formatZone(d) {
			  var z = d.getTimezoneOffset();
			  return (z > 0 ? "-" : (z *= -1, "+"))
				  + pad(z / 60 | 0, "0", 2)
				  + pad(z % 60, "0", 2);
			}

			function formatUTCDayOfMonth(d, p) {
			  return pad(d.getUTCDate(), p, 2);
			}

			function formatUTCHour24(d, p) {
			  return pad(d.getUTCHours(), p, 2);
			}

			function formatUTCHour12(d, p) {
			  return pad(d.getUTCHours() % 12 || 12, p, 2);
			}

			function formatUTCDayOfYear(d, p) {
			  return pad(1 + utcDay.count(utcYear(d), d), p, 3);
			}

			function formatUTCMilliseconds(d, p) {
			  return pad(d.getUTCMilliseconds(), p, 3);
			}

			function formatUTCMonthNumber(d, p) {
			  return pad(d.getUTCMonth() + 1, p, 2);
			}

			function formatUTCMinutes(d, p) {
			  return pad(d.getUTCMinutes(), p, 2);
			}

			function formatUTCSeconds(d, p) {
			  return pad(d.getUTCSeconds(), p, 2);
			}

			function formatUTCWeekNumberSunday(d, p) {
			  return pad(utcSunday.count(utcYear(d), d), p, 2);
			}

			function formatUTCWeekdayNumber(d) {
			  return d.getUTCDay();
			}

			function formatUTCWeekNumberMonday(d, p) {
			  return pad(utcMonday.count(utcYear(d), d), p, 2);
			}

			function formatUTCYear(d, p) {
			  return pad(d.getUTCFullYear() % 100, p, 2);
			}

			function formatUTCFullYear(d, p) {
			  return pad(d.getUTCFullYear() % 10000, p, 4);
			}

			function formatUTCZone() {
			  return "+0000";
			}

			function formatLiteralPercent() {
			  return "%";
			}

			var locale$2;





			defaultLocale$1({
			  dateTime: "%x, %X",
			  date: "%-m/%-d/%Y",
			  time: "%-I:%M:%S %p",
			  periods: ["AM", "PM"],
			  days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
			  shortDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
			  months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
			  shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
			});

			function defaultLocale$1(definition) {
			  locale$2 = formatLocale$1(definition);
			  exports.timeFormat = locale$2.format;
			  exports.timeParse = locale$2.parse;
			  exports.utcFormat = locale$2.utcFormat;
			  exports.utcParse = locale$2.utcParse;
			  return locale$2;
			}

			var isoSpecifier = "%Y-%m-%dT%H:%M:%S.%LZ";

			function formatIsoNative(date) {
			  return date.toISOString();
			}

			var formatIso = Date.prototype.toISOString
				? formatIsoNative
				: exports.utcFormat(isoSpecifier);

			function parseIsoNative(string) {
			  var date = new Date(string);
			  return isNaN(date) ? null : date;
			}

			var parseIso = +new Date("2000-01-01T00:00:00.000Z")
				? parseIsoNative
				: exports.utcParse(isoSpecifier);

			var array$2 = Array.prototype;

			var map$3 = array$2.map;
			var slice$3 = array$2.slice;

			var implicit = {name: "implicit"};

			function ordinal(range) {
			  var index = map$1(),
				  domain = [],
				  unknown = implicit;

			  range = range == null ? [] : slice$3.call(range);

			  function scale(d) {
				var key = d + "", i = index.get(key);
				if (!i) {
				  if (unknown !== implicit) return unknown;
				  index.set(key, i = domain.push(d));
				}
				return range[(i - 1) % range.length];
			  }

			  scale.domain = function(_) {
				if (!arguments.length) return domain.slice();
				domain = [], index = map$1();
				var i = -1, n = _.length, d, key;
				while (++i < n) if (!index.has(key = (d = _[i]) + "")) index.set(key, domain.push(d));
				return scale;
			  };

			  scale.range = function(_) {
				return arguments.length ? (range = slice$3.call(_), scale) : range.slice();
			  };

			  scale.unknown = function(_) {
				return arguments.length ? (unknown = _, scale) : unknown;
			  };

			  scale.copy = function() {
				return ordinal()
					.domain(domain)
					.range(range)
					.unknown(unknown);
			  };

			  return scale;
			}

			function band() {
			  var scale = ordinal().unknown(undefined),
				  domain = scale.domain,
				  ordinalRange = scale.range,
				  range$$1 = [0, 1],
				  step,
				  bandwidth,
				  round = false,
				  paddingInner = 0,
				  paddingOuter = 0,
				  align = 0.5;

			  delete scale.unknown;

			  function rescale() {
				var n = domain().length,
					reverse = range$$1[1] < range$$1[0],
					start = range$$1[reverse - 0],
					stop = range$$1[1 - reverse];
				step = (stop - start) / Math.max(1, n - paddingInner + paddingOuter * 2);
				if (round) step = Math.floor(step);
				start += (stop - start - step * (n - paddingInner)) * align;
				bandwidth = step * (1 - paddingInner);
				if (round) start = Math.round(start), bandwidth = Math.round(bandwidth);
				var values = range(n).map(function(i) { return start + step * i; });
				return ordinalRange(reverse ? values.reverse() : values);
			  }

			  scale.domain = function(_) {
				return arguments.length ? (domain(_), rescale()) : domain();
			  };

			  scale.range = function(_) {
				return arguments.length ? (range$$1 = [+_[0], +_[1]], rescale()) : range$$1.slice();
			  };

			  scale.rangeRound = function(_) {
				return range$$1 = [+_[0], +_[1]], round = true, rescale();
			  };

			  scale.bandwidth = function() {
				return bandwidth;
			  };

			  scale.step = function() {
				return step;
			  };

			  scale.round = function(_) {
				return arguments.length ? (round = !!_, rescale()) : round;
			  };

			  scale.padding = function(_) {
				return arguments.length ? (paddingInner = paddingOuter = Math.max(0, Math.min(1, _)), rescale()) : paddingInner;
			  };

			  scale.paddingInner = function(_) {
				return arguments.length ? (paddingInner = Math.max(0, Math.min(1, _)), rescale()) : paddingInner;
			  };

			  scale.paddingOuter = function(_) {
				return arguments.length ? (paddingOuter = Math.max(0, Math.min(1, _)), rescale()) : paddingOuter;
			  };

			  scale.align = function(_) {
				return arguments.length ? (align = Math.max(0, Math.min(1, _)), rescale()) : align;
			  };

			  scale.copy = function() {
				return band()
					.domain(domain())
					.range(range$$1)
					.round(round)
					.paddingInner(paddingInner)
					.paddingOuter(paddingOuter)
					.align(align);
			  };

			  return rescale();
			}

			function pointish(scale) {
			  var copy = scale.copy;

			  scale.padding = scale.paddingOuter;
			  delete scale.paddingInner;
			  delete scale.paddingOuter;

			  scale.copy = function() {
				return pointish(copy());
			  };

			  return scale;
			}

			function point$4() {
			  return pointish(band().paddingInner(1));
			}

			var constant$4 = function(x) {
			  return function() {
				return x;
			  };
			};

			var number$1 = function(x) {
			  return +x;
			};

			var unit = [0, 1];

			function deinterpolateLinear(a, b) {
			  return (b -= (a = +a))
				  ? function(x) { return (x - a) / b; }
				  : constant$4(b);
			}

			function deinterpolateClamp(deinterpolate) {
			  return function(a, b) {
				var d = deinterpolate(a = +a, b = +b);
				return function(x) { return x <= a ? 0 : x >= b ? 1 : d(x); };
			  };
			}

			function reinterpolateClamp(reinterpolate) {
			  return function(a, b) {
				var r = reinterpolate(a = +a, b = +b);
				return function(t) { return t <= 0 ? a : t >= 1 ? b : r(t); };
			  };
			}

			function bimap(domain, range$$1, deinterpolate, reinterpolate) {
			  var d0 = domain[0], d1 = domain[1], r0 = range$$1[0], r1 = range$$1[1];
			  if (d1 < d0) d0 = deinterpolate(d1, d0), r0 = reinterpolate(r1, r0);
			  else d0 = deinterpolate(d0, d1), r0 = reinterpolate(r0, r1);
			  return function(x) { return r0(d0(x)); };
			}

			function polymap(domain, range$$1, deinterpolate, reinterpolate) {
			  var j = Math.min(domain.length, range$$1.length) - 1,
				  d = new Array(j),
				  r = new Array(j),
				  i = -1;

			  // Reverse descending domains.
			  if (domain[j] < domain[0]) {
				domain = domain.slice().reverse();
				range$$1 = range$$1.slice().reverse();
			  }

			  while (++i < j) {
				d[i] = deinterpolate(domain[i], domain[i + 1]);
				r[i] = reinterpolate(range$$1[i], range$$1[i + 1]);
			  }

			  return function(x) {
				var i = bisectRight(domain, x, 1, j) - 1;
				return r[i](d[i](x));
			  };
			}

			function copy(source, target) {
			  return target
				  .domain(source.domain())
				  .range(source.range())
				  .interpolate(source.interpolate())
				  .clamp(source.clamp());
			}

			// deinterpolate(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].
			// reinterpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding domain value x in [a,b].
			function continuous(deinterpolate, reinterpolate) {
			  var domain = unit,
				  range$$1 = unit,
				  interpolate$$1 = interpolate,
				  clamp = false,
				  piecewise,
				  output,
				  input;

			  function rescale() {
				piecewise = Math.min(domain.length, range$$1.length) > 2 ? polymap : bimap;
				output = input = null;
				return scale;
			  }

			  function scale(x) {
				return (output || (output = piecewise(domain, range$$1, clamp ? deinterpolateClamp(deinterpolate) : deinterpolate, interpolate$$1)))(+x);
			  }

			  scale.invert = function(y) {
				return (input || (input = piecewise(range$$1, domain, deinterpolateLinear, clamp ? reinterpolateClamp(reinterpolate) : reinterpolate)))(+y);
			  };

			  scale.domain = function(_) {
				return arguments.length ? (domain = map$3.call(_, number$1), rescale()) : domain.slice();
			  };

			  scale.range = function(_) {
				return arguments.length ? (range$$1 = slice$3.call(_), rescale()) : range$$1.slice();
			  };

			  scale.rangeRound = function(_) {
				return range$$1 = slice$3.call(_), interpolate$$1 = interpolateRound, rescale();
			  };

			  scale.clamp = function(_) {
				return arguments.length ? (clamp = !!_, rescale()) : clamp;
			  };

			  scale.interpolate = function(_) {
				return arguments.length ? (interpolate$$1 = _, rescale()) : interpolate$$1;
			  };

			  return rescale();
			}

			var tickFormat = function(domain, count, specifier) {
			  var start = domain[0],
				  stop = domain[domain.length - 1],
				  step = tickStep(start, stop, count == null ? 10 : count),
				  precision;
			  specifier = formatSpecifier(specifier == null ? ",f" : specifier);
			  switch (specifier.type) {
				case "s": {
				  var value = Math.max(Math.abs(start), Math.abs(stop));
				  if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision;
				  return exports.formatPrefix(specifier, value);
				}
				case "":
				case "e":
				case "g":
				case "p":
				case "r": {
				  if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e");
				  break;
				}
				case "f":
				case "%": {
				  if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2;
				  break;
				}
			  }
			  return exports.format(specifier);
			};

			function linearish(scale) {
			  var domain = scale.domain;

			  scale.ticks = function(count) {
				var d = domain();
				return ticks(d[0], d[d.length - 1], count == null ? 10 : count);
			  };

			  scale.tickFormat = function(count, specifier) {
				return tickFormat(domain(), count, specifier);
			  };

			  scale.nice = function(count) {
				var d = domain(),
					i = d.length - 1,
					n = count == null ? 10 : count,
					start = d[0],
					stop = d[i],
					step = tickStep(start, stop, n);

				if (step) {
				  step = tickStep(Math.floor(start / step) * step, Math.ceil(stop / step) * step, n);
				  d[0] = Math.floor(start / step) * step;
				  d[i] = Math.ceil(stop / step) * step;
				  domain(d);
				}

				return scale;
			  };

			  return scale;
			}

			function linear$2() {
			  var scale = continuous(deinterpolateLinear, interpolateNumber);

			  scale.copy = function() {
				return copy(scale, linear$2());
			  };

			  return linearish(scale);
			}

			function identity$4() {
			  var domain = [0, 1];

			  function scale(x) {
				return +x;
			  }

			  scale.invert = scale;

			  scale.domain = scale.range = function(_) {
				return arguments.length ? (domain = map$3.call(_, number$1), scale) : domain.slice();
			  };

			  scale.copy = function() {
				return identity$4().domain(domain);
			  };

			  return linearish(scale);
			}

			var nice = function(domain, interval) {
			  domain = domain.slice();

			  var i0 = 0,
				  i1 = domain.length - 1,
				  x0 = domain[i0],
				  x1 = domain[i1],
				  t;

			  if (x1 < x0) {
				t = i0, i0 = i1, i1 = t;
				t = x0, x0 = x1, x1 = t;
			  }

			  domain[i0] = interval.floor(x0);
			  domain[i1] = interval.ceil(x1);
			  return domain;
			};

			function deinterpolate(a, b) {
			  return (b = Math.log(b / a))
				  ? function(x) { return Math.log(x / a) / b; }
				  : constant$4(b);
			}

			function reinterpolate(a, b) {
			  return a < 0
				  ? function(t) { return -Math.pow(-b, t) * Math.pow(-a, 1 - t); }
				  : function(t) { return Math.pow(b, t) * Math.pow(a, 1 - t); };
			}

			function pow10(x) {
			  return isFinite(x) ? +("1e" + x) : x < 0 ? 0 : x;
			}

			function powp(base) {
			  return base === 10 ? pow10
				  : base === Math.E ? Math.exp
				  : function(x) { return Math.pow(base, x); };
			}

			function logp(base) {
			  return base === Math.E ? Math.log
				  : base === 10 && Math.log10
				  || base === 2 && Math.log2
				  || (base = Math.log(base), function(x) { return Math.log(x) / base; });
			}

			function reflect(f) {
			  return function(x) {
				return -f(-x);
			  };
			}

			function log() {
			  var scale = continuous(deinterpolate, reinterpolate).domain([1, 10]),
				  domain = scale.domain,
				  base = 10,
				  logs = logp(10),
				  pows = powp(10);

			  function rescale() {
				logs = logp(base), pows = powp(base);
				if (domain()[0] < 0) logs = reflect(logs), pows = reflect(pows);
				return scale;
			  }

			  scale.base = function(_) {
				return arguments.length ? (base = +_, rescale()) : base;
			  };

			  scale.domain = function(_) {
				return arguments.length ? (domain(_), rescale()) : domain();
			  };

			  scale.ticks = function(count) {
				var d = domain(),
					u = d[0],
					v = d[d.length - 1],
					r;

				if (r = v < u) i = u, u = v, v = i;

				var i = logs(u),
					j = logs(v),
					p,
					k,
					t,
					n = count == null ? 10 : +count,
					z = [];

				if (!(base % 1) && j - i < n) {
				  i = Math.round(i) - 1, j = Math.round(j) + 1;
				  if (u > 0) for (; i < j; ++i) {
					for (k = 1, p = pows(i); k < base; ++k) {
					  t = p * k;
					  if (t < u) continue;
					  if (t > v) break;
					  z.push(t);
					}
				  } else for (; i < j; ++i) {
					for (k = base - 1, p = pows(i); k >= 1; --k) {
					  t = p * k;
					  if (t < u) continue;
					  if (t > v) break;
					  z.push(t);
					}
				  }
				} else {
				  z = ticks(i, j, Math.min(j - i, n)).map(pows);
				}

				return r ? z.reverse() : z;
			  };

			  scale.tickFormat = function(count, specifier) {
				if (specifier == null) specifier = base === 10 ? ".0e" : ",";
				if (typeof specifier !== "function") specifier = exports.format(specifier);
				if (count === Infinity) return specifier;
				if (count == null) count = 10;
				var k = Math.max(1, base * count / scale.ticks().length); // TODO fast estimate?
				return function(d) {
				  var i = d / pows(Math.round(logs(d)));
				  if (i * base < base - 0.5) i *= base;
				  return i <= k ? specifier(d) : "";
				};
			  };

			  scale.nice = function() {
				return domain(nice(domain(), {
				  floor: function(x) { return pows(Math.floor(logs(x))); },
				  ceil: function(x) { return pows(Math.ceil(logs(x))); }
				}));
			  };

			  scale.copy = function() {
				return copy(scale, log().base(base));
			  };

			  return scale;
			}

			function raise(x, exponent) {
			  return x < 0 ? -Math.pow(-x, exponent) : Math.pow(x, exponent);
			}

			function pow() {
			  var exponent = 1,
				  scale = continuous(deinterpolate, reinterpolate),
				  domain = scale.domain;

			  function deinterpolate(a, b) {
				return (b = raise(b, exponent) - (a = raise(a, exponent)))
					? function(x) { return (raise(x, exponent) - a) / b; }
					: constant$4(b);
			  }

			  function reinterpolate(a, b) {
				b = raise(b, exponent) - (a = raise(a, exponent));
				return function(t) { return raise(a + b * t, 1 / exponent); };
			  }

			  scale.exponent = function(_) {
				return arguments.length ? (exponent = +_, domain(domain())) : exponent;
			  };

			  scale.copy = function() {
				return copy(scale, pow().exponent(exponent));
			  };

			  return linearish(scale);
			}

			function sqrt() {
			  return pow().exponent(0.5);
			}

			function quantile$$1() {
			  var domain = [],
				  range$$1 = [],
				  thresholds = [];

			  function rescale() {
				var i = 0, n = Math.max(1, range$$1.length);
				thresholds = new Array(n - 1);
				while (++i < n) thresholds[i - 1] = threshold(domain, i / n);
				return scale;
			  }

			  function scale(x) {
				if (!isNaN(x = +x)) return range$$1[bisectRight(thresholds, x)];
			  }

			  scale.invertExtent = function(y) {
				var i = range$$1.indexOf(y);
				return i < 0 ? [NaN, NaN] : [
				  i > 0 ? thresholds[i - 1] : domain[0],
				  i < thresholds.length ? thresholds[i] : domain[domain.length - 1]
				];
			  };

			  scale.domain = function(_) {
				if (!arguments.length) return domain.slice();
				domain = [];
				for (var i = 0, n = _.length, d; i < n; ++i) if (d = _[i], d != null && !isNaN(d = +d)) domain.push(d);
				domain.sort(ascending);
				return rescale();
			  };

			  scale.range = function(_) {
				return arguments.length ? (range$$1 = slice$3.call(_), rescale()) : range$$1.slice();
			  };

			  scale.quantiles = function() {
				return thresholds.slice();
			  };

			  scale.copy = function() {
				return quantile$$1()
					.domain(domain)
					.range(range$$1);
			  };

			  return scale;
			}

			function quantize$1() {
			  var x0 = 0,
				  x1 = 1,
				  n = 1,
				  domain = [0.5],
				  range$$1 = [0, 1];

			  function scale(x) {
				if (x <= x) return range$$1[bisectRight(domain, x, 0, n)];
			  }

			  function rescale() {
				var i = -1;
				domain = new Array(n);
				while (++i < n) domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1);
				return scale;
			  }

			  scale.domain = function(_) {
				return arguments.length ? (x0 = +_[0], x1 = +_[1], rescale()) : [x0, x1];
			  };

			  scale.range = function(_) {
				return arguments.length ? (n = (range$$1 = slice$3.call(_)).length - 1, rescale()) : range$$1.slice();
			  };

			  scale.invertExtent = function(y) {
				var i = range$$1.indexOf(y);
				return i < 0 ? [NaN, NaN]
					: i < 1 ? [x0, domain[0]]
					: i >= n ? [domain[n - 1], x1]
					: [domain[i - 1], domain[i]];
			  };

			  scale.copy = function() {
				return quantize$1()
					.domain([x0, x1])
					.range(range$$1);
			  };

			  return linearish(scale);
			}

			function threshold$1() {
			  var domain = [0.5],
				  range$$1 = [0, 1],
				  n = 1;

			  function scale(x) {
				if (x <= x) return range$$1[bisectRight(domain, x, 0, n)];
			  }

			  scale.domain = function(_) {
				return arguments.length ? (domain = slice$3.call(_), n = Math.min(domain.length, range$$1.length - 1), scale) : domain.slice();
			  };

			  scale.range = function(_) {
				return arguments.length ? (range$$1 = slice$3.call(_), n = Math.min(domain.length, range$$1.length - 1), scale) : range$$1.slice();
			  };

			  scale.invertExtent = function(y) {
				var i = range$$1.indexOf(y);
				return [domain[i - 1], domain[i]];
			  };

			  scale.copy = function() {
				return threshold$1()
					.domain(domain)
					.range(range$$1);
			  };

			  return scale;
			}

			var durationSecond$1 = 1000;
			var durationMinute$1 = durationSecond$1 * 60;
			var durationHour$1 = durationMinute$1 * 60;
			var durationDay$1 = durationHour$1 * 24;
			var durationWeek$1 = durationDay$1 * 7;
			var durationMonth = durationDay$1 * 30;
			var durationYear = durationDay$1 * 365;

			function date$1(t) {
			  return new Date(t);
			}

			function number$2(t) {
			  return t instanceof Date ? +t : +new Date(+t);
			}

			function calendar(year$$1, month$$1, week, day$$1, hour$$1, minute$$1, second$$1, millisecond$$1, format) {
			  var scale = continuous(deinterpolateLinear, interpolateNumber),
				  invert = scale.invert,
				  domain = scale.domain;

			  var formatMillisecond = format(".%L"),
				  formatSecond = format(":%S"),
				  formatMinute = format("%I:%M"),
				  formatHour = format("%I %p"),
				  formatDay = format("%a %d"),
				  formatWeek = format("%b %d"),
				  formatMonth = format("%B"),
				  formatYear = format("%Y");

			  var tickIntervals = [
				[second$$1,  1,      durationSecond$1],
				[second$$1,  5,  5 * durationSecond$1],
				[second$$1, 15, 15 * durationSecond$1],
				[second$$1, 30, 30 * durationSecond$1],
				[minute$$1,  1,      durationMinute$1],
				[minute$$1,  5,  5 * durationMinute$1],
				[minute$$1, 15, 15 * durationMinute$1],
				[minute$$1, 30, 30 * durationMinute$1],
				[  hour$$1,  1,      durationHour$1  ],
				[  hour$$1,  3,  3 * durationHour$1  ],
				[  hour$$1,  6,  6 * durationHour$1  ],
				[  hour$$1, 12, 12 * durationHour$1  ],
				[   day$$1,  1,      durationDay$1   ],
				[   day$$1,  2,  2 * durationDay$1   ],
				[  week,  1,      durationWeek$1  ],
				[ month$$1,  1,      durationMonth ],
				[ month$$1,  3,  3 * durationMonth ],
				[  year$$1,  1,      durationYear  ]
			  ];

			  function tickFormat(date) {
				return (second$$1(date) < date ? formatMillisecond
					: minute$$1(date) < date ? formatSecond
					: hour$$1(date) < date ? formatMinute
					: day$$1(date) < date ? formatHour
					: month$$1(date) < date ? (week(date) < date ? formatDay : formatWeek)
					: year$$1(date) < date ? formatMonth
					: formatYear)(date);
			  }

			  function tickInterval(interval, start, stop, step) {
				if (interval == null) interval = 10;

				// If a desired tick count is specified, pick a reasonable tick interval
				// based on the extent of the domain and a rough estimate of tick size.
				// Otherwise, assume interval is already a time interval and use it.
				if (typeof interval === "number") {
				  var target = Math.abs(stop - start) / interval,
					  i = bisector(function(i) { return i[2]; }).right(tickIntervals, target);
				  if (i === tickIntervals.length) {
					step = tickStep(start / durationYear, stop / durationYear, interval);
					interval = year$$1;
				  } else if (i) {
					i = tickIntervals[target / tickIntervals[i - 1][2] < tickIntervals[i][2] / target ? i - 1 : i];
					step = i[1];
					interval = i[0];
				  } else {
					step = tickStep(start, stop, interval);
					interval = millisecond$$1;
				  }
				}

				return step == null ? interval : interval.every(step);
			  }

			  scale.invert = function(y) {
				return new Date(invert(y));
			  };

			  scale.domain = function(_) {
				return arguments.length ? domain(map$3.call(_, number$2)) : domain().map(date$1);
			  };

			  scale.ticks = function(interval, step) {
				var d = domain(),
					t0 = d[0],
					t1 = d[d.length - 1],
					r = t1 < t0,
					t;
				if (r) t = t0, t0 = t1, t1 = t;
				t = tickInterval(interval, t0, t1, step);
				t = t ? t.range(t0, t1 + 1) : []; // inclusive stop
				return r ? t.reverse() : t;
			  };

			  scale.tickFormat = function(count, specifier) {
				return specifier == null ? tickFormat : format(specifier);
			  };

			  scale.nice = function(interval, step) {
				var d = domain();
				return (interval = tickInterval(interval, d[0], d[d.length - 1], step))
					? domain(nice(d, interval))
					: scale;
			  };

			  scale.copy = function() {
				return copy(scale, calendar(year$$1, month$$1, week, day$$1, hour$$1, minute$$1, second$$1, millisecond$$1, format));
			  };

			  return scale;
			}

			var time = function() {
			  return calendar(year, month, sunday, day, hour, minute, second, millisecond, exports.timeFormat).domain([new Date(2000, 0, 1), new Date(2000, 0, 2)]);
			};

			var utcTime = function() {
			  return calendar(utcYear, utcMonth, utcSunday, utcDay, utcHour, utcMinute, second, millisecond, exports.utcFormat).domain([Date.UTC(2000, 0, 1), Date.UTC(2000, 0, 2)]);
			};

			var colors = function(s) {
			  return s.match(/.{6}/g).map(function(x) {
				return "#" + x;
			  });
			};

			var category10 = colors("1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf");

			var category20b = colors("393b795254a36b6ecf9c9ede6379398ca252b5cf6bcedb9c8c6d31bd9e39e7ba52e7cb94843c39ad494ad6616be7969c7b4173a55194ce6dbdde9ed6");

			var category20c = colors("3182bd6baed69ecae1c6dbefe6550dfd8d3cfdae6bfdd0a231a35474c476a1d99bc7e9c0756bb19e9ac8bcbddcdadaeb636363969696bdbdbdd9d9d9");

			var category20 = colors("1f77b4aec7e8ff7f0effbb782ca02c98df8ad62728ff98969467bdc5b0d58c564bc49c94e377c2f7b6d27f7f7fc7c7c7bcbd22dbdb8d17becf9edae5");

			var cubehelix$3 = cubehelixLong(cubehelix(300, 0.5, 0.0), cubehelix(-240, 0.5, 1.0));

			var warm = cubehelixLong(cubehelix(-100, 0.75, 0.35), cubehelix(80, 1.50, 0.8));

			var cool = cubehelixLong(cubehelix(260, 0.75, 0.35), cubehelix(80, 1.50, 0.8));

			var rainbow = cubehelix();

			var rainbow$1 = function(t) {
			  if (t < 0 || t > 1) t -= Math.floor(t);
			  var ts = Math.abs(t - 0.5);
			  rainbow.h = 360 * t - 100;
			  rainbow.s = 1.5 - 1.5 * ts;
			  rainbow.l = 0.8 - 0.9 * ts;
			  return rainbow + "";
			};

			function ramp(range) {
			  var n = range.length;
			  return function(t) {
				return range[Math.max(0, Math.min(n - 1, Math.floor(t * n)))];
			  };
			}

			var viridis = ramp(colors("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725"));

			var magma = ramp(colors("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf"));

			var inferno = ramp(colors("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4"));

			var plasma = ramp(colors("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921"));

			function sequential(interpolator) {
			  var x0 = 0,
				  x1 = 1,
				  clamp = false;

			  function scale(x) {
				var t = (x - x0) / (x1 - x0);
				return interpolator(clamp ? Math.max(0, Math.min(1, t)) : t);
			  }

			  scale.domain = function(_) {
				return arguments.length ? (x0 = +_[0], x1 = +_[1], scale) : [x0, x1];
			  };

			  scale.clamp = function(_) {
				return arguments.length ? (clamp = !!_, scale) : clamp;
			  };

			  scale.interpolator = function(_) {
				return arguments.length ? (interpolator = _, scale) : interpolator;
			  };

			  scale.copy = function() {
				return sequential(interpolator).domain([x0, x1]).clamp(clamp);
			  };

			  return linearish(scale);
			}

			var xhtml = "http://www.w3.org/1999/xhtml";

			var namespaces = {
			  svg: "http://www.w3.org/2000/svg",
			  xhtml: xhtml,
			  xlink: "http://www.w3.org/1999/xlink",
			  xml: "http://www.w3.org/XML/1998/namespace",
			  xmlns: "http://www.w3.org/2000/xmlns/"
			};

			var namespace = function(name) {
			  var prefix = name += "", i = prefix.indexOf(":");
			  if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1);
			  return namespaces.hasOwnProperty(prefix) ? {space: namespaces[prefix], local: name} : name;
			};

			function creatorInherit(name) {
			  return function() {
				var document = this.ownerDocument,
					uri = this.namespaceURI;
				return uri === xhtml && document.documentElement.namespaceURI === xhtml
					? document.createElement(name)
					: document.createElementNS(uri, name);
			  };
			}

			function creatorFixed(fullname) {
			  return function() {
				return this.ownerDocument.createElementNS(fullname.space, fullname.local);
			  };
			}

			var creator = function(name) {
			  var fullname = namespace(name);
			  return (fullname.local
				  ? creatorFixed
				  : creatorInherit)(fullname);
			};

			var nextId = 0;

			function local() {
			  return new Local;
			}

			function Local() {
			  this._ = "@" + (++nextId).toString(36);
			}

			Local.prototype = local.prototype = {
			  constructor: Local,
			  get: function(node) {
				var id = this._;
				while (!(id in node)) if (!(node = node.parentNode)) return;
				return node[id];
			  },
			  set: function(node, value) {
				return node[this._] = value;
			  },
			  remove: function(node) {
				return this._ in node && delete node[this._];
			  },
			  toString: function() {
				return this._;
			  }
			};

			var matcher = function(selector) {
			  return function() {
				return this.matches(selector);
			  };
			};

			if (typeof document !== "undefined") {
			  var element = document.documentElement;
			  if (!element.matches) {
				var vendorMatches = element.webkitMatchesSelector
					|| element.msMatchesSelector
					|| element.mozMatchesSelector
					|| element.oMatchesSelector;
				matcher = function(selector) {
				  return function() {
					return vendorMatches.call(this, selector);
				  };
				};
			  }
			}

			var matcher$1 = matcher;

			var filterEvents = {};

			exports.event = null;

			if (typeof document !== "undefined") {
			  var element$1 = document.documentElement;
			  if (!("onmouseenter" in element$1)) {
				filterEvents = {mouseenter: "mouseover", mouseleave: "mouseout"};
			  }
			}

			function filterContextListener(listener, index, group) {
			  listener = contextListener(listener, index, group);
			  return function(event) {
				var related = event.relatedTarget;
				if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) {
				  listener.call(this, event);
				}
			  };
			}

			function contextListener(listener, index, group) {
			  return function(event1) {
				var event0 = exports.event; // Events can be reentrant (e.g., focus).
				exports.event = event1;
				try {
				  listener.call(this, this.__data__, index, group);
				} finally {
				  exports.event = event0;
				}
			  };
			}

			function parseTypenames$1(typenames) {
			  return typenames.trim().split(/^|\s+/).map(function(t) {
				var name = "", i = t.indexOf(".");
				if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
				return {type: t, name: name};
			  });
			}

			function onRemove(typename) {
			  return function() {
				var on = this.__on;
				if (!on) return;
				for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {
				  if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {
					this.removeEventListener(o.type, o.listener, o.capture);
				  } else {
					on[++i] = o;
				  }
				}
				if (++i) on.length = i;
				else delete this.__on;
			  };
			}

			function onAdd(typename, value, capture) {
			  var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener;
			  return function(d, i, group) {
				var on = this.__on, o, listener = wrap(value, i, group);
				if (on) for (var j = 0, m = on.length; j < m; ++j) {
				  if ((o = on[j]).type === typename.type && o.name === typename.name) {
					this.removeEventListener(o.type, o.listener, o.capture);
					this.addEventListener(o.type, o.listener = listener, o.capture = capture);
					o.value = value;
					return;
				  }
				}
				this.addEventListener(typename.type, listener, capture);
				o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture};
				if (!on) this.__on = [o];
				else on.push(o);
			  };
			}

			var selection_on = function(typename, value, capture) {
			  var typenames = parseTypenames$1(typename + ""), i, n = typenames.length, t;

			  if (arguments.length < 2) {
				var on = this.node().__on;
				if (on) for (var j = 0, m = on.length, o; j < m; ++j) {
				  for (i = 0, o = on[j]; i < n; ++i) {
					if ((t = typenames[i]).type === o.type && t.name === o.name) {
					  return o.value;
					}
				  }
				}
				return;
			  }

			  on = value ? onAdd : onRemove;
			  if (capture == null) capture = false;
			  for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture));
			  return this;
			};

			function customEvent(event1, listener, that, args) {
			  var event0 = exports.event;
			  event1.sourceEvent = exports.event;
			  exports.event = event1;
			  try {
				return listener.apply(that, args);
			  } finally {
				exports.event = event0;
			  }
			}

			var sourceEvent = function() {
			  var current = exports.event, source;
			  while (source = current.sourceEvent) current = source;
			  return current;
			};

			var point$5 = function(node, event) {
			  var svg = node.ownerSVGElement || node;

			  if (svg.createSVGPoint) {
				var point = svg.createSVGPoint();
				point.x = event.clientX, point.y = event.clientY;
				point = point.matrixTransform(node.getScreenCTM().inverse());
				return [point.x, point.y];
			  }

			  var rect = node.getBoundingClientRect();
			  return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];
			};

			var mouse = function(node) {
			  var event = sourceEvent();
			  if (event.changedTouches) event = event.changedTouches[0];
			  return point$5(node, event);
			};

			function none$2() {}

			var selector = function(selector) {
			  return selector == null ? none$2 : function() {
				return this.querySelector(selector);
			  };
			};

			var selection_select = function(select) {
			  if (typeof select !== "function") select = selector(select);

			  for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
				for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
				  if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
					if ("__data__" in node) subnode.__data__ = node.__data__;
					subgroup[i] = subnode;
				  }
				}
			  }

			  return new Selection(subgroups, this._parents);
			};

			function empty() {
			  return [];
			}

			var selectorAll = function(selector) {
			  return selector == null ? empty : function() {
				return this.querySelectorAll(selector);
			  };
			};

			var selection_selectAll = function(select) {
			  if (typeof select !== "function") select = selectorAll(select);

			  for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
				for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
				  if (node = group[i]) {
					subgroups.push(select.call(node, node.__data__, i, group));
					parents.push(node);
				  }
				}
			  }

			  return new Selection(subgroups, parents);
			};

			var selection_filter = function(match) {
			  if (typeof match !== "function") match = matcher$1(match);

			  for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
				for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
				  if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
					subgroup.push(node);
				  }
				}
			  }

			  return new Selection(subgroups, this._parents);
			};

			var sparse = function(update) {
			  return new Array(update.length);
			};

			var selection_enter = function() {
			  return new Selection(this._enter || this._groups.map(sparse), this._parents);
			};

			function EnterNode(parent, datum) {
			  this.ownerDocument = parent.ownerDocument;
			  this.namespaceURI = parent.namespaceURI;
			  this._next = null;
			  this._parent = parent;
			  this.__data__ = datum;
			}

			EnterNode.prototype = {
			  constructor: EnterNode,
			  appendChild: function(child) { return this._parent.insertBefore(child, this._next); },
			  insertBefore: function(child, next) { return this._parent.insertBefore(child, next); },
			  querySelector: function(selector) { return this._parent.querySelector(selector); },
			  querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); }
			};

			var constant$5 = function(x) {
			  return function() {
				return x;
			  };
			};

			var keyPrefix = "$"; // Protect against keys like “__proto__”.

			function bindIndex(parent, group, enter, update, exit, data) {
			  var i = 0,
				  node,
				  groupLength = group.length,
				  dataLength = data.length;

			  // Put any non-null nodes that fit into update.
			  // Put any null nodes into enter.
			  // Put any remaining data into enter.
			  for (; i < dataLength; ++i) {
				if (node = group[i]) {
				  node.__data__ = data[i];
				  update[i] = node;
				} else {
				  enter[i] = new EnterNode(parent, data[i]);
				}
			  }

			  // Put any non-null nodes that don’t fit into exit.
			  for (; i < groupLength; ++i) {
				if (node = group[i]) {
				  exit[i] = node;
				}
			  }
			}

			function bindKey(parent, group, enter, update, exit, data, key) {
			  var i,
				  node,
				  nodeByKeyValue = {},
				  groupLength = group.length,
				  dataLength = data.length,
				  keyValues = new Array(groupLength),
				  keyValue;

			  // Compute the key for each node.
			  // If multiple nodes have the same key, the duplicates are added to exit.
			  for (i = 0; i < groupLength; ++i) {
				if (node = group[i]) {
				  keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group);
				  if (keyValue in nodeByKeyValue) {
					exit[i] = node;
				  } else {
					nodeByKeyValue[keyValue] = node;
				  }
				}
			  }

			  // Compute the key for each datum.
			  // If there a node associated with this key, join and add it to update.
			  // If there is not (or the key is a duplicate), add it to enter.
			  for (i = 0; i < dataLength; ++i) {
				keyValue = keyPrefix + key.call(parent, data[i], i, data);
				if (node = nodeByKeyValue[keyValue]) {
				  update[i] = node;
				  node.__data__ = data[i];
				  nodeByKeyValue[keyValue] = null;
				} else {
				  enter[i] = new EnterNode(parent, data[i]);
				}
			  }

			  // Add any remaining nodes that were not bound to data to exit.
			  for (i = 0; i < groupLength; ++i) {
				if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) {
				  exit[i] = node;
				}
			  }
			}

			var selection_data = function(value, key) {
			  if (!value) {
				data = new Array(this.size()), j = -1;
				this.each(function(d) { data[++j] = d; });
				return data;
			  }

			  var bind = key ? bindKey : bindIndex,
				  parents = this._parents,
				  groups = this._groups;

			  if (typeof value !== "function") value = constant$5(value);

			  for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {
				var parent = parents[j],
					group = groups[j],
					groupLength = group.length,
					data = value.call(parent, parent && parent.__data__, j, parents),
					dataLength = data.length,
					enterGroup = enter[j] = new Array(dataLength),
					updateGroup = update[j] = new Array(dataLength),
					exitGroup = exit[j] = new Array(groupLength);

				bind(parent, group, enterGroup, updateGroup, exitGroup, data, key);

				// Now connect the enter nodes to their following update node, such that
				// appendChild can insert the materialized enter node before this node,
				// rather than at the end of the parent node.
				for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {
				  if (previous = enterGroup[i0]) {
					if (i0 >= i1) i1 = i0 + 1;
					while (!(next = updateGroup[i1]) && ++i1 < dataLength);
					previous._next = next || null;
				  }
				}
			  }

			  update = new Selection(update, parents);
			  update._enter = enter;
			  update._exit = exit;
			  return update;
			};

			var selection_exit = function() {
			  return new Selection(this._exit || this._groups.map(sparse), this._parents);
			};

			var selection_merge = function(selection) {

			  for (var groups0 = this._groups, groups1 = selection._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {
				for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
				  if (node = group0[i] || group1[i]) {
					merge[i] = node;
				  }
				}
			  }

			  for (; j < m0; ++j) {
				merges[j] = groups0[j];
			  }

			  return new Selection(merges, this._parents);
			};

			var selection_order = function() {

			  for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {
				for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {
				  if (node = group[i]) {
					if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);
					next = node;
				  }
				}
			  }

			  return this;
			};

			var selection_sort = function(compare) {
			  if (!compare) compare = ascending$2;

			  function compareNode(a, b) {
				return a && b ? compare(a.__data__, b.__data__) : !a - !b;
			  }

			  for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {
				for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {
				  if (node = group[i]) {
					sortgroup[i] = node;
				  }
				}
				sortgroup.sort(compareNode);
			  }

			  return new Selection(sortgroups, this._parents).order();
			};

			function ascending$2(a, b) {
			  return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
			}

			var selection_call = function() {
			  var callback = arguments[0];
			  arguments[0] = this;
			  callback.apply(null, arguments);
			  return this;
			};

			var selection_nodes = function() {
			  var nodes = new Array(this.size()), i = -1;
			  this.each(function() { nodes[++i] = this; });
			  return nodes;
			};

			var selection_node = function() {

			  for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
				for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {
				  var node = group[i];
				  if (node) return node;
				}
			  }

			  return null;
			};

			var selection_size = function() {
			  var size = 0;
			  this.each(function() { ++size; });
			  return size;
			};

			var selection_empty = function() {
			  return !this.node();
			};

			var selection_each = function(callback) {

			  for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
				for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {
				  if (node = group[i]) callback.call(node, node.__data__, i, group);
				}
			  }

			  return this;
			};

			function attrRemove(name) {
			  return function() {
				this.removeAttribute(name);
			  };
			}

			function attrRemoveNS(fullname) {
			  return function() {
				this.removeAttributeNS(fullname.space, fullname.local);
			  };
			}

			function attrConstant(name, value) {
			  return function() {
				this.setAttribute(name, value);
			  };
			}

			function attrConstantNS(fullname, value) {
			  return function() {
				this.setAttributeNS(fullname.space, fullname.local, value);
			  };
			}

			function attrFunction(name, value) {
			  return function() {
				var v = value.apply(this, arguments);
				if (v == null) this.removeAttribute(name);
				else this.setAttribute(name, v);
			  };
			}

			function attrFunctionNS(fullname, value) {
			  return function() {
				var v = value.apply(this, arguments);
				if (v == null) this.removeAttributeNS(fullname.space, fullname.local);
				else this.setAttributeNS(fullname.space, fullname.local, v);
			  };
			}

			var selection_attr = function(name, value) {
			  var fullname = namespace(name);

			  if (arguments.length < 2) {
				var node = this.node();
				return fullname.local
					? node.getAttributeNS(fullname.space, fullname.local)
					: node.getAttribute(fullname);
			  }

			  return this.each((value == null
				  ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === "function"
				  ? (fullname.local ? attrFunctionNS : attrFunction)
				  : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value));
			};

			var window = function(node) {
			  return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node
				  || (node.document && node) // node is a Window
				  || node.defaultView; // node is a Document
			};

			function styleRemove(name) {
			  return function() {
				this.style.removeProperty(name);
			  };
			}

			function styleConstant(name, value, priority) {
			  return function() {
				this.style.setProperty(name, value, priority);
			  };
			}

			function styleFunction(name, value, priority) {
			  return function() {
				var v = value.apply(this, arguments);
				if (v == null) this.style.removeProperty(name);
				else this.style.setProperty(name, v, priority);
			  };
			}

			var selection_style = function(name, value, priority) {
			  var node;
			  return arguments.length > 1
				  ? this.each((value == null
						? styleRemove : typeof value === "function"
						? styleFunction
						: styleConstant)(name, value, priority == null ? "" : priority))
				  : window(node = this.node())
					  .getComputedStyle(node, null)
					  .getPropertyValue(name);
			};

			function propertyRemove(name) {
			  return function() {
				delete this[name];
			  };
			}

			function propertyConstant(name, value) {
			  return function() {
				this[name] = value;
			  };
			}

			function propertyFunction(name, value) {
			  return function() {
				var v = value.apply(this, arguments);
				if (v == null) delete this[name];
				else this[name] = v;
			  };
			}

			var selection_property = function(name, value) {
			  return arguments.length > 1
				  ? this.each((value == null
					  ? propertyRemove : typeof value === "function"
					  ? propertyFunction
					  : propertyConstant)(name, value))
				  : this.node()[name];
			};

			function classArray(string) {
			  return string.trim().split(/^|\s+/);
			}

			function classList(node) {
			  return node.classList || new ClassList(node);
			}

			function ClassList(node) {
			  this._node = node;
			  this._names = classArray(node.getAttribute("class") || "");
			}

			ClassList.prototype = {
			  add: function(name) {
				var i = this._names.indexOf(name);
				if (i < 0) {
				  this._names.push(name);
				  this._node.setAttribute("class", this._names.join(" "));
				}
			  },
			  remove: function(name) {
				var i = this._names.indexOf(name);
				if (i >= 0) {
				  this._names.splice(i, 1);
				  this._node.setAttribute("class", this._names.join(" "));
				}
			  },
			  contains: function(name) {
				return this._names.indexOf(name) >= 0;
			  }
			};

			function classedAdd(node, names) {
			  var list = classList(node), i = -1, n = names.length;
			  while (++i < n) list.add(names[i]);
			}

			function classedRemove(node, names) {
			  var list = classList(node), i = -1, n = names.length;
			  while (++i < n) list.remove(names[i]);
			}

			function classedTrue(names) {
			  return function() {
				classedAdd(this, names);
			  };
			}

			function classedFalse(names) {
			  return function() {
				classedRemove(this, names);
			  };
			}

			function classedFunction(names, value) {
			  return function() {
				(value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);
			  };
			}

			var selection_classed = function(name, value) {
			  var names = classArray(name + "");

			  if (arguments.length < 2) {
				var list = classList(this.node()), i = -1, n = names.length;
				while (++i < n) if (!list.contains(names[i])) return false;
				return true;
			  }

			  return this.each((typeof value === "function"
				  ? classedFunction : value
				  ? classedTrue
				  : classedFalse)(names, value));
			};

			function textRemove() {
			  this.textContent = "";
			}

			function textConstant(value) {
			  return function() {
				this.textContent = value;
			  };
			}

			function textFunction(value) {
			  return function() {
				var v = value.apply(this, arguments);
				this.textContent = v == null ? "" : v;
			  };
			}

			var selection_text = function(value) {
			  return arguments.length
				  ? this.each(value == null
					  ? textRemove : (typeof value === "function"
					  ? textFunction
					  : textConstant)(value))
				  : this.node().textContent;
			};

			function htmlRemove() {
			  this.innerHTML = "";
			}

			function htmlConstant(value) {
			  return function() {
				this.innerHTML = value;
			  };
			}

			function htmlFunction(value) {
			  return function() {
				var v = value.apply(this, arguments);
				this.innerHTML = v == null ? "" : v;
			  };
			}

			var selection_html = function(value) {
			  return arguments.length
				  ? this.each(value == null
					  ? htmlRemove : (typeof value === "function"
					  ? htmlFunction
					  : htmlConstant)(value))
				  : this.node().innerHTML;
			};

			function raise$1() {
			  if (this.nextSibling) this.parentNode.appendChild(this);
			}

			var selection_raise = function() {
			  return this.each(raise$1);
			};

			function lower() {
			  if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);
			}

			var selection_lower = function() {
			  return this.each(lower);
			};

			var selection_append = function(name) {
			  var create = typeof name === "function" ? name : creator(name);
			  return this.select(function() {
				return this.appendChild(create.apply(this, arguments));
			  });
			};

			function constantNull() {
			  return null;
			}

			var selection_insert = function(name, before) {
			  var create = typeof name === "function" ? name : creator(name),
				  select = before == null ? constantNull : typeof before === "function" ? before : selector(before);
			  return this.select(function() {
				return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);
			  });
			};

			function remove() {
			  var parent = this.parentNode;
			  if (parent) parent.removeChild(this);
			}

			var selection_remove = function() {
			  return this.each(remove);
			};

			var selection_datum = function(value) {
			  return arguments.length
				  ? this.property("__data__", value)
				  : this.node().__data__;
			};

			function dispatchEvent(node, type, params) {
			  var window$$1 = window(node),
				  event = window$$1.CustomEvent;

			  if (event) {
				event = new event(type, params);
			  } else {
				event = window$$1.document.createEvent("Event");
				if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;
				else event.initEvent(type, false, false);
			  }

			  node.dispatchEvent(event);
			}

			function dispatchConstant(type, params) {
			  return function() {
				return dispatchEvent(this, type, params);
			  };
			}

			function dispatchFunction(type, params) {
			  return function() {
				return dispatchEvent(this, type, params.apply(this, arguments));
			  };
			}

			var selection_dispatch = function(type, params) {
			  return this.each((typeof params === "function"
				  ? dispatchFunction
				  : dispatchConstant)(type, params));
			};

			var root = [null];

			function Selection(groups, parents) {
			  this._groups = groups;
			  this._parents = parents;
			}

			function selection() {
			  return new Selection([[document.documentElement]], root);
			}

			Selection.prototype = selection.prototype = {
			  constructor: Selection,
			  select: selection_select,
			  selectAll: selection_selectAll,
			  filter: selection_filter,
			  data: selection_data,
			  enter: selection_enter,
			  exit: selection_exit,
			  merge: selection_merge,
			  order: selection_order,
			  sort: selection_sort,
			  call: selection_call,
			  nodes: selection_nodes,
			  node: selection_node,
			  size: selection_size,
			  empty: selection_empty,
			  each: selection_each,
			  attr: selection_attr,
			  style: selection_style,
			  property: selection_property,
			  classed: selection_classed,
			  text: selection_text,
			  html: selection_html,
			  raise: selection_raise,
			  lower: selection_lower,
			  append: selection_append,
			  insert: selection_insert,
			  remove: selection_remove,
			  datum: selection_datum,
			  on: selection_on,
			  dispatch: selection_dispatch
			};

			var select = function(selector) {
			  return typeof selector === "string"
				  ? new Selection([[document.querySelector(selector)]], [document.documentElement])
				  : new Selection([[selector]], root);
			};

			var selectAll = function(selector) {
			  return typeof selector === "string"
				  ? new Selection([document.querySelectorAll(selector)], [document.documentElement])
				  : new Selection([selector == null ? [] : selector], root);
			};

			var touch = function(node, touches, identifier) {
			  if (arguments.length < 3) identifier = touches, touches = sourceEvent().changedTouches;

			  for (var i = 0, n = touches ? touches.length : 0, touch; i < n; ++i) {
				if ((touch = touches[i]).identifier === identifier) {
				  return point$5(node, touch);
				}
			  }

			  return null;
			};

			var touches = function(node, touches) {
			  if (touches == null) touches = sourceEvent().touches;

			  for (var i = 0, n = touches ? touches.length : 0, points = new Array(n); i < n; ++i) {
				points[i] = point$5(node, touches[i]);
			  }

			  return points;
			};

			var emptyOn = dispatch("start", "end", "interrupt");
			var emptyTween = [];

			var CREATED = 0;
			var SCHEDULED = 1;
			var STARTING = 2;
			var STARTED = 3;
			var RUNNING = 4;
			var ENDING = 5;
			var ENDED = 6;

			var schedule = function(node, name, id, index, group, timing) {
			  var schedules = node.__transition;
			  if (!schedules) node.__transition = {};
			  else if (id in schedules) return;
			  create(node, id, {
				name: name,
				index: index, // For context during callback.
				group: group, // For context during callback.
				on: emptyOn,
				tween: emptyTween,
				time: timing.time,
				delay: timing.delay,
				duration: timing.duration,
				ease: timing.ease,
				timer: null,
				state: CREATED
			  });
			};

			function init(node, id) {
			  var schedule = node.__transition;
			  if (!schedule || !(schedule = schedule[id]) || schedule.state > CREATED) throw new Error("too late");
			  return schedule;
			}

			function set$3(node, id) {
			  var schedule = node.__transition;
			  if (!schedule || !(schedule = schedule[id]) || schedule.state > STARTING) throw new Error("too late");
			  return schedule;
			}

			function get$1(node, id) {
			  var schedule = node.__transition;
			  if (!schedule || !(schedule = schedule[id])) throw new Error("too late");
			  return schedule;
			}

			function create(node, id, self) {
			  var schedules = node.__transition,
				  tween;

			  // Initialize the self timer when the transition is created.
			  // Note the actual delay is not known until the first callback!
			  schedules[id] = self;
			  self.timer = timer(schedule, 0, self.time);

			  function schedule(elapsed) {
				self.state = SCHEDULED;
				self.timer.restart(start, self.delay, self.time);

				// If the elapsed delay is less than our first sleep, start immediately.
				if (self.delay <= elapsed) start(elapsed - self.delay);
			  }

			  function start(elapsed) {
				var i, j, n, o;

				// If the state is not SCHEDULED, then we previously errored on start.
				if (self.state !== SCHEDULED) return stop();

				for (i in schedules) {
				  o = schedules[i];
				  if (o.name !== self.name) continue;

				  // While this element already has a starting transition during this frame,
				  // defer starting an interrupting transition until that transition has a
				  // chance to tick (and possibly end); see d3/d3-transition#54!
				  if (o.state === STARTED) return timeout$1(start);

				  // Interrupt the active transition, if any.
				  // Dispatch the interrupt event.
				  if (o.state === RUNNING) {
					o.state = ENDED;
					o.timer.stop();
					o.on.call("interrupt", node, node.__data__, o.index, o.group);
					delete schedules[i];
				  }

				  // Cancel any pre-empted transitions. No interrupt event is dispatched
				  // because the cancelled transitions never started. Note that this also
				  // removes this transition from the pending list!
				  else if (+i < id) {
					o.state = ENDED;
					o.timer.stop();
					delete schedules[i];
				  }
				}

				// Defer the first tick to end of the current frame; see d3/d3#1576.
				// Note the transition may be canceled after start and before the first tick!
				// Note this must be scheduled before the start event; see d3/d3-transition#16!
				// Assuming this is successful, subsequent callbacks go straight to tick.
				timeout$1(function() {
				  if (self.state === STARTED) {
					self.state = RUNNING;
					self.timer.restart(tick, self.delay, self.time);
					tick(elapsed);
				  }
				});

				// Dispatch the start event.
				// Note this must be done before the tween are initialized.
				self.state = STARTING;
				self.on.call("start", node, node.__data__, self.index, self.group);
				if (self.state !== STARTING) return; // interrupted
				self.state = STARTED;

				// Initialize the tween, deleting null tween.
				tween = new Array(n = self.tween.length);
				for (i = 0, j = -1; i < n; ++i) {
				  if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) {
					tween[++j] = o;
				  }
				}
				tween.length = j + 1;
			  }

			  function tick(elapsed) {
				var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1),
					i = -1,
					n = tween.length;

				while (++i < n) {
				  tween[i].call(null, t);
				}

				// Dispatch the end event.
				if (self.state === ENDING) {
				  self.on.call("end", node, node.__data__, self.index, self.group);
				  stop();
				}
			  }

			  function stop() {
				self.state = ENDED;
				self.timer.stop();
				delete schedules[id];
				for (var i in schedules) return; // eslint-disable-line no-unused-vars
				delete node.__transition;
			  }
			}

			var interrupt = function(node, name) {
			  var schedules = node.__transition,
				  schedule,
				  active,
				  empty = true,
				  i;

			  if (!schedules) return;

			  name = name == null ? null : name + "";

			  for (i in schedules) {
				if ((schedule = schedules[i]).name !== name) { empty = false; continue; }
				active = schedule.state > STARTING && schedule.state < ENDING;
				schedule.state = ENDED;
				schedule.timer.stop();
				if (active) schedule.on.call("interrupt", node, node.__data__, schedule.index, schedule.group);
				delete schedules[i];
			  }

			  if (empty) delete node.__transition;
			};

			var selection_interrupt = function(name) {
			  return this.each(function() {
				interrupt(this, name);
			  });
			};

			function tweenRemove(id, name) {
			  var tween0, tween1;
			  return function() {
				var schedule = set$3(this, id),
					tween = schedule.tween;

				// If this node shared tween with the previous node,
				// just assign the updated shared tween and we’re done!
				// Otherwise, copy-on-write.
				if (tween !== tween0) {
				  tween1 = tween0 = tween;
				  for (var i = 0, n = tween1.length; i < n; ++i) {
					if (tween1[i].name === name) {
					  tween1 = tween1.slice();
					  tween1.splice(i, 1);
					  break;
					}
				  }
				}

				schedule.tween = tween1;
			  };
			}

			function tweenFunction(id, name, value) {
			  var tween0, tween1;
			  if (typeof value !== "function") throw new Error;
			  return function() {
				var schedule = set$3(this, id),
					tween = schedule.tween;

				// If this node shared tween with the previous node,
				// just assign the updated shared tween and we’re done!
				// Otherwise, copy-on-write.
				if (tween !== tween0) {
				  tween1 = (tween0 = tween).slice();
				  for (var t = {name: name, value: value}, i = 0, n = tween1.length; i < n; ++i) {
					if (tween1[i].name === name) {
					  tween1[i] = t;
					  break;
					}
				  }
				  if (i === n) tween1.push(t);
				}

				schedule.tween = tween1;
			  };
			}

			var transition_tween = function(name, value) {
			  var id = this._id;

			  name += "";

			  if (arguments.length < 2) {
				var tween = get$1(this.node(), id).tween;
				for (var i = 0, n = tween.length, t; i < n; ++i) {
				  if ((t = tween[i]).name === name) {
					return t.value;
				  }
				}
				return null;
			  }

			  return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value));
			};

			function tweenValue(transition, name, value) {
			  var id = transition._id;

			  transition.each(function() {
				var schedule = set$3(this, id);
				(schedule.value || (schedule.value = {}))[name] = value.apply(this, arguments);
			  });

			  return function(node) {
				return get$1(node, id).value[name];
			  };
			}

			var interpolate$1 = function(a, b) {
			  var c;
			  return (typeof b === "number" ? interpolateNumber
				  : b instanceof color ? interpolateRgb
				  : (c = color(b)) ? (b = c, interpolateRgb)
				  : interpolateString)(a, b);
			};

			function attrRemove$1(name) {
			  return function() {
				this.removeAttribute(name);
			  };
			}

			function attrRemoveNS$1(fullname) {
			  return function() {
				this.removeAttributeNS(fullname.space, fullname.local);
			  };
			}

			function attrConstant$1(name, interpolate$$1, value1) {
			  var value00,
				  interpolate0;
			  return function() {
				var value0 = this.getAttribute(name);
				return value0 === value1 ? null
					: value0 === value00 ? interpolate0
					: interpolate0 = interpolate$$1(value00 = value0, value1);
			  };
			}

			function attrConstantNS$1(fullname, interpolate$$1, value1) {
			  var value00,
				  interpolate0;
			  return function() {
				var value0 = this.getAttributeNS(fullname.space, fullname.local);
				return value0 === value1 ? null
					: value0 === value00 ? interpolate0
					: interpolate0 = interpolate$$1(value00 = value0, value1);
			  };
			}

			function attrFunction$1(name, interpolate$$1, value) {
			  var value00,
				  value10,
				  interpolate0;
			  return function() {
				var value0, value1 = value(this);
				if (value1 == null) return void this.removeAttribute(name);
				value0 = this.getAttribute(name);
				return value0 === value1 ? null
					: value0 === value00 && value1 === value10 ? interpolate0
					: interpolate0 = interpolate$$1(value00 = value0, value10 = value1);
			  };
			}

			function attrFunctionNS$1(fullname, interpolate$$1, value) {
			  var value00,
				  value10,
				  interpolate0;
			  return function() {
				var value0, value1 = value(this);
				if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local);
				value0 = this.getAttributeNS(fullname.space, fullname.local);
				return value0 === value1 ? null
					: value0 === value00 && value1 === value10 ? interpolate0
					: interpolate0 = interpolate$$1(value00 = value0, value10 = value1);
			  };
			}

			var transition_attr = function(name, value) {
			  var fullname = namespace(name), i = fullname === "transform" ? interpolateTransformSvg : interpolate$1;
			  return this.attrTween(name, typeof value === "function"
				  ? (fullname.local ? attrFunctionNS$1 : attrFunction$1)(fullname, i, tweenValue(this, "attr." + name, value))
				  : value == null ? (fullname.local ? attrRemoveNS$1 : attrRemove$1)(fullname)
				  : (fullname.local ? attrConstantNS$1 : attrConstant$1)(fullname, i, value));
			};

			function attrTweenNS(fullname, value) {
			  function tween() {
				var node = this, i = value.apply(node, arguments);
				return i && function(t) {
				  node.setAttributeNS(fullname.space, fullname.local, i(t));
				};
			  }
			  tween._value = value;
			  return tween;
			}

			function attrTween(name, value) {
			  function tween() {
				var node = this, i = value.apply(node, arguments);
				return i && function(t) {
				  node.setAttribute(name, i(t));
				};
			  }
			  tween._value = value;
			  return tween;
			}

			var transition_attrTween = function(name, value) {
			  var key = "attr." + name;
			  if (arguments.length < 2) return (key = this.tween(key)) && key._value;
			  if (value == null) return this.tween(key, null);
			  if (typeof value !== "function") throw new Error;
			  var fullname = namespace(name);
			  return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value));
			};

			function delayFunction(id, value) {
			  return function() {
				init(this, id).delay = +value.apply(this, arguments);
			  };
			}

			function delayConstant(id, value) {
			  return value = +value, function() {
				init(this, id).delay = value;
			  };
			}

			var transition_delay = function(value) {
			  var id = this._id;

			  return arguments.length
				  ? this.each((typeof value === "function"
					  ? delayFunction
					  : delayConstant)(id, value))
				  : get$1(this.node(), id).delay;
			};

			function durationFunction(id, value) {
			  return function() {
				set$3(this, id).duration = +value.apply(this, arguments);
			  };
			}

			function durationConstant(id, value) {
			  return value = +value, function() {
				set$3(this, id).duration = value;
			  };
			}

			var transition_duration = function(value) {
			  var id = this._id;

			  return arguments.length
				  ? this.each((typeof value === "function"
					  ? durationFunction
					  : durationConstant)(id, value))
				  : get$1(this.node(), id).duration;
			};

			function easeConstant(id, value) {
			  if (typeof value !== "function") throw new Error;
			  return function() {
				set$3(this, id).ease = value;
			  };
			}

			var transition_ease = function(value) {
			  var id = this._id;

			  return arguments.length
				  ? this.each(easeConstant(id, value))
				  : get$1(this.node(), id).ease;
			};

			var transition_filter = function(match) {
			  if (typeof match !== "function") match = matcher$1(match);

			  for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
				for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
				  if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
					subgroup.push(node);
				  }
				}
			  }

			  return new Transition(subgroups, this._parents, this._name, this._id);
			};

			var transition_merge = function(transition) {
			  if (transition._id !== this._id) throw new Error;

			  for (var groups0 = this._groups, groups1 = transition._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {
				for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
				  if (node = group0[i] || group1[i]) {
					merge[i] = node;
				  }
				}
			  }

			  for (; j < m0; ++j) {
				merges[j] = groups0[j];
			  }

			  return new Transition(merges, this._parents, this._name, this._id);
			};

			function start$1(name) {
			  return (name + "").trim().split(/^|\s+/).every(function(t) {
				var i = t.indexOf(".");
				if (i >= 0) t = t.slice(0, i);
				return !t || t === "start";
			  });
			}

			function onFunction(id, name, listener) {
			  var on0, on1, sit = start$1(name) ? init : set$3;
			  return function() {
				var schedule = sit(this, id),
					on = schedule.on;

				// If this node shared a dispatch with the previous node,
				// just assign the updated shared dispatch and we’re done!
				// Otherwise, copy-on-write.
				if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener);

				schedule.on = on1;
			  };
			}

			var transition_on = function(name, listener) {
			  var id = this._id;

			  return arguments.length < 2
				  ? get$1(this.node(), id).on.on(name)
				  : this.each(onFunction(id, name, listener));
			};

			function removeFunction(id) {
			  return function() {
				var parent = this.parentNode;
				for (var i in this.__transition) if (+i !== id) return;
				if (parent) parent.removeChild(this);
			  };
			}

			var transition_remove = function() {
			  return this.on("end.remove", removeFunction(this._id));
			};

			var transition_select = function(select$$1) {
			  var name = this._name,
				  id = this._id;

			  if (typeof select$$1 !== "function") select$$1 = selector(select$$1);

			  for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
				for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
				  if ((node = group[i]) && (subnode = select$$1.call(node, node.__data__, i, group))) {
					if ("__data__" in node) subnode.__data__ = node.__data__;
					subgroup[i] = subnode;
					schedule(subgroup[i], name, id, i, subgroup, get$1(node, id));
				  }
				}
			  }

			  return new Transition(subgroups, this._parents, name, id);
			};

			var transition_selectAll = function(select$$1) {
			  var name = this._name,
				  id = this._id;

			  if (typeof select$$1 !== "function") select$$1 = selectorAll(select$$1);

			  for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
				for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
				  if (node = group[i]) {
					for (var children = select$$1.call(node, node.__data__, i, group), child, inherit = get$1(node, id), k = 0, l = children.length; k < l; ++k) {
					  if (child = children[k]) {
						schedule(child, name, id, k, children, inherit);
					  }
					}
					subgroups.push(children);
					parents.push(node);
				  }
				}
			  }

			  return new Transition(subgroups, parents, name, id);
			};

			var Selection$1 = selection.prototype.constructor;

			var transition_selection = function() {
			  return new Selection$1(this._groups, this._parents);
			};

			function styleRemove$1(name, interpolate$$1) {
			  var value00,
				  value10,
				  interpolate0;
			  return function() {
				var style = window(this).getComputedStyle(this, null),
					value0 = style.getPropertyValue(name),
					value1 = (this.style.removeProperty(name), style.getPropertyValue(name));
				return value0 === value1 ? null
					: value0 === value00 && value1 === value10 ? interpolate0
					: interpolate0 = interpolate$$1(value00 = value0, value10 = value1);
			  };
			}

			function styleRemoveEnd(name) {
			  return function() {
				this.style.removeProperty(name);
			  };
			}

			function styleConstant$1(name, interpolate$$1, value1) {
			  var value00,
				  interpolate0;
			  return function() {
				var value0 = window(this).getComputedStyle(this, null).getPropertyValue(name);
				return value0 === value1 ? null
					: value0 === value00 ? interpolate0
					: interpolate0 = interpolate$$1(value00 = value0, value1);
			  };
			}

			function styleFunction$1(name, interpolate$$1, value) {
			  var value00,
				  value10,
				  interpolate0;
			  return function() {
				var style = window(this).getComputedStyle(this, null),
					value0 = style.getPropertyValue(name),
					value1 = value(this);
				if (value1 == null) value1 = (this.style.removeProperty(name), style.getPropertyValue(name));
				return value0 === value1 ? null
					: value0 === value00 && value1 === value10 ? interpolate0
					: interpolate0 = interpolate$$1(value00 = value0, value10 = value1);
			  };
			}

			var transition_style = function(name, value, priority) {
			  var i = (name += "") === "transform" ? interpolateTransformCss : interpolate$1;
			  return value == null ? this
					  .styleTween(name, styleRemove$1(name, i))
					  .on("end.style." + name, styleRemoveEnd(name))
				  : this.styleTween(name, typeof value === "function"
					  ? styleFunction$1(name, i, tweenValue(this, "style." + name, value))
					  : styleConstant$1(name, i, value), priority);
			};

			function styleTween(name, value, priority) {
			  function tween() {
				var node = this, i = value.apply(node, arguments);
				return i && function(t) {
				  node.style.setProperty(name, i(t), priority);
				};
			  }
			  tween._value = value;
			  return tween;
			}

			var transition_styleTween = function(name, value, priority) {
			  var key = "style." + (name += "");
			  if (arguments.length < 2) return (key = this.tween(key)) && key._value;
			  if (value == null) return this.tween(key, null);
			  if (typeof value !== "function") throw new Error;
			  return this.tween(key, styleTween(name, value, priority == null ? "" : priority));
			};

			function textConstant$1(value) {
			  return function() {
				this.textContent = value;
			  };
			}

			function textFunction$1(value) {
			  return function() {
				var value1 = value(this);
				this.textContent = value1 == null ? "" : value1;
			  };
			}

			var transition_text = function(value) {
			  return this.tween("text", typeof value === "function"
				  ? textFunction$1(tweenValue(this, "text", value))
				  : textConstant$1(value == null ? "" : value + ""));
			};

			var transition_transition = function() {
			  var name = this._name,
				  id0 = this._id,
				  id1 = newId();

			  for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
				for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
				  if (node = group[i]) {
					var inherit = get$1(node, id0);
					schedule(node, name, id1, i, group, {
					  time: inherit.time + inherit.delay + inherit.duration,
					  delay: 0,
					  duration: inherit.duration,
					  ease: inherit.ease
					});
				  }
				}
			  }

			  return new Transition(groups, this._parents, name, id1);
			};

			var id = 0;

			function Transition(groups, parents, name, id) {
			  this._groups = groups;
			  this._parents = parents;
			  this._name = name;
			  this._id = id;
			}

			function transition(name) {
			  return selection().transition(name);
			}

			function newId() {
			  return ++id;
			}

			var selection_prototype = selection.prototype;

			Transition.prototype = transition.prototype = {
			  constructor: Transition,
			  select: transition_select,
			  selectAll: transition_selectAll,
			  filter: transition_filter,
			  merge: transition_merge,
			  selection: transition_selection,
			  transition: transition_transition,
			  call: selection_prototype.call,
			  nodes: selection_prototype.nodes,
			  node: selection_prototype.node,
			  size: selection_prototype.size,
			  empty: selection_prototype.empty,
			  each: selection_prototype.each,
			  on: transition_on,
			  attr: transition_attr,
			  attrTween: transition_attrTween,
			  style: transition_style,
			  styleTween: transition_styleTween,
			  text: transition_text,
			  remove: transition_remove,
			  tween: transition_tween,
			  delay: transition_delay,
			  duration: transition_duration,
			  ease: transition_ease
			};

			var defaultTiming = {
			  time: null, // Set on use.
			  delay: 0,
			  duration: 250,
			  ease: cubicInOut
			};

			function inherit(node, id) {
			  var timing;
			  while (!(timing = node.__transition) || !(timing = timing[id])) {
				if (!(node = node.parentNode)) {
				  return defaultTiming.time = now(), defaultTiming;
				}
			  }
			  return timing;
			}

			var selection_transition = function(name) {
			  var id,
				  timing;

			  if (name instanceof Transition) {
				id = name._id, name = name._name;
			  } else {
				id = newId(), (timing = defaultTiming).time = now(), name = name == null ? null : name + "";
			  }

			  for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
				for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
				  if (node = group[i]) {
					schedule(node, name, id, i, group, timing || inherit(node, id));
				  }
				}
			  }

			  return new Transition(groups, this._parents, name, id);
			};

			selection.prototype.interrupt = selection_interrupt;
			selection.prototype.transition = selection_transition;

			var root$1 = [null];

			var active = function(node, name) {
			  var schedules = node.__transition,
				  schedule,
				  i;

			  if (schedules) {
				name = name == null ? null : name + "";
				for (i in schedules) {
				  if ((schedule = schedules[i]).state > SCHEDULED && schedule.name === name) {
					return new Transition([[node]], root$1, name, +i);
				  }
				}
			  }

			  return null;
			};

			var slice$4 = Array.prototype.slice;

			var identity$5 = function(x) {
			  return x;
			};

			var top = 1;
			var right = 2;
			var bottom = 3;
			var left = 4;
			var epsilon$2 = 1e-6;

			function translateX(scale0, scale1, d) {
			  var x = scale0(d);
			  return "translate(" + (isFinite(x) ? x : scale1(d)) + ",0)";
			}

			function translateY(scale0, scale1, d) {
			  var y = scale0(d);
			  return "translate(0," + (isFinite(y) ? y : scale1(d)) + ")";
			}

			function center(scale) {
			  var offset = scale.bandwidth() / 2;
			  if (scale.round()) offset = Math.round(offset);
			  return function(d) {
				return scale(d) + offset;
			  };
			}

			function entering() {
			  return !this.__axis;
			}

			function axis(orient, scale) {
			  var tickArguments = [],
				  tickValues = null,
				  tickFormat = null,
				  tickSizeInner = 6,
				  tickSizeOuter = 6,
				  tickPadding = 3;

			  function axis(context) {
				var values = tickValues == null ? (scale.ticks ? scale.ticks.apply(scale, tickArguments) : scale.domain()) : tickValues,
					format = tickFormat == null ? (scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments) : identity$5) : tickFormat,
					spacing = Math.max(tickSizeInner, 0) + tickPadding,
					transform = orient === top || orient === bottom ? translateX : translateY,
					range = scale.range(),
					range0 = range[0] + 0.5,
					range1 = range[range.length - 1] + 0.5,
					position = (scale.bandwidth ? center : identity$5)(scale.copy()),
					selection = context.selection ? context.selection() : context,
					path = selection.selectAll(".domain").data([null]),
					tick = selection.selectAll(".tick").data(values, scale).order(),
					tickExit = tick.exit(),
					tickEnter = tick.enter().append("g").attr("class", "tick"),
					line = tick.select("line"),
					text = tick.select("text"),
					k = orient === top || orient === left ? -1 : 1,
					x, y = orient === left || orient === right ? (x = "x", "y") : (x = "y", "x");

				path = path.merge(path.enter().insert("path", ".tick")
					.attr("class", "domain")
					.attr("stroke", "#000"));

				tick = tick.merge(tickEnter);

				line = line.merge(tickEnter.append("line")
					.attr("stroke", "#000")
					.attr(x + "2", k * tickSizeInner)
					.attr(y + "1", 0.5)
					.attr(y + "2", 0.5));

				text = text.merge(tickEnter.append("text")
					.attr("fill", "#000")
					.attr(x, k * spacing)
					.attr(y, 0.5)
					.attr("dy", orient === top ? "0em" : orient === bottom ? "0.71em" : "0.32em"));

				if (context !== selection) {
				  path = path.transition(context);
				  tick = tick.transition(context);
				  line = line.transition(context);
				  text = text.transition(context);

				  tickExit = tickExit.transition(context)
					  .attr("opacity", epsilon$2)
					  .attr("transform", function(d) { return transform(position, this.parentNode.__axis || position, d); });

				  tickEnter
					  .attr("opacity", epsilon$2)
					  .attr("transform", function(d) { return transform(this.parentNode.__axis || position, position, d); });
				}

				tickExit.remove();

				path
					.attr("d", orient === left || orient == right
						? "M" + k * tickSizeOuter + "," + range0 + "H0.5V" + range1 + "H" + k * tickSizeOuter
						: "M" + range0 + "," + k * tickSizeOuter + "V0.5H" + range1 + "V" + k * tickSizeOuter);

				tick
					.attr("opacity", 1)
					.attr("transform", function(d) { return transform(position, position, d); });

				line
					.attr(x + "2", k * tickSizeInner);

				text
					.attr(x, k * spacing)
					.text(format);

				selection.filter(entering)
					.attr("fill", "none")
					.attr("font-size", 10)
					.attr("font-family", "sans-serif")
					.attr("text-anchor", orient === right ? "start" : orient === left ? "end" : "middle");

				selection
					.each(function() { this.__axis = position; });
			  }

			  axis.scale = function(_) {
				return arguments.length ? (scale = _, axis) : scale;
			  };

			  axis.ticks = function() {
				return tickArguments = slice$4.call(arguments), axis;
			  };

			  axis.tickArguments = function(_) {
				return arguments.length ? (tickArguments = _ == null ? [] : slice$4.call(_), axis) : tickArguments.slice();
			  };

			  axis.tickValues = function(_) {
				return arguments.length ? (tickValues = _ == null ? null : slice$4.call(_), axis) : tickValues && tickValues.slice();
			  };

			  axis.tickFormat = function(_) {
				return arguments.length ? (tickFormat = _, axis) : tickFormat;
			  };

			  axis.tickSize = function(_) {
				return arguments.length ? (tickSizeInner = tickSizeOuter = +_, axis) : tickSizeInner;
			  };

			  axis.tickSizeInner = function(_) {
				return arguments.length ? (tickSizeInner = +_, axis) : tickSizeInner;
			  };

			  axis.tickSizeOuter = function(_) {
				return arguments.length ? (tickSizeOuter = +_, axis) : tickSizeOuter;
			  };

			  axis.tickPadding = function(_) {
				return arguments.length ? (tickPadding = +_, axis) : tickPadding;
			  };

			  return axis;
			}

			function axisTop(scale) {
			  return axis(top, scale);
			}

			function axisRight(scale) {
			  return axis(right, scale);
			}

			function axisBottom(scale) {
			  return axis(bottom, scale);
			}

			function axisLeft(scale) {
			  return axis(left, scale);
			}

			function defaultSeparation(a, b) {
			  return a.parent === b.parent ? 1 : 2;
			}

			function meanX(children) {
			  return children.reduce(meanXReduce, 0) / children.length;
			}

			function meanXReduce(x, c) {
			  return x + c.x;
			}

			function maxY(children) {
			  return 1 + children.reduce(maxYReduce, 0);
			}

			function maxYReduce(y, c) {
			  return Math.max(y, c.y);
			}

			function leafLeft(node) {
			  var children;
			  while (children = node.children) node = children[0];
			  return node;
			}

			function leafRight(node) {
			  var children;
			  while (children = node.children) node = children[children.length - 1];
			  return node;
			}

			var cluster = function() {
			  var separation = defaultSeparation,
				  dx = 1,
				  dy = 1,
				  nodeSize = false;

			  function cluster(root) {
				var previousNode,
					x = 0;

				// First walk, computing the initial x & y values.
				root.eachAfter(function(node) {
				  var children = node.children;
				  if (children) {
					node.x = meanX(children);
					node.y = maxY(children);
				  } else {
					node.x = previousNode ? x += separation(node, previousNode) : 0;
					node.y = 0;
					previousNode = node;
				  }
				});

				var left = leafLeft(root),
					right = leafRight(root),
					x0 = left.x - separation(left, right) / 2,
					x1 = right.x + separation(right, left) / 2;

				// Second walk, normalizing x & y to the desired size.
				return root.eachAfter(nodeSize ? function(node) {
				  node.x = (node.x - root.x) * dx;
				  node.y = (root.y - node.y) * dy;
				} : function(node) {
				  node.x = (node.x - x0) / (x1 - x0) * dx;
				  node.y = (1 - (root.y ? node.y / root.y : 1)) * dy;
				});
			  }

			  cluster.separation = function(x) {
				return arguments.length ? (separation = x, cluster) : separation;
			  };

			  cluster.size = function(x) {
				return arguments.length ? (nodeSize = false, dx = +x[0], dy = +x[1], cluster) : (nodeSize ? null : [dx, dy]);
			  };

			  cluster.nodeSize = function(x) {
				return arguments.length ? (nodeSize = true, dx = +x[0], dy = +x[1], cluster) : (nodeSize ? [dx, dy] : null);
			  };

			  return cluster;
			};

			var node_each = function(callback) {
			  var node = this, current, next = [node], children, i, n;
			  do {
				current = next.reverse(), next = [];
				while (node = current.pop()) {
				  callback(node), children = node.children;
				  if (children) for (i = 0, n = children.length; i < n; ++i) {
					next.push(children[i]);
				  }
				}
			  } while (next.length);
			  return this;
			};

			var node_eachBefore = function(callback) {
			  var node = this, nodes = [node], children, i;
			  while (node = nodes.pop()) {
				callback(node), children = node.children;
				if (children) for (i = children.length - 1; i >= 0; --i) {
				  nodes.push(children[i]);
				}
			  }
			  return this;
			};

			var node_eachAfter = function(callback) {
			  var node = this, nodes = [node], next = [], children, i, n;
			  while (node = nodes.pop()) {
				next.push(node), children = node.children;
				if (children) for (i = 0, n = children.length; i < n; ++i) {
				  nodes.push(children[i]);
				}
			  }
			  while (node = next.pop()) {
				callback(node);
			  }
			  return this;
			};

			var node_sum = function(value) {
			  return this.eachAfter(function(node) {
				var sum = +value(node.data) || 0,
					children = node.children,
					i = children && children.length;
				while (--i >= 0) sum += children[i].value;
				node.value = sum;
			  });
			};

			var node_sort = function(compare) {
			  return this.eachBefore(function(node) {
				if (node.children) {
				  node.children.sort(compare);
				}
			  });
			};

			var node_path = function(end) {
			  var start = this,
				  ancestor = leastCommonAncestor(start, end),
				  nodes = [start];
			  while (start !== ancestor) {
				start = start.parent;
				nodes.push(start);
			  }
			  var k = nodes.length;
			  while (end !== ancestor) {
				nodes.splice(k, 0, end);
				end = end.parent;
			  }
			  return nodes;
			};

			function leastCommonAncestor(a, b) {
			  if (a === b) return a;
			  var aNodes = a.ancestors(),
				  bNodes = b.ancestors(),
				  c = null;
			  a = aNodes.pop();
			  b = bNodes.pop();
			  while (a === b) {
				c = a;
				a = aNodes.pop();
				b = bNodes.pop();
			  }
			  return c;
			}

			var node_ancestors = function() {
			  var node = this, nodes = [node];
			  while (node = node.parent) {
				nodes.push(node);
			  }
			  return nodes;
			};

			var node_descendants = function() {
			  var nodes = [];
			  this.each(function(node) {
				nodes.push(node);
			  });
			  return nodes;
			};

			var node_leaves = function() {
			  var leaves = [];
			  this.eachBefore(function(node) {
				if (!node.children) {
				  leaves.push(node);
				}
			  });
			  return leaves;
			};

			var node_links = function() {
			  var root = this, links = [];
			  root.each(function(node) {
				if (node !== root) { // Don’t include the root’s parent, if any.
				  links.push({source: node.parent, target: node});
				}
			  });
			  return links;
			};

			function hierarchy(data, children) {
			  var root = new Node(data),
				  valued = +data.value && (root.value = data.value),
				  node,
				  nodes = [root],
				  child,
				  childs,
				  i,
				  n;

			  if (children == null) children = defaultChildren;

			  while (node = nodes.pop()) {
				if (valued) node.value = +node.data.value;
				if ((childs = children(node.data)) && (n = childs.length)) {
				  node.children = new Array(n);
				  for (i = n - 1; i >= 0; --i) {
					nodes.push(child = node.children[i] = new Node(childs[i]));
					child.parent = node;
					child.depth = node.depth + 1;
				  }
				}
			  }

			  return root.eachBefore(computeHeight);
			}

			function node_copy() {
			  return hierarchy(this).eachBefore(copyData);
			}

			function defaultChildren(d) {
			  return d.children;
			}

			function copyData(node) {
			  node.data = node.data.data;
			}

			function computeHeight(node) {
			  var height = 0;
			  do node.height = height;
			  while ((node = node.parent) && (node.height < ++height));
			}

			function Node(data) {
			  this.data = data;
			  this.depth =
			  this.height = 0;
			  this.parent = null;
			}

			Node.prototype = hierarchy.prototype = {
			  constructor: Node,
			  each: node_each,
			  eachAfter: node_eachAfter,
			  eachBefore: node_eachBefore,
			  sum: node_sum,
			  sort: node_sort,
			  path: node_path,
			  ancestors: node_ancestors,
			  descendants: node_descendants,
			  leaves: node_leaves,
			  links: node_links,
			  copy: node_copy
			};

			function Node$2(value) {
			  this._ = value;
			  this.next = null;
			}

			var shuffle$1 = function(array) {
			  var i,
				  n = (array = array.slice()).length,
				  head = null,
				  node = head;

			  while (n) {
				var next = new Node$2(array[n - 1]);
				if (node) node = node.next = next;
				else node = head = next;
				array[i] = array[--n];
			  }

			  return {
				head: head,
				tail: node
			  };
			};

			var enclose = function(circles) {
			  return encloseN(shuffle$1(circles), []);
			};

			function encloses(a, b) {
			  var dx = b.x - a.x,
				  dy = b.y - a.y,
				  dr = a.r - b.r;
			  return dr * dr + 1e-6 > dx * dx + dy * dy;
			}

			// Returns the smallest circle that contains circles L and intersects circles B.
			function encloseN(L, B) {
			  var circle,
				  l0 = null,
				  l1 = L.head,
				  l2,
				  p1;

			  switch (B.length) {
				case 1: circle = enclose1(B[0]); break;
				case 2: circle = enclose2(B[0], B[1]); break;
				case 3: circle = enclose3(B[0], B[1], B[2]); break;
			  }

			  while (l1) {
				p1 = l1._, l2 = l1.next;
				if (!circle || !encloses(circle, p1)) {

				  // Temporarily truncate L before l1.
				  if (l0) L.tail = l0, l0.next = null;
				  else L.head = L.tail = null;

				  B.push(p1);
				  circle = encloseN(L, B); // Note: reorders L!
				  B.pop();

				  // Move l1 to the front of L and reconnect the truncated list L.
				  if (L.head) l1.next = L.head, L.head = l1;
				  else l1.next = null, L.head = L.tail = l1;
				  l0 = L.tail, l0.next = l2;

				} else {
				  l0 = l1;
				}
				l1 = l2;
			  }

			  L.tail = l0;
			  return circle;
			}

			function enclose1(a) {
			  return {
				x: a.x,
				y: a.y,
				r: a.r
			  };
			}

			function enclose2(a, b) {
			  var x1 = a.x, y1 = a.y, r1 = a.r,
				  x2 = b.x, y2 = b.y, r2 = b.r,
				  x21 = x2 - x1, y21 = y2 - y1, r21 = r2 - r1,
				  l = Math.sqrt(x21 * x21 + y21 * y21);
			  return {
				x: (x1 + x2 + x21 / l * r21) / 2,
				y: (y1 + y2 + y21 / l * r21) / 2,
				r: (l + r1 + r2) / 2
			  };
			}

			function enclose3(a, b, c) {
			  var x1 = a.x, y1 = a.y, r1 = a.r,
				  x2 = b.x, y2 = b.y, r2 = b.r,
				  x3 = c.x, y3 = c.y, r3 = c.r,
				  a2 = 2 * (x1 - x2),
				  b2 = 2 * (y1 - y2),
				  c2 = 2 * (r2 - r1),
				  d2 = x1 * x1 + y1 * y1 - r1 * r1 - x2 * x2 - y2 * y2 + r2 * r2,
				  a3 = 2 * (x1 - x3),
				  b3 = 2 * (y1 - y3),
				  c3 = 2 * (r3 - r1),
				  d3 = x1 * x1 + y1 * y1 - r1 * r1 - x3 * x3 - y3 * y3 + r3 * r3,
				  ab = a3 * b2 - a2 * b3,
				  xa = (b2 * d3 - b3 * d2) / ab - x1,
				  xb = (b3 * c2 - b2 * c3) / ab,
				  ya = (a3 * d2 - a2 * d3) / ab - y1,
				  yb = (a2 * c3 - a3 * c2) / ab,
				  A = xb * xb + yb * yb - 1,
				  B = 2 * (xa * xb + ya * yb + r1),
				  C = xa * xa + ya * ya - r1 * r1,
				  r = (-B - Math.sqrt(B * B - 4 * A * C)) / (2 * A);
			  return {
				x: xa + xb * r + x1,
				y: ya + yb * r + y1,
				r: r
			  };
			}

			function place(a, b, c) {
			  var ax = a.x,
				  ay = a.y,
				  da = b.r + c.r,
				  db = a.r + c.r,
				  dx = b.x - ax,
				  dy = b.y - ay,
				  dc = dx * dx + dy * dy;
			  if (dc) {
				var x = 0.5 + ((db *= db) - (da *= da)) / (2 * dc),
					y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc);
				c.x = ax + x * dx + y * dy;
				c.y = ay + x * dy - y * dx;
			  } else {
				c.x = ax + db;
				c.y = ay;
			  }
			}

			function intersects(a, b) {
			  var dx = b.x - a.x,
				  dy = b.y - a.y,
				  dr = a.r + b.r;
			  return dr * dr > dx * dx + dy * dy;
			}

			function distance2(circle, x, y) {
			  var dx = circle.x - x,
				  dy = circle.y - y;
			  return dx * dx + dy * dy;
			}

			function Node$1(circle) {
			  this._ = circle;
			  this.next = null;
			  this.previous = null;
			}

			function packEnclose(circles) {
			  if (!(n = circles.length)) return 0;

			  var a, b, c, n;

			  // Place the first circle.
			  a = circles[0], a.x = 0, a.y = 0;
			  if (!(n > 1)) return a.r;

			  // Place the second circle.
			  b = circles[1], a.x = -b.r, b.x = a.r, b.y = 0;
			  if (!(n > 2)) return a.r + b.r;

			  // Place the third circle.
			  place(b, a, c = circles[2]);

			  // Initialize the weighted centroid.
			  var aa = a.r * a.r,
				  ba = b.r * b.r,
				  ca = c.r * c.r,
				  oa = aa + ba + ca,
				  ox = aa * a.x + ba * b.x + ca * c.x,
				  oy = aa * a.y + ba * b.y + ca * c.y,
				  cx, cy, i, j, k, sj, sk;

			  // Initialize the front-chain using the first three circles a, b and c.
			  a = new Node$1(a), b = new Node$1(b), c = new Node$1(c);
			  a.next = c.previous = b;
			  b.next = a.previous = c;
			  c.next = b.previous = a;

			  // Attempt to place each remaining circle…
			  pack: for (i = 3; i < n; ++i) {
				place(a._, b._, c = circles[i]), c = new Node$1(c);

				// If there are only three elements in the front-chain…
				if ((k = a.previous) === (j = b.next)) {
				  // If the new circle intersects the third circle,
				  // rotate the front chain to try the next position.
				  if (intersects(j._, c._)) {
					a = b, b = j, --i;
					continue pack;
				  }
				}

				// Find the closest intersecting circle on the front-chain, if any.
				else {
				  sj = j._.r, sk = k._.r;
				  do {
					if (sj <= sk) {
					  if (intersects(j._, c._)) {
						b = j, a.next = b, b.previous = a, --i;
						continue pack;
					  }
					  j = j.next, sj += j._.r;
					} else {
					  if (intersects(k._, c._)) {
						a = k, a.next = b, b.previous = a, --i;
						continue pack;
					  }
					  k = k.previous, sk += k._.r;
					}
				  } while (j !== k.next);
				}

				// Success! Insert the new circle c between a and b.
				c.previous = a, c.next = b, a.next = b.previous = b = c;

				// Update the weighted centroid.
				oa += ca = c._.r * c._.r;
				ox += ca * c._.x;
				oy += ca * c._.y;

				// Compute the new closest circle a to centroid.
				aa = distance2(a._, cx = ox / oa, cy = oy / oa);
				while ((c = c.next) !== b) {
				  if ((ca = distance2(c._, cx, cy)) < aa) {
					a = c, aa = ca;
				  }
				}
				b = a.next;
			  }

			  // Compute the enclosing circle of the front chain.
			  a = [b._], c = b; while ((c = c.next) !== b) a.push(c._); c = enclose(a);

			  // Translate the circles to put the enclosing circle around the origin.
			  for (i = 0; i < n; ++i) a = circles[i], a.x -= c.x, a.y -= c.y;

			  return c.r;
			}

			var siblings = function(circles) {
			  packEnclose(circles);
			  return circles;
			};

			function optional(f) {
			  return f == null ? null : required(f);
			}

			function required(f) {
			  if (typeof f !== "function") throw new Error;
			  return f;
			}

			function constantZero() {
			  return 0;
			}

			var constant$6 = function(x) {
			  return function() {
				return x;
			  };
			};

			function defaultRadius(d) {
			  return Math.sqrt(d.value);
			}

			var index = function() {
			  var radius = null,
				  dx = 1,
				  dy = 1,
				  padding = constantZero;

			  function pack(root) {
				root.x = dx / 2, root.y = dy / 2;
				if (radius) {
				  root.eachBefore(radiusLeaf(radius))
					  .eachAfter(packChildren(padding, 0.5))
					  .eachBefore(translateChild(1));
				} else {
				  root.eachBefore(radiusLeaf(defaultRadius))
					  .eachAfter(packChildren(constantZero, 1))
					  .eachAfter(packChildren(padding, root.r / Math.min(dx, dy)))
					  .eachBefore(translateChild(Math.min(dx, dy) / (2 * root.r)));
				}
				return root;
			  }

			  pack.radius = function(x) {
				return arguments.length ? (radius = optional(x), pack) : radius;
			  };

			  pack.size = function(x) {
				return arguments.length ? (dx = +x[0], dy = +x[1], pack) : [dx, dy];
			  };

			  pack.padding = function(x) {
				return arguments.length ? (padding = typeof x === "function" ? x : constant$6(+x), pack) : padding;
			  };

			  return pack;
			};

			function radiusLeaf(radius) {
			  return function(node) {
				if (!node.children) {
				  node.r = Math.max(0, +radius(node) || 0);
				}
			  };
			}

			function packChildren(padding, k) {
			  return function(node) {
				if (children = node.children) {
				  var children,
					  i,
					  n = children.length,
					  r = padding(node) * k || 0,
					  e;

				  if (r) for (i = 0; i < n; ++i) children[i].r += r;
				  e = packEnclose(children);
				  if (r) for (i = 0; i < n; ++i) children[i].r -= r;
				  node.r = e + r;
				}
			  };
			}

			function translateChild(k) {
			  return function(node) {
				var parent = node.parent;
				node.r *= k;
				if (parent) {
				  node.x = parent.x + k * node.x;
				  node.y = parent.y + k * node.y;
				}
			  };
			}

			var roundNode = function(node) {
			  node.x0 = Math.round(node.x0);
			  node.y0 = Math.round(node.y0);
			  node.x1 = Math.round(node.x1);
			  node.y1 = Math.round(node.y1);
			};

			var treemapDice = function(parent, x0, y0, x1, y1) {
			  var nodes = parent.children,
				  node,
				  i = -1,
				  n = nodes.length,
				  k = parent.value && (x1 - x0) / parent.value;

			  while (++i < n) {
				node = nodes[i], node.y0 = y0, node.y1 = y1;
				node.x0 = x0, node.x1 = x0 += node.value * k;
			  }
			};

			var partition = function() {
			  var dx = 1,
				  dy = 1,
				  padding = 0,
				  round = false;

			  function partition(root) {
				var n = root.height + 1;
				root.x0 =
				root.y0 = padding;
				root.x1 = dx;
				root.y1 = dy / n;
				root.eachBefore(positionNode(dy, n));
				if (round) root.eachBefore(roundNode);
				return root;
			  }

			  function positionNode(dy, n) {
				return function(node) {
				  if (node.children) {
					treemapDice(node, node.x0, dy * (node.depth + 1) / n, node.x1, dy * (node.depth + 2) / n);
				  }
				  var x0 = node.x0,
					  y0 = node.y0,
					  x1 = node.x1 - padding,
					  y1 = node.y1 - padding;
				  if (x1 < x0) x0 = x1 = (x0 + x1) / 2;
				  if (y1 < y0) y0 = y1 = (y0 + y1) / 2;
				  node.x0 = x0;
				  node.y0 = y0;
				  node.x1 = x1;
				  node.y1 = y1;
				};
			  }

			  partition.round = function(x) {
				return arguments.length ? (round = !!x, partition) : round;
			  };

			  partition.size = function(x) {
				return arguments.length ? (dx = +x[0], dy = +x[1], partition) : [dx, dy];
			  };

			  partition.padding = function(x) {
				return arguments.length ? (padding = +x, partition) : padding;
			  };

			  return partition;
			};

			var keyPrefix$1 = "$";
			var preroot = {depth: -1};
			var ambiguous = {};

			function defaultId(d) {
			  return d.id;
			}

			function defaultParentId(d) {
			  return d.parentId;
			}

			var stratify = function() {
			  var id = defaultId,
				  parentId = defaultParentId;

			  function stratify(data) {
				var d,
					i,
					n = data.length,
					root,
					parent,
					node,
					nodes = new Array(n),
					nodeId,
					nodeKey,
					nodeByKey = {};

				for (i = 0; i < n; ++i) {
				  d = data[i], node = nodes[i] = new Node(d);
				  if ((nodeId = id(d, i, data)) != null && (nodeId += "")) {
					nodeKey = keyPrefix$1 + (node.id = nodeId);
					nodeByKey[nodeKey] = nodeKey in nodeByKey ? ambiguous : node;
				  }
				}

				for (i = 0; i < n; ++i) {
				  node = nodes[i], nodeId = parentId(data[i], i, data);
				  if (nodeId == null || !(nodeId += "")) {
					if (root) throw new Error("multiple roots");
					root = node;
				  } else {
					parent = nodeByKey[keyPrefix$1 + nodeId];
					if (!parent) throw new Error("missing: " + nodeId);
					if (parent === ambiguous) throw new Error("ambiguous: " + nodeId);
					if (parent.children) parent.children.push(node);
					else parent.children = [node];
					node.parent = parent;
				  }
				}

				if (!root) throw new Error("no root");
				root.parent = preroot;
				root.eachBefore(function(node) { node.depth = node.parent.depth + 1; --n; }).eachBefore(computeHeight);
				root.parent = null;
				if (n > 0) throw new Error("cycle");

				return root;
			  }

			  stratify.id = function(x) {
				return arguments.length ? (id = required(x), stratify) : id;
			  };

			  stratify.parentId = function(x) {
				return arguments.length ? (parentId = required(x), stratify) : parentId;
			  };

			  return stratify;
			};

			function defaultSeparation$1(a, b) {
			  return a.parent === b.parent ? 1 : 2;
			}

			// function radialSeparation(a, b) {
			//   return (a.parent === b.parent ? 1 : 2) / a.depth;
			// }

			// This function is used to traverse the left contour of a subtree (or
			// subforest). It returns the successor of v on this contour. This successor is
			// either given by the leftmost child of v or by the thread of v. The function
			// returns null if and only if v is on the highest level of its subtree.
			function nextLeft(v) {
			  var children = v.children;
			  return children ? children[0] : v.t;
			}

			// This function works analogously to nextLeft.
			function nextRight(v) {
			  var children = v.children;
			  return children ? children[children.length - 1] : v.t;
			}

			// Shifts the current subtree rooted at w+. This is done by increasing
			// prelim(w+) and mod(w+) by shift.
			function moveSubtree(wm, wp, shift) {
			  var change = shift / (wp.i - wm.i);
			  wp.c -= change;
			  wp.s += shift;
			  wm.c += change;
			  wp.z += shift;
			  wp.m += shift;
			}

			// All other shifts, applied to the smaller subtrees between w- and w+, are
			// performed by this function. To prepare the shifts, we have to adjust
			// change(w+), shift(w+), and change(w-).
			function executeShifts(v) {
			  var shift = 0,
				  change = 0,
				  children = v.children,
				  i = children.length,
				  w;
			  while (--i >= 0) {
				w = children[i];
				w.z += shift;
				w.m += shift;
				shift += w.s + (change += w.c);
			  }
			}

			// If vi-’s ancestor is a sibling of v, returns vi-’s ancestor. Otherwise,
			// returns the specified (default) ancestor.
			function nextAncestor(vim, v, ancestor) {
			  return vim.a.parent === v.parent ? vim.a : ancestor;
			}

			function TreeNode(node, i) {
			  this._ = node;
			  this.parent = null;
			  this.children = null;
			  this.A = null; // default ancestor
			  this.a = this; // ancestor
			  this.z = 0; // prelim
			  this.m = 0; // mod
			  this.c = 0; // change
			  this.s = 0; // shift
			  this.t = null; // thread
			  this.i = i; // number
			}

			TreeNode.prototype = Object.create(Node.prototype);

			function treeRoot(root) {
			  var tree = new TreeNode(root, 0),
				  node,
				  nodes = [tree],
				  child,
				  children,
				  i,
				  n;

			  while (node = nodes.pop()) {
				if (children = node._.children) {
				  node.children = new Array(n = children.length);
				  for (i = n - 1; i >= 0; --i) {
					nodes.push(child = node.children[i] = new TreeNode(children[i], i));
					child.parent = node;
				  }
				}
			  }

			  (tree.parent = new TreeNode(null, 0)).children = [tree];
			  return tree;
			}

			// Node-link tree diagram using the Reingold-Tilford "tidy" algorithm
			var tree = function() {
			  var separation = defaultSeparation$1,
				  dx = 1,
				  dy = 1,
				  nodeSize = null;

			  function tree(root) {
				var t = treeRoot(root);

				// Compute the layout using Buchheim et al.’s algorithm.
				t.eachAfter(firstWalk), t.parent.m = -t.z;
				t.eachBefore(secondWalk);

				// If a fixed node size is specified, scale x and y.
				if (nodeSize) root.eachBefore(sizeNode);

				// If a fixed tree size is specified, scale x and y based on the extent.
				// Compute the left-most, right-most, and depth-most nodes for extents.
				else {
				  var left = root,
					  right = root,
					  bottom = root;
				  root.eachBefore(function(node) {
					if (node.x < left.x) left = node;
					if (node.x > right.x) right = node;
					if (node.depth > bottom.depth) bottom = node;
				  });
				  var s = left === right ? 1 : separation(left, right) / 2,
					  tx = s - left.x,
					  kx = dx / (right.x + s + tx),
					  ky = dy / (bottom.depth || 1);
				  root.eachBefore(function(node) {
					node.x = (node.x + tx) * kx;
					node.y = node.depth * ky;
				  });
				}

				return root;
			  }

			  // Computes a preliminary x-coordinate for v. Before that, FIRST WALK is
			  // applied recursively to the children of v, as well as the function
			  // APPORTION. After spacing out the children by calling EXECUTE SHIFTS, the
			  // node v is placed to the midpoint of its outermost children.
			  function firstWalk(v) {
				var children = v.children,
					siblings = v.parent.children,
					w = v.i ? siblings[v.i - 1] : null;
				if (children) {
				  executeShifts(v);
				  var midpoint = (children[0].z + children[children.length - 1].z) / 2;
				  if (w) {
					v.z = w.z + separation(v._, w._);
					v.m = v.z - midpoint;
				  } else {
					v.z = midpoint;
				  }
				} else if (w) {
				  v.z = w.z + separation(v._, w._);
				}
				v.parent.A = apportion(v, w, v.parent.A || siblings[0]);
			  }

			  // Computes all real x-coordinates by summing up the modifiers recursively.
			  function secondWalk(v) {
				v._.x = v.z + v.parent.m;
				v.m += v.parent.m;
			  }

			  // The core of the algorithm. Here, a new subtree is combined with the
			  // previous subtrees. Threads are used to traverse the inside and outside
			  // contours of the left and right subtree up to the highest common level. The
			  // vertices used for the traversals are vi+, vi-, vo-, and vo+, where the
			  // superscript o means outside and i means inside, the subscript - means left
			  // subtree and + means right subtree. For summing up the modifiers along the
			  // contour, we use respective variables si+, si-, so-, and so+. Whenever two
			  // nodes of the inside contours conflict, we compute the left one of the
			  // greatest uncommon ancestors using the function ANCESTOR and call MOVE
			  // SUBTREE to shift the subtree and prepare the shifts of smaller subtrees.
			  // Finally, we add a new thread (if necessary).
			  function apportion(v, w, ancestor) {
				if (w) {
				  var vip = v,
					  vop = v,
					  vim = w,
					  vom = vip.parent.children[0],
					  sip = vip.m,
					  sop = vop.m,
					  sim = vim.m,
					  som = vom.m,
					  shift;
				  while (vim = nextRight(vim), vip = nextLeft(vip), vim && vip) {
					vom = nextLeft(vom);
					vop = nextRight(vop);
					vop.a = v;
					shift = vim.z + sim - vip.z - sip + separation(vim._, vip._);
					if (shift > 0) {
					  moveSubtree(nextAncestor(vim, v, ancestor), v, shift);
					  sip += shift;
					  sop += shift;
					}
					sim += vim.m;
					sip += vip.m;
					som += vom.m;
					sop += vop.m;
				  }
				  if (vim && !nextRight(vop)) {
					vop.t = vim;
					vop.m += sim - sop;
				  }
				  if (vip && !nextLeft(vom)) {
					vom.t = vip;
					vom.m += sip - som;
					ancestor = v;
				  }
				}
				return ancestor;
			  }

			  function sizeNode(node) {
				node.x *= dx;
				node.y = node.depth * dy;
			  }

			  tree.separation = function(x) {
				return arguments.length ? (separation = x, tree) : separation;
			  };

			  tree.size = function(x) {
				return arguments.length ? (nodeSize = false, dx = +x[0], dy = +x[1], tree) : (nodeSize ? null : [dx, dy]);
			  };

			  tree.nodeSize = function(x) {
				return arguments.length ? (nodeSize = true, dx = +x[0], dy = +x[1], tree) : (nodeSize ? [dx, dy] : null);
			  };

			  return tree;
			};

			var treemapSlice = function(parent, x0, y0, x1, y1) {
			  var nodes = parent.children,
				  node,
				  i = -1,
				  n = nodes.length,
				  k = parent.value && (y1 - y0) / parent.value;

			  while (++i < n) {
				node = nodes[i], node.x0 = x0, node.x1 = x1;
				node.y0 = y0, node.y1 = y0 += node.value * k;
			  }
			};

			var phi = (1 + Math.sqrt(5)) / 2;

			function squarifyRatio(ratio, parent, x0, y0, x1, y1) {
			  var rows = [],
				  nodes = parent.children,
				  row,
				  nodeValue,
				  i0 = 0,
				  i1,
				  n = nodes.length,
				  dx, dy,
				  value = parent.value,
				  sumValue,
				  minValue,
				  maxValue,
				  newRatio,
				  minRatio,
				  alpha,
				  beta;

			  while (i0 < n) {
				dx = x1 - x0, dy = y1 - y0;
				minValue = maxValue = sumValue = nodes[i0].value;
				alpha = Math.max(dy / dx, dx / dy) / (value * ratio);
				beta = sumValue * sumValue * alpha;
				minRatio = Math.max(maxValue / beta, beta / minValue);

				// Keep adding nodes while the aspect ratio maintains or improves.
				for (i1 = i0 + 1; i1 < n; ++i1) {
				  sumValue += nodeValue = nodes[i1].value;
				  if (nodeValue < minValue) minValue = nodeValue;
				  if (nodeValue > maxValue) maxValue = nodeValue;
				  beta = sumValue * sumValue * alpha;
				  newRatio = Math.max(maxValue / beta, beta / minValue);
				  if (newRatio > minRatio) { sumValue -= nodeValue; break; }
				  minRatio = newRatio;
				}

				// Position and record the row orientation.
				rows.push(row = {value: sumValue, dice: dx < dy, children: nodes.slice(i0, i1)});
				if (row.dice) treemapDice(row, x0, y0, x1, value ? y0 += dy * sumValue / value : y1);
				else treemapSlice(row, x0, y0, value ? x0 += dx * sumValue / value : x1, y1);
				value -= sumValue, i0 = i1;
			  }

			  return rows;
			}

			var squarify = (function custom(ratio) {

			  function squarify(parent, x0, y0, x1, y1) {
				squarifyRatio(ratio, parent, x0, y0, x1, y1);
			  }

			  squarify.ratio = function(x) {
				return custom((x = +x) > 1 ? x : 1);
			  };

			  return squarify;
			})(phi);

			var index$1 = function() {
			  var tile = squarify,
				  round = false,
				  dx = 1,
				  dy = 1,
				  paddingStack = [0],
				  paddingInner = constantZero,
				  paddingTop = constantZero,
				  paddingRight = constantZero,
				  paddingBottom = constantZero,
				  paddingLeft = constantZero;

			  function treemap(root) {
				root.x0 =
				root.y0 = 0;
				root.x1 = dx;
				root.y1 = dy;
				root.eachBefore(positionNode);
				paddingStack = [0];
				if (round) root.eachBefore(roundNode);
				return root;
			  }

			  function positionNode(node) {
				var p = paddingStack[node.depth],
					x0 = node.x0 + p,
					y0 = node.y0 + p,
					x1 = node.x1 - p,
					y1 = node.y1 - p;
				if (x1 < x0) x0 = x1 = (x0 + x1) / 2;
				if (y1 < y0) y0 = y1 = (y0 + y1) / 2;
				node.x0 = x0;
				node.y0 = y0;
				node.x1 = x1;
				node.y1 = y1;
				if (node.children) {
				  p = paddingStack[node.depth + 1] = paddingInner(node) / 2;
				  x0 += paddingLeft(node) - p;
				  y0 += paddingTop(node) - p;
				  x1 -= paddingRight(node) - p;
				  y1 -= paddingBottom(node) - p;
				  if (x1 < x0) x0 = x1 = (x0 + x1) / 2;
				  if (y1 < y0) y0 = y1 = (y0 + y1) / 2;
				  tile(node, x0, y0, x1, y1);
				}
			  }

			  treemap.round = function(x) {
				return arguments.length ? (round = !!x, treemap) : round;
			  };

			  treemap.size = function(x) {
				return arguments.length ? (dx = +x[0], dy = +x[1], treemap) : [dx, dy];
			  };

			  treemap.tile = function(x) {
				return arguments.length ? (tile = required(x), treemap) : tile;
			  };

			  treemap.padding = function(x) {
				return arguments.length ? treemap.paddingInner(x).paddingOuter(x) : treemap.paddingInner();
			  };

			  treemap.paddingInner = function(x) {
				return arguments.length ? (paddingInner = typeof x === "function" ? x : constant$6(+x), treemap) : paddingInner;
			  };

			  treemap.paddingOuter = function(x) {
				return arguments.length ? treemap.paddingTop(x).paddingRight(x).paddingBottom(x).paddingLeft(x) : treemap.paddingTop();
			  };

			  treemap.paddingTop = function(x) {
				return arguments.length ? (paddingTop = typeof x === "function" ? x : constant$6(+x), treemap) : paddingTop;
			  };

			  treemap.paddingRight = function(x) {
				return arguments.length ? (paddingRight = typeof x === "function" ? x : constant$6(+x), treemap) : paddingRight;
			  };

			  treemap.paddingBottom = function(x) {
				return arguments.length ? (paddingBottom = typeof x === "function" ? x : constant$6(+x), treemap) : paddingBottom;
			  };

			  treemap.paddingLeft = function(x) {
				return arguments.length ? (paddingLeft = typeof x === "function" ? x : constant$6(+x), treemap) : paddingLeft;
			  };

			  return treemap;
			};

			var binary = function(parent, x0, y0, x1, y1) {
			  var nodes = parent.children,
				  i, n = nodes.length,
				  sum, sums = new Array(n + 1);

			  for (sums[0] = sum = i = 0; i < n; ++i) {
				sums[i + 1] = sum += nodes[i].value;
			  }

			  partition(0, n, parent.value, x0, y0, x1, y1);

			  function partition(i, j, value, x0, y0, x1, y1) {
				if (i >= j - 1) {
				  var node = nodes[i];
				  node.x0 = x0, node.y0 = y0;
				  node.x1 = x1, node.y1 = y1;
				  return;
				}

				var valueOffset = sums[i],
					valueTarget = (value / 2) + valueOffset,
					k = i + 1,
					hi = j - 1;

				while (k < hi) {
				  var mid = k + hi >>> 1;
				  if (sums[mid] < valueTarget) k = mid + 1;
				  else hi = mid;
				}

				var valueLeft = sums[k] - valueOffset,
					valueRight = value - valueLeft;

				if ((y1 - y0) > (x1 - x0)) {
				  var yk = (y0 * valueRight + y1 * valueLeft) / value;
				  partition(i, k, valueLeft, x0, y0, x1, yk);
				  partition(k, j, valueRight, x0, yk, x1, y1);
				} else {
				  var xk = (x0 * valueRight + x1 * valueLeft) / value;
				  partition(i, k, valueLeft, x0, y0, xk, y1);
				  partition(k, j, valueRight, xk, y0, x1, y1);
				}
			  }
			};

			var sliceDice = function(parent, x0, y0, x1, y1) {
			  (parent.depth & 1 ? treemapSlice : treemapDice)(parent, x0, y0, x1, y1);
			};

			var resquarify = (function custom(ratio) {

			  function resquarify(parent, x0, y0, x1, y1) {
				if ((rows = parent._squarify) && (rows.ratio === ratio)) {
				  var rows,
					  row,
					  nodes,
					  i,
					  j = -1,
					  n,
					  m = rows.length,
					  value = parent.value;

				  while (++j < m) {
					row = rows[j], nodes = row.children;
					for (i = row.value = 0, n = nodes.length; i < n; ++i) row.value += nodes[i].value;
					if (row.dice) treemapDice(row, x0, y0, x1, y0 += (y1 - y0) * row.value / value);
					else treemapSlice(row, x0, y0, x0 += (x1 - x0) * row.value / value, y1);
					value -= row.value;
				  }
				} else {
				  parent._squarify = rows = squarifyRatio(ratio, parent, x0, y0, x1, y1);
				  rows.ratio = ratio;
				}
			  }

			  resquarify.ratio = function(x) {
				return custom((x = +x) > 1 ? x : 1);
			  };

			  return resquarify;
			})(phi);

			var center$1 = function(x, y) {
			  var nodes;

			  if (x == null) x = 0;
			  if (y == null) y = 0;

			  function force() {
				var i,
					n = nodes.length,
					node,
					sx = 0,
					sy = 0;

				for (i = 0; i < n; ++i) {
				  node = nodes[i], sx += node.x, sy += node.y;
				}

				for (sx = sx / n - x, sy = sy / n - y, i = 0; i < n; ++i) {
				  node = nodes[i], node.x -= sx, node.y -= sy;
				}
			  }

			  force.initialize = function(_) {
				nodes = _;
			  };

			  force.x = function(_) {
				return arguments.length ? (x = +_, force) : x;
			  };

			  force.y = function(_) {
				return arguments.length ? (y = +_, force) : y;
			  };

			  return force;
			};

			var constant$7 = function(x) {
			  return function() {
				return x;
			  };
			};

			var jiggle = function() {
			  return (Math.random() - 0.5) * 1e-6;
			};

			function x$1(d) {
			  return d.x + d.vx;
			}

			function y$1(d) {
			  return d.y + d.vy;
			}

			var collide = function(radius) {
			  var nodes,
				  radii,
				  strength = 1,
				  iterations = 1;

			  if (typeof radius !== "function") radius = constant$7(radius == null ? 1 : +radius);

			  function force() {
				var i, n = nodes.length,
					tree,
					node,
					xi,
					yi,
					ri,
					ri2;

				for (var k = 0; k < iterations; ++k) {
				  tree = quadtree(nodes, x$1, y$1).visitAfter(prepare);
				  for (i = 0; i < n; ++i) {
					node = nodes[i];
					ri = radii[i], ri2 = ri * ri;
					xi = node.x + node.vx;
					yi = node.y + node.vy;
					tree.visit(apply);
				  }
				}

				function apply(quad, x0, y0, x1, y1) {
				  var data = quad.data, rj = quad.r, r = ri + rj;
				  if (data) {
					if (data.index > i) {
					  var x = xi - data.x - data.vx,
						  y = yi - data.y - data.vy,
						  l = x * x + y * y;
					  if (l < r * r) {
						if (x === 0) x = jiggle(), l += x * x;
						if (y === 0) y = jiggle(), l += y * y;
						l = (r - (l = Math.sqrt(l))) / l * strength;
						node.vx += (x *= l) * (r = (rj *= rj) / (ri2 + rj));
						node.vy += (y *= l) * r;
						data.vx -= x * (r = 1 - r);
						data.vy -= y * r;
					  }
					}
					return;
				  }
				  return x0 > xi + r || x1 < xi - r || y0 > yi + r || y1 < yi - r;
				}
			  }

			  function prepare(quad) {
				if (quad.data) return quad.r = radii[quad.data.index];
				for (var i = quad.r = 0; i < 4; ++i) {
				  if (quad[i] && quad[i].r > quad.r) {
					quad.r = quad[i].r;
				  }
				}
			  }

			  function initialize() {
				if (!nodes) return;
				var i, n = nodes.length;
				radii = new Array(n);
				for (i = 0; i < n; ++i) radii[i] = +radius(nodes[i], i, nodes);
			  }

			  force.initialize = function(_) {
				nodes = _;
				initialize();
			  };

			  force.iterations = function(_) {
				return arguments.length ? (iterations = +_, force) : iterations;
			  };

			  force.strength = function(_) {
				return arguments.length ? (strength = +_, force) : strength;
			  };

			  force.radius = function(_) {
				return arguments.length ? (radius = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : radius;
			  };

			  return force;
			};

			function index$2(d, i) {
			  return i;
			}

			function find(nodeById, nodeId) {
			  var node = nodeById.get(nodeId);
			  if (!node) throw new Error("missing: " + nodeId);
			  return node;
			}

			var link = function(links) {
			  var id = index$2,
				  strength = defaultStrength,
				  strengths,
				  distance = constant$7(30),
				  distances,
				  nodes,
				  count,
				  bias,
				  iterations = 1;

			  if (links == null) links = [];

			  function defaultStrength(link) {
				return 1 / Math.min(count[link.source.index], count[link.target.index]);
			  }

			  function force(alpha) {
				for (var k = 0, n = links.length; k < iterations; ++k) {
				  for (var i = 0, link, source, target, x, y, l, b; i < n; ++i) {
					link = links[i], source = link.source, target = link.target;
					x = target.x + target.vx - source.x - source.vx || jiggle();
					y = target.y + target.vy - source.y - source.vy || jiggle();
					l = Math.sqrt(x * x + y * y);
					l = (l - distances[i]) / l * alpha * strengths[i];
					x *= l, y *= l;
					target.vx -= x * (b = bias[i]);
					target.vy -= y * b;
					source.vx += x * (b = 1 - b);
					source.vy += y * b;
				  }
				}
			  }

			  function initialize() {
				if (!nodes) return;

				var i,
					n = nodes.length,
					m = links.length,
					nodeById = map$1(nodes, id),
					link;

				for (i = 0, count = new Array(n); i < n; ++i) {
				  count[i] = 0;
				}

				for (i = 0; i < m; ++i) {
				  link = links[i], link.index = i;
				  if (typeof link.source !== "object") link.source = find(nodeById, link.source);
				  if (typeof link.target !== "object") link.target = find(nodeById, link.target);
				  ++count[link.source.index], ++count[link.target.index];
				}

				for (i = 0, bias = new Array(m); i < m; ++i) {
				  link = links[i], bias[i] = count[link.source.index] / (count[link.source.index] + count[link.target.index]);
				}

				strengths = new Array(m), initializeStrength();
				distances = new Array(m), initializeDistance();
			  }

			  function initializeStrength() {
				if (!nodes) return;

				for (var i = 0, n = links.length; i < n; ++i) {
				  strengths[i] = +strength(links[i], i, links);
				}
			  }

			  function initializeDistance() {
				if (!nodes) return;

				for (var i = 0, n = links.length; i < n; ++i) {
				  distances[i] = +distance(links[i], i, links);
				}
			  }

			  force.initialize = function(_) {
				nodes = _;
				initialize();
			  };

			  force.links = function(_) {
				return arguments.length ? (links = _, initialize(), force) : links;
			  };

			  force.id = function(_) {
				return arguments.length ? (id = _, force) : id;
			  };

			  force.iterations = function(_) {
				return arguments.length ? (iterations = +_, force) : iterations;
			  };

			  force.strength = function(_) {
				return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initializeStrength(), force) : strength;
			  };

			  force.distance = function(_) {
				return arguments.length ? (distance = typeof _ === "function" ? _ : constant$7(+_), initializeDistance(), force) : distance;
			  };

			  return force;
			};

			function x$2(d) {
			  return d.x;
			}

			function y$2(d) {
			  return d.y;
			}

			var initialRadius = 10;
			var initialAngle = Math.PI * (3 - Math.sqrt(5));

			var simulation = function(nodes) {
			  var simulation,
				  alpha = 1,
				  alphaMin = 0.001,
				  alphaDecay = 1 - Math.pow(alphaMin, 1 / 300),
				  alphaTarget = 0,
				  velocityDecay = 0.6,
				  forces = map$1(),
				  stepper = timer(step),
				  event = dispatch("tick", "end");

			  if (nodes == null) nodes = [];

			  function step() {
				tick();
				event.call("tick", simulation);
				if (alpha < alphaMin) {
				  stepper.stop();
				  event.call("end", simulation);
				}
			  }

			  function tick() {
				var i, n = nodes.length, node;

				alpha += (alphaTarget - alpha) * alphaDecay;

				forces.each(function(force) {
				  force(alpha);
				});

				for (i = 0; i < n; ++i) {
				  node = nodes[i];
				  if (node.fx == null) node.x += node.vx *= velocityDecay;
				  else node.x = node.fx, node.vx = 0;
				  if (node.fy == null) node.y += node.vy *= velocityDecay;
				  else node.y = node.fy, node.vy = 0;
				}
			  }

			  function initializeNodes() {
				for (var i = 0, n = nodes.length, node; i < n; ++i) {
				  node = nodes[i], node.index = i;
				  if (isNaN(node.x) || isNaN(node.y)) {
					var radius = initialRadius * Math.sqrt(i), angle = i * initialAngle;
					node.x = radius * Math.cos(angle);
					node.y = radius * Math.sin(angle);
				  }
				  if (isNaN(node.vx) || isNaN(node.vy)) {
					node.vx = node.vy = 0;
				  }
				}
			  }

			  function initializeForce(force) {
				if (force.initialize) force.initialize(nodes);
				return force;
			  }

			  initializeNodes();

			  return simulation = {
				tick: tick,

				restart: function() {
				  return stepper.restart(step), simulation;
				},

				stop: function() {
				  return stepper.stop(), simulation;
				},

				nodes: function(_) {
				  return arguments.length ? (nodes = _, initializeNodes(), forces.each(initializeForce), simulation) : nodes;
				},

				alpha: function(_) {
				  return arguments.length ? (alpha = +_, simulation) : alpha;
				},

				alphaMin: function(_) {
				  return arguments.length ? (alphaMin = +_, simulation) : alphaMin;
				},

				alphaDecay: function(_) {
				  return arguments.length ? (alphaDecay = +_, simulation) : +alphaDecay;
				},

				alphaTarget: function(_) {
				  return arguments.length ? (alphaTarget = +_, simulation) : alphaTarget;
				},

				velocityDecay: function(_) {
				  return arguments.length ? (velocityDecay = 1 - _, simulation) : 1 - velocityDecay;
				},

				force: function(name, _) {
				  return arguments.length > 1 ? ((_ == null ? forces.remove(name) : forces.set(name, initializeForce(_))), simulation) : forces.get(name);
				},

				find: function(x, y, radius) {
				  var i = 0,
					  n = nodes.length,
					  dx,
					  dy,
					  d2,
					  node,
					  closest;

				  if (radius == null) radius = Infinity;
				  else radius *= radius;

				  for (i = 0; i < n; ++i) {
					node = nodes[i];
					dx = x - node.x;
					dy = y - node.y;
					d2 = dx * dx + dy * dy;
					if (d2 < radius) closest = node, radius = d2;
				  }

				  return closest;
				},

				on: function(name, _) {
				  return arguments.length > 1 ? (event.on(name, _), simulation) : event.on(name);
				}
			  };
			};

			var manyBody = function() {
			  var nodes,
				  node,
				  alpha,
				  strength = constant$7(-30),
				  strengths,
				  distanceMin2 = 1,
				  distanceMax2 = Infinity,
				  theta2 = 0.81;

			  function force(_) {
				var i, n = nodes.length, tree = quadtree(nodes, x$2, y$2).visitAfter(accumulate);
				for (alpha = _, i = 0; i < n; ++i) node = nodes[i], tree.visit(apply);
			  }

			  function initialize() {
				if (!nodes) return;
				var i, n = nodes.length;
				strengths = new Array(n);
				for (i = 0; i < n; ++i) strengths[i] = +strength(nodes[i], i, nodes);
			  }

			  function accumulate(quad) {
				var strength = 0, q, c, x$$1, y$$1, i;

				// For internal nodes, accumulate forces from child quadrants.
				if (quad.length) {
				  for (x$$1 = y$$1 = i = 0; i < 4; ++i) {
					if ((q = quad[i]) && (c = q.value)) {
					  strength += c, x$$1 += c * q.x, y$$1 += c * q.y;
					}
				  }
				  quad.x = x$$1 / strength;
				  quad.y = y$$1 / strength;
				}

				// For leaf nodes, accumulate forces from coincident quadrants.
				else {
				  q = quad;
				  q.x = q.data.x;
				  q.y = q.data.y;
				  do strength += strengths[q.data.index];
				  while (q = q.next);
				}

				quad.value = strength;
			  }

			  function apply(quad, x1, _, x2) {
				if (!quad.value) return true;

				var x$$1 = quad.x - node.x,
					y$$1 = quad.y - node.y,
					w = x2 - x1,
					l = x$$1 * x$$1 + y$$1 * y$$1;

				// Apply the Barnes-Hut approximation if possible.
				// Limit forces for very close nodes; randomize direction if coincident.
				if (w * w / theta2 < l) {
				  if (l < distanceMax2) {
					if (x$$1 === 0) x$$1 = jiggle(), l += x$$1 * x$$1;
					if (y$$1 === 0) y$$1 = jiggle(), l += y$$1 * y$$1;
					if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l);
					node.vx += x$$1 * quad.value * alpha / l;
					node.vy += y$$1 * quad.value * alpha / l;
				  }
				  return true;
				}

				// Otherwise, process points directly.
				else if (quad.length || l >= distanceMax2) return;

				// Limit forces for very close nodes; randomize direction if coincident.
				if (quad.data !== node || quad.next) {
				  if (x$$1 === 0) x$$1 = jiggle(), l += x$$1 * x$$1;
				  if (y$$1 === 0) y$$1 = jiggle(), l += y$$1 * y$$1;
				  if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l);
				}

				do if (quad.data !== node) {
				  w = strengths[quad.data.index] * alpha / l;
				  node.vx += x$$1 * w;
				  node.vy += y$$1 * w;
				} while (quad = quad.next);
			  }

			  force.initialize = function(_) {
				nodes = _;
				initialize();
			  };

			  force.strength = function(_) {
				return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : strength;
			  };

			  force.distanceMin = function(_) {
				return arguments.length ? (distanceMin2 = _ * _, force) : Math.sqrt(distanceMin2);
			  };

			  force.distanceMax = function(_) {
				return arguments.length ? (distanceMax2 = _ * _, force) : Math.sqrt(distanceMax2);
			  };

			  force.theta = function(_) {
				return arguments.length ? (theta2 = _ * _, force) : Math.sqrt(theta2);
			  };

			  return force;
			};

			var x$3 = function(x) {
			  var strength = constant$7(0.1),
				  nodes,
				  strengths,
				  xz;

			  if (typeof x !== "function") x = constant$7(x == null ? 0 : +x);

			  function force(alpha) {
				for (var i = 0, n = nodes.length, node; i < n; ++i) {
				  node = nodes[i], node.vx += (xz[i] - node.x) * strengths[i] * alpha;
				}
			  }

			  function initialize() {
				if (!nodes) return;
				var i, n = nodes.length;
				strengths = new Array(n);
				xz = new Array(n);
				for (i = 0; i < n; ++i) {
				  strengths[i] = isNaN(xz[i] = +x(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);
				}
			  }

			  force.initialize = function(_) {
				nodes = _;
				initialize();
			  };

			  force.strength = function(_) {
				return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : strength;
			  };

			  force.x = function(_) {
				return arguments.length ? (x = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : x;
			  };

			  return force;
			};

			var y$3 = function(y) {
			  var strength = constant$7(0.1),
				  nodes,
				  strengths,
				  yz;

			  if (typeof y !== "function") y = constant$7(y == null ? 0 : +y);

			  function force(alpha) {
				for (var i = 0, n = nodes.length, node; i < n; ++i) {
				  node = nodes[i], node.vy += (yz[i] - node.y) * strengths[i] * alpha;
				}
			  }

			  function initialize() {
				if (!nodes) return;
				var i, n = nodes.length;
				strengths = new Array(n);
				yz = new Array(n);
				for (i = 0; i < n; ++i) {
				  strengths[i] = isNaN(yz[i] = +y(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);
				}
			  }

			  force.initialize = function(_) {
				nodes = _;
				initialize();
			  };

			  force.strength = function(_) {
				return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : strength;
			  };

			  force.y = function(_) {
				return arguments.length ? (y = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : y;
			  };

			  return force;
			};

			function nopropagation() {
			  exports.event.stopImmediatePropagation();
			}

			var noevent = function() {
			  exports.event.preventDefault();
			  exports.event.stopImmediatePropagation();
			};

			var dragDisable = function(view) {
			  var root = view.document.documentElement,
				  selection$$1 = select(view).on("dragstart.drag", noevent, true);
			  if ("onselectstart" in root) {
				selection$$1.on("selectstart.drag", noevent, true);
			  } else {
				root.__noselect = root.style.MozUserSelect;
				root.style.MozUserSelect = "none";
			  }
			};

			function yesdrag(view, noclick) {
			  var root = view.document.documentElement,
				  selection$$1 = select(view).on("dragstart.drag", null);
			  if (noclick) {
				selection$$1.on("click.drag", noevent, true);
				setTimeout(function() { selection$$1.on("click.drag", null); }, 0);
			  }
			  if ("onselectstart" in root) {
				selection$$1.on("selectstart.drag", null);
			  } else {
				root.style.MozUserSelect = root.__noselect;
				delete root.__noselect;
			  }
			}

			var constant$8 = function(x) {
			  return function() {
				return x;
			  };
			};

			function DragEvent(target, type, subject, id, active, x, y, dx, dy, dispatch) {
			  this.target = target;
			  this.type = type;
			  this.subject = subject;
			  this.identifier = id;
			  this.active = active;
			  this.x = x;
			  this.y = y;
			  this.dx = dx;
			  this.dy = dy;
			  this._ = dispatch;
			}

			DragEvent.prototype.on = function() {
			  var value = this._.on.apply(this._, arguments);
			  return value === this._ ? this : value;
			};

			// Ignore right-click, since that should open the context menu.
			function defaultFilter() {
			  return !exports.event.button;
			}

			function defaultContainer() {
			  return this.parentNode;
			}

			function defaultSubject(d) {
			  return d == null ? {x: exports.event.x, y: exports.event.y} : d;
			}

			var drag = function() {
			  var filter = defaultFilter,
				  container = defaultContainer,
				  subject = defaultSubject,
				  gestures = {},
				  listeners = dispatch("start", "drag", "end"),
				  active = 0,
				  mousemoving,
				  touchending;

			  function drag(selection$$1) {
				selection$$1
					.on("mousedown.drag", mousedowned)
					.on("touchstart.drag", touchstarted)
					.on("touchmove.drag", touchmoved)
					.on("touchend.drag touchcancel.drag", touchended)
					.style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
			  }

			  function mousedowned() {
				if (touchending || !filter.apply(this, arguments)) return;
				var gesture = beforestart("mouse", container.apply(this, arguments), mouse, this, arguments);
				if (!gesture) return;
				select(exports.event.view).on("mousemove.drag", mousemoved, true).on("mouseup.drag", mouseupped, true);
				dragDisable(exports.event.view);
				nopropagation();
				mousemoving = false;
				gesture("start");
			  }

			  function mousemoved() {
				noevent();
				mousemoving = true;
				gestures.mouse("drag");
			  }

			  function mouseupped() {
				select(exports.event.view).on("mousemove.drag mouseup.drag", null);
				yesdrag(exports.event.view, mousemoving);
				noevent();
				gestures.mouse("end");
			  }

			  function touchstarted() {
				if (!filter.apply(this, arguments)) return;
				var touches$$1 = exports.event.changedTouches,
					c = container.apply(this, arguments),
					n = touches$$1.length, i, gesture;

				for (i = 0; i < n; ++i) {
				  if (gesture = beforestart(touches$$1[i].identifier, c, touch, this, arguments)) {
					nopropagation();
					gesture("start");
				  }
				}
			  }

			  function touchmoved() {
				var touches$$1 = exports.event.changedTouches,
					n = touches$$1.length, i, gesture;

				for (i = 0; i < n; ++i) {
				  if (gesture = gestures[touches$$1[i].identifier]) {
					noevent();
					gesture("drag");
				  }
				}
			  }

			  function touchended() {
				var touches$$1 = exports.event.changedTouches,
					n = touches$$1.length, i, gesture;

				if (touchending) clearTimeout(touchending);
				touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed!
				for (i = 0; i < n; ++i) {
				  if (gesture = gestures[touches$$1[i].identifier]) {
					nopropagation();
					gesture("end");
				  }
				}
			  }

			  function beforestart(id, container, point, that, args) {
				var p = point(container, id), s, dx, dy,
					sublisteners = listeners.copy();

				if (!customEvent(new DragEvent(drag, "beforestart", s, id, active, p[0], p[1], 0, 0, sublisteners), function() {
				  if ((exports.event.subject = s = subject.apply(that, args)) == null) return false;
				  dx = s.x - p[0] || 0;
				  dy = s.y - p[1] || 0;
				  return true;
				})) return;

				return function gesture(type) {
				  var p0 = p, n;
				  switch (type) {
					case "start": gestures[id] = gesture, n = active++; break;
					case "end": delete gestures[id], --active; // nobreak
					case "drag": p = point(container, id), n = active; break;
				  }
				  customEvent(new DragEvent(drag, type, s, id, n, p[0] + dx, p[1] + dy, p[0] - p0[0], p[1] - p0[1], sublisteners), sublisteners.apply, sublisteners, [type, that, args]);
				};
			  }

			  drag.filter = function(_) {
				return arguments.length ? (filter = typeof _ === "function" ? _ : constant$8(!!_), drag) : filter;
			  };

			  drag.container = function(_) {
				return arguments.length ? (container = typeof _ === "function" ? _ : constant$8(_), drag) : container;
			  };

			  drag.subject = function(_) {
				return arguments.length ? (subject = typeof _ === "function" ? _ : constant$8(_), drag) : subject;
			  };

			  drag.on = function() {
				var value = listeners.on.apply(listeners, arguments);
				return value === listeners ? drag : value;
			  };

			  return drag;
			};

			var constant$9 = function(x) {
			  return function() {
				return x;
			  };
			};

			function x$4(d) {
			  return d[0];
			}

			function y$4(d) {
			  return d[1];
			}

			function RedBlackTree() {
			  this._ = null; // root node
			}

			function RedBlackNode(node) {
			  node.U = // parent node
			  node.C = // color - true for red, false for black
			  node.L = // left node
			  node.R = // right node
			  node.P = // previous node
			  node.N = null; // next node
			}

			RedBlackTree.prototype = {
			  constructor: RedBlackTree,

			  insert: function(after, node) {
				var parent, grandpa, uncle;

				if (after) {
				  node.P = after;
				  node.N = after.N;
				  if (after.N) after.N.P = node;
				  after.N = node;
				  if (after.R) {
					after = after.R;
					while (after.L) after = after.L;
					after.L = node;
				  } else {
					after.R = node;
				  }
				  parent = after;
				} else if (this._) {
				  after = RedBlackFirst(this._);
				  node.P = null;
				  node.N = after;
				  after.P = after.L = node;
				  parent = after;
				} else {
				  node.P = node.N = null;
				  this._ = node;
				  parent = null;
				}
				node.L = node.R = null;
				node.U = parent;
				node.C = true;

				after = node;
				while (parent && parent.C) {
				  grandpa = parent.U;
				  if (parent === grandpa.L) {
					uncle = grandpa.R;
					if (uncle && uncle.C) {
					  parent.C = uncle.C = false;
					  grandpa.C = true;
					  after = grandpa;
					} else {
					  if (after === parent.R) {
						RedBlackRotateLeft(this, parent);
						after = parent;
						parent = after.U;
					  }
					  parent.C = false;
					  grandpa.C = true;
					  RedBlackRotateRight(this, grandpa);
					}
				  } else {
					uncle = grandpa.L;
					if (uncle && uncle.C) {
					  parent.C = uncle.C = false;
					  grandpa.C = true;
					  after = grandpa;
					} else {
					  if (after === parent.L) {
						RedBlackRotateRight(this, parent);
						after = parent;
						parent = after.U;
					  }
					  parent.C = false;
					  grandpa.C = true;
					  RedBlackRotateLeft(this, grandpa);
					}
				  }
				  parent = after.U;
				}
				this._.C = false;
			  },

			  remove: function(node) {
				if (node.N) node.N.P = node.P;
				if (node.P) node.P.N = node.N;
				node.N = node.P = null;

				var parent = node.U,
					sibling,
					left = node.L,
					right = node.R,
					next,
					red;

				if (!left) next = right;
				else if (!right) next = left;
				else next = RedBlackFirst(right);

				if (parent) {
				  if (parent.L === node) parent.L = next;
				  else parent.R = next;
				} else {
				  this._ = next;
				}

				if (left && right) {
				  red = next.C;
				  next.C = node.C;
				  next.L = left;
				  left.U = next;
				  if (next !== right) {
					parent = next.U;
					next.U = node.U;
					node = next.R;
					parent.L = node;
					next.R = right;
					right.U = next;
				  } else {
					next.U = parent;
					parent = next;
					node = next.R;
				  }
				} else {
				  red = node.C;
				  node = next;
				}

				if (node) node.U = parent;
				if (red) return;
				if (node && node.C) { node.C = false; return; }

				do {
				  if (node === this._) break;
				  if (node === parent.L) {
					sibling = parent.R;
					if (sibling.C) {
					  sibling.C = false;
					  parent.C = true;
					  RedBlackRotateLeft(this, parent);
					  sibling = parent.R;
					}
					if ((sibling.L && sibling.L.C)
						|| (sibling.R && sibling.R.C)) {
					  if (!sibling.R || !sibling.R.C) {
						sibling.L.C = false;
						sibling.C = true;
						RedBlackRotateRight(this, sibling);
						sibling = parent.R;
					  }
					  sibling.C = parent.C;
					  parent.C = sibling.R.C = false;
					  RedBlackRotateLeft(this, parent);
					  node = this._;
					  break;
					}
				  } else {
					sibling = parent.L;
					if (sibling.C) {
					  sibling.C = false;
					  parent.C = true;
					  RedBlackRotateRight(this, parent);
					  sibling = parent.L;
					}
					if ((sibling.L && sibling.L.C)
					  || (sibling.R && sibling.R.C)) {
					  if (!sibling.L || !sibling.L.C) {
						sibling.R.C = false;
						sibling.C = true;
						RedBlackRotateLeft(this, sibling);
						sibling = parent.L;
					  }
					  sibling.C = parent.C;
					  parent.C = sibling.L.C = false;
					  RedBlackRotateRight(this, parent);
					  node = this._;
					  break;
					}
				  }
				  sibling.C = true;
				  node = parent;
				  parent = parent.U;
				} while (!node.C);

				if (node) node.C = false;
			  }
			};

			function RedBlackRotateLeft(tree, node) {
			  var p = node,
				  q = node.R,
				  parent = p.U;

			  if (parent) {
				if (parent.L === p) parent.L = q;
				else parent.R = q;
			  } else {
				tree._ = q;
			  }

			  q.U = parent;
			  p.U = q;
			  p.R = q.L;
			  if (p.R) p.R.U = p;
			  q.L = p;
			}

			function RedBlackRotateRight(tree, node) {
			  var p = node,
				  q = node.L,
				  parent = p.U;

			  if (parent) {
				if (parent.L === p) parent.L = q;
				else parent.R = q;
			  } else {
				tree._ = q;
			  }

			  q.U = parent;
			  p.U = q;
			  p.L = q.R;
			  if (p.L) p.L.U = p;
			  q.R = p;
			}

			function RedBlackFirst(node) {
			  while (node.L) node = node.L;
			  return node;
			}

			function createEdge(left, right, v0, v1) {
			  var edge = [null, null],
				  index = edges.push(edge) - 1;
			  edge.left = left;
			  edge.right = right;
			  if (v0) setEdgeEnd(edge, left, right, v0);
			  if (v1) setEdgeEnd(edge, right, left, v1);
			  cells[left.index].halfedges.push(index);
			  cells[right.index].halfedges.push(index);
			  return edge;
			}

			function createBorderEdge(left, v0, v1) {
			  var edge = [v0, v1];
			  edge.left = left;
			  return edge;
			}

			function setEdgeEnd(edge, left, right, vertex) {
			  if (!edge[0] && !edge[1]) {
				edge[0] = vertex;
				edge.left = left;
				edge.right = right;
			  } else if (edge.left === right) {
				edge[1] = vertex;
			  } else {
				edge[0] = vertex;
			  }
			}

			// Liang–Barsky line clipping.
			function clipEdge(edge, x0, y0, x1, y1) {
			  var a = edge[0],
				  b = edge[1],
				  ax = a[0],
				  ay = a[1],
				  bx = b[0],
				  by = b[1],
				  t0 = 0,
				  t1 = 1,
				  dx = bx - ax,
				  dy = by - ay,
				  r;

			  r = x0 - ax;
			  if (!dx && r > 0) return;
			  r /= dx;
			  if (dx < 0) {
				if (r < t0) return;
				if (r < t1) t1 = r;
			  } else if (dx > 0) {
				if (r > t1) return;
				if (r > t0) t0 = r;
			  }

			  r = x1 - ax;
			  if (!dx && r < 0) return;
			  r /= dx;
			  if (dx < 0) {
				if (r > t1) return;
				if (r > t0) t0 = r;
			  } else if (dx > 0) {
				if (r < t0) return;
				if (r < t1) t1 = r;
			  }

			  r = y0 - ay;
			  if (!dy && r > 0) return;
			  r /= dy;
			  if (dy < 0) {
				if (r < t0) return;
				if (r < t1) t1 = r;
			  } else if (dy > 0) {
				if (r > t1) return;
				if (r > t0) t0 = r;
			  }

			  r = y1 - ay;
			  if (!dy && r < 0) return;
			  r /= dy;
			  if (dy < 0) {
				if (r > t1) return;
				if (r > t0) t0 = r;
			  } else if (dy > 0) {
				if (r < t0) return;
				if (r < t1) t1 = r;
			  }

			  if (!(t0 > 0) && !(t1 < 1)) return true; // TODO Better check?

			  if (t0 > 0) edge[0] = [ax + t0 * dx, ay + t0 * dy];
			  if (t1 < 1) edge[1] = [ax + t1 * dx, ay + t1 * dy];
			  return true;
			}

			function connectEdge(edge, x0, y0, x1, y1) {
			  var v1 = edge[1];
			  if (v1) return true;

			  var v0 = edge[0],
				  left = edge.left,
				  right = edge.right,
				  lx = left[0],
				  ly = left[1],
				  rx = right[0],
				  ry = right[1],
				  fx = (lx + rx) / 2,
				  fy = (ly + ry) / 2,
				  fm,
				  fb;

			  if (ry === ly) {
				if (fx < x0 || fx >= x1) return;
				if (lx > rx) {
				  if (!v0) v0 = [fx, y0];
				  else if (v0[1] >= y1) return;
				  v1 = [fx, y1];
				} else {
				  if (!v0) v0 = [fx, y1];
				  else if (v0[1] < y0) return;
				  v1 = [fx, y0];
				}
			  } else {
				fm = (lx - rx) / (ry - ly);
				fb = fy - fm * fx;
				if (fm < -1 || fm > 1) {
				  if (lx > rx) {
					if (!v0) v0 = [(y0 - fb) / fm, y0];
					else if (v0[1] >= y1) return;
					v1 = [(y1 - fb) / fm, y1];
				  } else {
					if (!v0) v0 = [(y1 - fb) / fm, y1];
					else if (v0[1] < y0) return;
					v1 = [(y0 - fb) / fm, y0];
				  }
				} else {
				  if (ly < ry) {
					if (!v0) v0 = [x0, fm * x0 + fb];
					else if (v0[0] >= x1) return;
					v1 = [x1, fm * x1 + fb];
				  } else {
					if (!v0) v0 = [x1, fm * x1 + fb];
					else if (v0[0] < x0) return;
					v1 = [x0, fm * x0 + fb];
				  }
				}
			  }

			  edge[0] = v0;
			  edge[1] = v1;
			  return true;
			}

			function clipEdges(x0, y0, x1, y1) {
			  var i = edges.length,
				  edge;

			  while (i--) {
				if (!connectEdge(edge = edges[i], x0, y0, x1, y1)
					|| !clipEdge(edge, x0, y0, x1, y1)
					|| !(Math.abs(edge[0][0] - edge[1][0]) > epsilon$3
						|| Math.abs(edge[0][1] - edge[1][1]) > epsilon$3)) {
				  delete edges[i];
				}
			  }
			}

			function createCell(site) {
			  return cells[site.index] = {
				site: site,
				halfedges: []
			  };
			}

			function cellHalfedgeAngle(cell, edge) {
			  var site = cell.site,
				  va = edge.left,
				  vb = edge.right;
			  if (site === vb) vb = va, va = site;
			  if (vb) return Math.atan2(vb[1] - va[1], vb[0] - va[0]);
			  if (site === va) va = edge[1], vb = edge[0];
			  else va = edge[0], vb = edge[1];
			  return Math.atan2(va[0] - vb[0], vb[1] - va[1]);
			}

			function cellHalfedgeStart(cell, edge) {
			  return edge[+(edge.left !== cell.site)];
			}

			function cellHalfedgeEnd(cell, edge) {
			  return edge[+(edge.left === cell.site)];
			}

			function sortCellHalfedges() {
			  for (var i = 0, n = cells.length, cell, halfedges, j, m; i < n; ++i) {
				if ((cell = cells[i]) && (m = (halfedges = cell.halfedges).length)) {
				  var index = new Array(m),
					  array = new Array(m);
				  for (j = 0; j < m; ++j) index[j] = j, array[j] = cellHalfedgeAngle(cell, edges[halfedges[j]]);
				  index.sort(function(i, j) { return array[j] - array[i]; });
				  for (j = 0; j < m; ++j) array[j] = halfedges[index[j]];
				  for (j = 0; j < m; ++j) halfedges[j] = array[j];
				}
			  }
			}

			function clipCells(x0, y0, x1, y1) {
			  var nCells = cells.length,
				  iCell,
				  cell,
				  site,
				  iHalfedge,
				  halfedges,
				  nHalfedges,
				  start,
				  startX,
				  startY,
				  end,
				  endX,
				  endY,
				  cover = true;

			  for (iCell = 0; iCell < nCells; ++iCell) {
				if (cell = cells[iCell]) {
				  site = cell.site;
				  halfedges = cell.halfedges;
				  iHalfedge = halfedges.length;

				  // Remove any dangling clipped edges.
				  while (iHalfedge--) {
					if (!edges[halfedges[iHalfedge]]) {
					  halfedges.splice(iHalfedge, 1);
					}
				  }

				  // Insert any border edges as necessary.
				  iHalfedge = 0, nHalfedges = halfedges.length;
				  while (iHalfedge < nHalfedges) {
					end = cellHalfedgeEnd(cell, edges[halfedges[iHalfedge]]), endX = end[0], endY = end[1];
					start = cellHalfedgeStart(cell, edges[halfedges[++iHalfedge % nHalfedges]]), startX = start[0], startY = start[1];
					if (Math.abs(endX - startX) > epsilon$3 || Math.abs(endY - startY) > epsilon$3) {
					  halfedges.splice(iHalfedge, 0, edges.push(createBorderEdge(site, end,
						  Math.abs(endX - x0) < epsilon$3 && y1 - endY > epsilon$3 ? [x0, Math.abs(startX - x0) < epsilon$3 ? startY : y1]
						  : Math.abs(endY - y1) < epsilon$3 && x1 - endX > epsilon$3 ? [Math.abs(startY - y1) < epsilon$3 ? startX : x1, y1]
						  : Math.abs(endX - x1) < epsilon$3 && endY - y0 > epsilon$3 ? [x1, Math.abs(startX - x1) < epsilon$3 ? startY : y0]
						  : Math.abs(endY - y0) < epsilon$3 && endX - x0 > epsilon$3 ? [Math.abs(startY - y0) < epsilon$3 ? startX : x0, y0]
						  : null)) - 1);
					  ++nHalfedges;
					}
				  }

				  if (nHalfedges) cover = false;
				}
			  }

			  // If there weren’t any edges, have the closest site cover the extent.
			  // It doesn’t matter which corner of the extent we measure!
			  if (cover) {
				var dx, dy, d2, dc = Infinity;

				for (iCell = 0, cover = null; iCell < nCells; ++iCell) {
				  if (cell = cells[iCell]) {
					site = cell.site;
					dx = site[0] - x0;
					dy = site[1] - y0;
					d2 = dx * dx + dy * dy;
					if (d2 < dc) dc = d2, cover = cell;
				  }
				}

				if (cover) {
				  var v00 = [x0, y0], v01 = [x0, y1], v11 = [x1, y1], v10 = [x1, y0];
				  cover.halfedges.push(
					edges.push(createBorderEdge(site = cover.site, v00, v01)) - 1,
					edges.push(createBorderEdge(site, v01, v11)) - 1,
					edges.push(createBorderEdge(site, v11, v10)) - 1,
					edges.push(createBorderEdge(site, v10, v00)) - 1
				  );
				}
			  }

			  // Lastly delete any cells with no edges; these were entirely clipped.
			  for (iCell = 0; iCell < nCells; ++iCell) {
				if (cell = cells[iCell]) {
				  if (!cell.halfedges.length) {
					delete cells[iCell];
				  }
				}
			  }
			}

			var circlePool = [];

			var firstCircle;

			function Circle() {
			  RedBlackNode(this);
			  this.x =
			  this.y =
			  this.arc =
			  this.site =
			  this.cy = null;
			}

			function attachCircle(arc) {
			  var lArc = arc.P,
				  rArc = arc.N;

			  if (!lArc || !rArc) return;

			  var lSite = lArc.site,
				  cSite = arc.site,
				  rSite = rArc.site;

			  if (lSite === rSite) return;

			  var bx = cSite[0],
				  by = cSite[1],
				  ax = lSite[0] - bx,
				  ay = lSite[1] - by,
				  cx = rSite[0] - bx,
				  cy = rSite[1] - by;

			  var d = 2 * (ax * cy - ay * cx);
			  if (d >= -epsilon2$1) return;

			  var ha = ax * ax + ay * ay,
				  hc = cx * cx + cy * cy,
				  x = (cy * ha - ay * hc) / d,
				  y = (ax * hc - cx * ha) / d;

			  var circle = circlePool.pop() || new Circle;
			  circle.arc = arc;
			  circle.site = cSite;
			  circle.x = x + bx;
			  circle.y = (circle.cy = y + by) + Math.sqrt(x * x + y * y); // y bottom

			  arc.circle = circle;

			  var before = null,
				  node = circles._;

			  while (node) {
				if (circle.y < node.y || (circle.y === node.y && circle.x <= node.x)) {
				  if (node.L) node = node.L;
				  else { before = node.P; break; }
				} else {
				  if (node.R) node = node.R;
				  else { before = node; break; }
				}
			  }

			  circles.insert(before, circle);
			  if (!before) firstCircle = circle;
			}

			function detachCircle(arc) {
			  var circle = arc.circle;
			  if (circle) {
				if (!circle.P) firstCircle = circle.N;
				circles.remove(circle);
				circlePool.push(circle);
				RedBlackNode(circle);
				arc.circle = null;
			  }
			}

			var beachPool = [];

			function Beach() {
			  RedBlackNode(this);
			  this.edge =
			  this.site =
			  this.circle = null;
			}

			function createBeach(site) {
			  var beach = beachPool.pop() || new Beach;
			  beach.site = site;
			  return beach;
			}

			function detachBeach(beach) {
			  detachCircle(beach);
			  beaches.remove(beach);
			  beachPool.push(beach);
			  RedBlackNode(beach);
			}

			function removeBeach(beach) {
			  var circle = beach.circle,
				  x = circle.x,
				  y = circle.cy,
				  vertex = [x, y],
				  previous = beach.P,
				  next = beach.N,
				  disappearing = [beach];

			  detachBeach(beach);

			  var lArc = previous;
			  while (lArc.circle
				  && Math.abs(x - lArc.circle.x) < epsilon$3
				  && Math.abs(y - lArc.circle.cy) < epsilon$3) {
				previous = lArc.P;
				disappearing.unshift(lArc);
				detachBeach(lArc);
				lArc = previous;
			  }

			  disappearing.unshift(lArc);
			  detachCircle(lArc);

			  var rArc = next;
			  while (rArc.circle
				  && Math.abs(x - rArc.circle.x) < epsilon$3
				  && Math.abs(y - rArc.circle.cy) < epsilon$3) {
				next = rArc.N;
				disappearing.push(rArc);
				detachBeach(rArc);
				rArc = next;
			  }

			  disappearing.push(rArc);
			  detachCircle(rArc);

			  var nArcs = disappearing.length,
				  iArc;
			  for (iArc = 1; iArc < nArcs; ++iArc) {
				rArc = disappearing[iArc];
				lArc = disappearing[iArc - 1];
				setEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex);
			  }

			  lArc = disappearing[0];
			  rArc = disappearing[nArcs - 1];
			  rArc.edge = createEdge(lArc.site, rArc.site, null, vertex);

			  attachCircle(lArc);
			  attachCircle(rArc);
			}

			function addBeach(site) {
			  var x = site[0],
				  directrix = site[1],
				  lArc,
				  rArc,
				  dxl,
				  dxr,
				  node = beaches._;

			  while (node) {
				dxl = leftBreakPoint(node, directrix) - x;
				if (dxl > epsilon$3) node = node.L; else {
				  dxr = x - rightBreakPoint(node, directrix);
				  if (dxr > epsilon$3) {
					if (!node.R) {
					  lArc = node;
					  break;
					}
					node = node.R;
				  } else {
					if (dxl > -epsilon$3) {
					  lArc = node.P;
					  rArc = node;
					} else if (dxr > -epsilon$3) {
					  lArc = node;
					  rArc = node.N;
					} else {
					  lArc = rArc = node;
					}
					break;
				  }
				}
			  }

			  createCell(site);
			  var newArc = createBeach(site);
			  beaches.insert(lArc, newArc);

			  if (!lArc && !rArc) return;

			  if (lArc === rArc) {
				detachCircle(lArc);
				rArc = createBeach(lArc.site);
				beaches.insert(newArc, rArc);
				newArc.edge = rArc.edge = createEdge(lArc.site, newArc.site);
				attachCircle(lArc);
				attachCircle(rArc);
				return;
			  }

			  if (!rArc) { // && lArc
				newArc.edge = createEdge(lArc.site, newArc.site);
				return;
			  }

			  // else lArc !== rArc
			  detachCircle(lArc);
			  detachCircle(rArc);

			  var lSite = lArc.site,
				  ax = lSite[0],
				  ay = lSite[1],
				  bx = site[0] - ax,
				  by = site[1] - ay,
				  rSite = rArc.site,
				  cx = rSite[0] - ax,
				  cy = rSite[1] - ay,
				  d = 2 * (bx * cy - by * cx),
				  hb = bx * bx + by * by,
				  hc = cx * cx + cy * cy,
				  vertex = [(cy * hb - by * hc) / d + ax, (bx * hc - cx * hb) / d + ay];

			  setEdgeEnd(rArc.edge, lSite, rSite, vertex);
			  newArc.edge = createEdge(lSite, site, null, vertex);
			  rArc.edge = createEdge(site, rSite, null, vertex);
			  attachCircle(lArc);
			  attachCircle(rArc);
			}

			function leftBreakPoint(arc, directrix) {
			  var site = arc.site,
				  rfocx = site[0],
				  rfocy = site[1],
				  pby2 = rfocy - directrix;

			  if (!pby2) return rfocx;

			  var lArc = arc.P;
			  if (!lArc) return -Infinity;

			  site = lArc.site;
			  var lfocx = site[0],
				  lfocy = site[1],
				  plby2 = lfocy - directrix;

			  if (!plby2) return lfocx;

			  var hl = lfocx - rfocx,
				  aby2 = 1 / pby2 - 1 / plby2,
				  b = hl / plby2;

			  if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx;

			  return (rfocx + lfocx) / 2;
			}

			function rightBreakPoint(arc, directrix) {
			  var rArc = arc.N;
			  if (rArc) return leftBreakPoint(rArc, directrix);
			  var site = arc.site;
			  return site[1] === directrix ? site[0] : Infinity;
			}

			var epsilon$3 = 1e-6;
			var epsilon2$1 = 1e-12;
			var beaches;
			var cells;
			var circles;
			var edges;

			function triangleArea(a, b, c) {
			  return (a[0] - c[0]) * (b[1] - a[1]) - (a[0] - b[0]) * (c[1] - a[1]);
			}

			function lexicographic(a, b) {
			  return b[1] - a[1]
				  || b[0] - a[0];
			}

			function Diagram(sites, extent) {
			  var site = sites.sort(lexicographic).pop(),
				  x,
				  y,
				  circle;

			  edges = [];
			  cells = new Array(sites.length);
			  beaches = new RedBlackTree;
			  circles = new RedBlackTree;

			  while (true) {
				circle = firstCircle;
				if (site && (!circle || site[1] < circle.y || (site[1] === circle.y && site[0] < circle.x))) {
				  if (site[0] !== x || site[1] !== y) {
					addBeach(site);
					x = site[0], y = site[1];
				  }
				  site = sites.pop();
				} else if (circle) {
				  removeBeach(circle.arc);
				} else {
				  break;
				}
			  }

			  sortCellHalfedges();

			  if (extent) {
				var x0 = +extent[0][0],
					y0 = +extent[0][1],
					x1 = +extent[1][0],
					y1 = +extent[1][1];
				clipEdges(x0, y0, x1, y1);
				clipCells(x0, y0, x1, y1);
			  }

			  this.edges = edges;
			  this.cells = cells;

			  beaches =
			  circles =
			  edges =
			  cells = null;
			}

			Diagram.prototype = {
			  constructor: Diagram,

			  polygons: function() {
				var edges = this.edges;

				return this.cells.map(function(cell) {
				  var polygon = cell.halfedges.map(function(i) { return cellHalfedgeStart(cell, edges[i]); });
				  polygon.data = cell.site.data;
				  return polygon;
				});
			  },

			  triangles: function() {
				var triangles = [],
					edges = this.edges;

				this.cells.forEach(function(cell, i) {
				  var site = cell.site,
					  halfedges = cell.halfedges,
					  j = -1,
					  m = halfedges.length,
					  s0,
					  e1 = edges[halfedges[m - 1]],
					  s1 = e1.left === site ? e1.right : e1.left;

				  while (++j < m) {
					s0 = s1;
					e1 = edges[halfedges[j]];
					s1 = e1.left === site ? e1.right : e1.left;
					if (s0 && s1 && i < s0.index && i < s1.index && triangleArea(site, s0, s1) < 0) {
					  triangles.push([site.data, s0.data, s1.data]);
					}
				  }
				});

				return triangles;
			  },

			  links: function() {
				return this.edges.filter(function(edge) {
				  return edge.right;
				}).map(function(edge) {
				  return {
					source: edge.left.data,
					target: edge.right.data
				  };
				});
			  },

			  find: function(x, y, radius) {
				var that = this,
					i0, i1 = that._found || 0,
					cell = that.cells[i1] || that.cells[i1 = 0],
					dx = x - cell.site[0],
					dy = y - cell.site[1],
					d2 = dx * dx + dy * dy;

				do {
				  cell = that.cells[i0 = i1], i1 = null;
				  cell.halfedges.forEach(function(e) {
					var edge = that.edges[e], v = edge.left;
					if ((v === cell.site || !v) && !(v = edge.right)) return;
					var vx = x - v[0],
						vy = y - v[1],
						v2 = vx * vx + vy * vy;
					if (v2 < d2) d2 = v2, i1 = v.index;
				  });
				} while (i1 !== null);

				that._found = i0;

				return radius == null || d2 <= radius * radius ? cell.site : null;
			  }
			};

			var voronoi = function() {
			  var x$$1 = x$4,
				  y$$1 = y$4,
				  extent = null;

			  function voronoi(data) {
				return new Diagram(data.map(function(d, i) {
				  var s = [Math.round(x$$1(d, i, data) / epsilon$3) * epsilon$3, Math.round(y$$1(d, i, data) / epsilon$3) * epsilon$3];
				  s.index = i;
				  s.data = d;
				  return s;
				}), extent);
			  }

			  voronoi.polygons = function(data) {
				return voronoi(data).polygons();
			  };

			  voronoi.links = function(data) {
				return voronoi(data).links();
			  };

			  voronoi.triangles = function(data) {
				return voronoi(data).triangles();
			  };

			  voronoi.x = function(_) {
				return arguments.length ? (x$$1 = typeof _ === "function" ? _ : constant$9(+_), voronoi) : x$$1;
			  };

			  voronoi.y = function(_) {
				return arguments.length ? (y$$1 = typeof _ === "function" ? _ : constant$9(+_), voronoi) : y$$1;
			  };

			  voronoi.extent = function(_) {
				return arguments.length ? (extent = _ == null ? null : [[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]], voronoi) : extent && [[extent[0][0], extent[0][1]], [extent[1][0], extent[1][1]]];
			  };

			  voronoi.size = function(_) {
				return arguments.length ? (extent = _ == null ? null : [[0, 0], [+_[0], +_[1]]], voronoi) : extent && [extent[1][0] - extent[0][0], extent[1][1] - extent[0][1]];
			  };

			  return voronoi;
			};

			var constant$10 = function(x) {
			  return function() {
				return x;
			  };
			};

			function ZoomEvent(target, type, transform) {
			  this.target = target;
			  this.type = type;
			  this.transform = transform;
			}

			function Transform(k, x, y) {
			  this.k = k;
			  this.x = x;
			  this.y = y;
			}

			Transform.prototype = {
			  constructor: Transform,
			  scale: function(k) {
				return k === 1 ? this : new Transform(this.k * k, this.x, this.y);
			  },
			  translate: function(x, y) {
				return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y);
			  },
			  apply: function(point) {
				return [point[0] * this.k + this.x, point[1] * this.k + this.y];
			  },
			  applyX: function(x) {
				return x * this.k + this.x;
			  },
			  applyY: function(y) {
				return y * this.k + this.y;
			  },
			  invert: function(location) {
				return [(location[0] - this.x) / this.k, (location[1] - this.y) / this.k];
			  },
			  invertX: function(x) {
				return (x - this.x) / this.k;
			  },
			  invertY: function(y) {
				return (y - this.y) / this.k;
			  },
			  rescaleX: function(x) {
				return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x));
			  },
			  rescaleY: function(y) {
				return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y));
			  },
			  toString: function() {
				return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")";
			  }
			};

			var identity$6 = new Transform(1, 0, 0);

			transform.prototype = Transform.prototype;

			function transform(node) {
			  return node.__zoom || identity$6;
			}

			function nopropagation$1() {
			  exports.event.stopImmediatePropagation();
			}

			var noevent$1 = function() {
			  exports.event.preventDefault();
			  exports.event.stopImmediatePropagation();
			};

			// Ignore right-click, since that should open the context menu.
			function defaultFilter$1() {
			  return !exports.event.button;
			}

			function defaultExtent() {
			  var e = this, w, h;
			  if (e instanceof SVGElement) {
				e = e.ownerSVGElement || e;
				w = e.width.baseVal.value;
				h = e.height.baseVal.value;
			  } else {
				w = e.clientWidth;
				h = e.clientHeight;
			  }
			  return [[0, 0], [w, h]];
			}

			function defaultTransform() {
			  return this.__zoom || identity$6;
			}

			var zoom = function() {
			  var filter = defaultFilter$1,
				  extent = defaultExtent,
				  k0 = 0,
				  k1 = Infinity,
				  x0 = -k1,
				  x1 = k1,
				  y0 = x0,
				  y1 = x1,
				  duration = 250,
				  gestures = [],
				  listeners = dispatch("start", "zoom", "end"),
				  touchstarting,
				  touchending,
				  touchDelay = 500,
				  wheelDelay = 150;

			  function zoom(selection$$1) {
				selection$$1
					.on("wheel.zoom", wheeled)
					.on("mousedown.zoom", mousedowned)
					.on("dblclick.zoom", dblclicked)
					.on("touchstart.zoom", touchstarted)
					.on("touchmove.zoom", touchmoved)
					.on("touchend.zoom touchcancel.zoom", touchended)
					.style("-webkit-tap-highlight-color", "rgba(0,0,0,0)")
					.property("__zoom", defaultTransform);
			  }

			  zoom.transform = function(collection, transform) {
				var selection$$1 = collection.selection ? collection.selection() : collection;
				selection$$1.property("__zoom", defaultTransform);
				if (collection !== selection$$1) {
				  schedule(collection, transform);
				} else {
				  selection$$1.interrupt().each(function() {
					gesture(this, arguments)
						.start()
						.zoom(null, typeof transform === "function" ? transform.apply(this, arguments) : transform)
						.end();
				  });
				}
			  };

			  zoom.scaleBy = function(selection$$1, k) {
				zoom.scaleTo(selection$$1, function() {
				  var k0 = this.__zoom.k,
					  k1 = typeof k === "function" ? k.apply(this, arguments) : k;
				  return k0 * k1;
				});
			  };

			  zoom.scaleTo = function(selection$$1, k) {
				zoom.transform(selection$$1, function() {
				  var e = extent.apply(this, arguments),
					  t0 = this.__zoom,
					  p0 = centroid(e),
					  p1 = t0.invert(p0),
					  k1 = typeof k === "function" ? k.apply(this, arguments) : k;
				  return constrain(translate(scale(t0, k1), p0, p1), e);
				});
			  };

			  zoom.translateBy = function(selection$$1, x, y) {
				zoom.transform(selection$$1, function() {
				  return constrain(this.__zoom.translate(
					typeof x === "function" ? x.apply(this, arguments) : x,
					typeof y === "function" ? y.apply(this, arguments) : y
				  ), extent.apply(this, arguments));
				});
			  };

			  function scale(transform, k) {
				k = Math.max(k0, Math.min(k1, k));
				return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
			  }

			  function translate(transform, p0, p1) {
				var x = p0[0] - p1[0] * transform.k, y = p0[1] - p1[1] * transform.k;
				return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
			  }

			  function constrain(transform, extent) {
				var dx = Math.min(0, transform.invertX(extent[0][0]) - x0) || Math.max(0, transform.invertX(extent[1][0]) - x1),
					dy = Math.min(0, transform.invertY(extent[0][1]) - y0) || Math.max(0, transform.invertY(extent[1][1]) - y1);
				return dx || dy ? transform.translate(dx, dy) : transform;
			  }

			  function centroid(extent) {
				return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
			  }

			  function schedule(transition$$1, transform, center) {
				transition$$1
					.on("start.zoom", function() { gesture(this, arguments).start(); })
					.on("interrupt.zoom end.zoom", function() { gesture(this, arguments).end(); })
					.tween("zoom", function() {
					  var that = this,
						  args = arguments,
						  g = gesture(that, args),
						  e = extent.apply(that, args),
						  p = center || centroid(e),
						  w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
						  a = that.__zoom,
						  b = typeof transform === "function" ? transform.apply(that, args) : transform,
						  i = interpolateZoom(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
					  return function(t) {
						if (t === 1) t = b; // Avoid rounding error on end.
						else { var l = i(t), k = w / l[2]; t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k); }
						g.zoom(null, t);
					  };
					});
			  }

			  function gesture(that, args) {
				for (var i = 0, n = gestures.length, g; i < n; ++i) {
				  if ((g = gestures[i]).that === that) {
					return g;
				  }
				}
				return new Gesture(that, args);
			  }

			  function Gesture(that, args) {
				this.that = that;
				this.args = args;
				this.index = -1;
				this.active = 0;
				this.extent = extent.apply(that, args);
			  }

			  Gesture.prototype = {
				start: function() {
				  if (++this.active === 1) {
					this.index = gestures.push(this) - 1;
					this.emit("start");
				  }
				  return this;
				},
				zoom: function(key, transform) {
				  if (this.mouse && key !== "mouse") this.mouse[1] = transform.invert(this.mouse[0]);
				  if (this.touch0 && key !== "touch") this.touch0[1] = transform.invert(this.touch0[0]);
				  if (this.touch1 && key !== "touch") this.touch1[1] = transform.invert(this.touch1[0]);
				  this.that.__zoom = transform;
				  this.emit("zoom");
				  return this;
				},
				end: function() {
				  if (--this.active === 0) {
					gestures.splice(this.index, 1);
					this.index = -1;
					this.emit("end");
				  }
				  return this;
				},
				emit: function(type) {
				  customEvent(new ZoomEvent(zoom, type, this.that.__zoom), listeners.apply, listeners, [type, this.that, this.args]);
				}
			  };

			  function wheeled() {
				if (!filter.apply(this, arguments)) return;
				var g = gesture(this, arguments),
					t = this.__zoom,
					k = Math.max(k0, Math.min(k1, t.k * Math.pow(2, -exports.event.deltaY * (exports.event.deltaMode ? 120 : 1) / 500))),
					p = mouse(this);

				// If the mouse is in the same location as before, reuse it.
				// If there were recent wheel events, reset the wheel idle timeout.
				if (g.wheel) {
				  if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
					g.mouse[1] = t.invert(g.mouse[0] = p);
				  }
				  clearTimeout(g.wheel);
				}

				// If this wheel event won’t trigger a transform change, ignore it.
				else if (t.k === k) return;

				// Otherwise, capture the mouse point and location at the start.
				else {
				  g.mouse = [p, t.invert(p)];
				  interrupt(this);
				  g.start();
				}

				noevent$1();
				g.wheel = setTimeout(wheelidled, wheelDelay);
				g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent));

				function wheelidled() {
				  g.wheel = null;
				  g.end();
				}
			  }

			  function mousedowned() {
				if (touchending || !filter.apply(this, arguments)) return;
				var g = gesture(this, arguments),
					v = select(exports.event.view).on("mousemove.zoom", mousemoved, true).on("mouseup.zoom", mouseupped, true),
					p = mouse(this);

				dragDisable(exports.event.view);
				nopropagation$1();
				g.mouse = [p, this.__zoom.invert(p)];
				interrupt(this);
				g.start();

				function mousemoved() {
				  noevent$1();
				  g.moved = true;
				  g.zoom("mouse", constrain(translate(g.that.__zoom, g.mouse[0] = mouse(g.that), g.mouse[1]), g.extent));
				}

				function mouseupped() {
				  v.on("mousemove.zoom mouseup.zoom", null);
				  yesdrag(exports.event.view, g.moved);
				  noevent$1();
				  g.end();
				}
			  }

			  function dblclicked() {
				if (!filter.apply(this, arguments)) return;
				var t0 = this.__zoom,
					p0 = mouse(this),
					p1 = t0.invert(p0),
					k1 = t0.k * (exports.event.shiftKey ? 0.5 : 2),
					t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, arguments));

				noevent$1();
				if (duration > 0) select(this).transition().duration(duration).call(schedule, t1, p0);
				else select(this).call(zoom.transform, t1);
			  }

			  function touchstarted() {
				if (!filter.apply(this, arguments)) return;
				var g = gesture(this, arguments),
					touches$$1 = exports.event.changedTouches,
					n = touches$$1.length, i, t, p;

				nopropagation$1();
				for (i = 0; i < n; ++i) {
				  t = touches$$1[i], p = touch(this, touches$$1, t.identifier);
				  p = [p, this.__zoom.invert(p), t.identifier];
				  if (!g.touch0) g.touch0 = p;
				  else if (!g.touch1) g.touch1 = p;
				}
				if (touchstarting) {
				  touchstarting = clearTimeout(touchstarting);
				  if (!g.touch1) return g.end(), dblclicked.apply(this, arguments);
				}
				if (exports.event.touches.length === n) {
				  touchstarting = setTimeout(function() { touchstarting = null; }, touchDelay);
				  interrupt(this);
				  g.start();
				}
			  }

			  function touchmoved() {
				var g = gesture(this, arguments),
					touches$$1 = exports.event.changedTouches,
					n = touches$$1.length, i, t, p, l;

				noevent$1();
				if (touchstarting) touchstarting = clearTimeout(touchstarting);
				for (i = 0; i < n; ++i) {
				  t = touches$$1[i], p = touch(this, touches$$1, t.identifier);
				  if (g.touch0 && g.touch0[2] === t.identifier) g.touch0[0] = p;
				  else if (g.touch1 && g.touch1[2] === t.identifier) g.touch1[0] = p;
				}
				t = g.that.__zoom;
				if (g.touch1) {
				  var p0 = g.touch0[0], l0 = g.touch0[1],
					  p1 = g.touch1[0], l1 = g.touch1[1],
					  dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
					  dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
				  t = scale(t, Math.sqrt(dp / dl));
				  p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
				  l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
				}
				else if (g.touch0) p = g.touch0[0], l = g.touch0[1];
				else return;
				g.zoom("touch", constrain(translate(t, p, l), g.extent));
			  }

			  function touchended() {
				var g = gesture(this, arguments),
					touches$$1 = exports.event.changedTouches,
					n = touches$$1.length, i, t;

				nopropagation$1();
				if (touchending) clearTimeout(touchending);
				touchending = setTimeout(function() { touchending = null; }, touchDelay);
				for (i = 0; i < n; ++i) {
				  t = touches$$1[i];
				  if (g.touch0 && g.touch0[2] === t.identifier) delete g.touch0;
				  else if (g.touch1 && g.touch1[2] === t.identifier) delete g.touch1;
				}
				if (g.touch1 && !g.touch0) g.touch0 = g.touch1, delete g.touch1;
				if (!g.touch0) g.end();
			  }

			  zoom.filter = function(_) {
				return arguments.length ? (filter = typeof _ === "function" ? _ : constant$10(!!_), zoom) : filter;
			  };

			  zoom.extent = function(_) {
				return arguments.length ? (extent = typeof _ === "function" ? _ : constant$10([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
			  };

			  zoom.scaleExtent = function(_) {
				return arguments.length ? (k0 = +_[0], k1 = +_[1], zoom) : [k0, k1];
			  };

			  zoom.translateExtent = function(_) {
				return arguments.length ? (x0 = +_[0][0], x1 = +_[1][0], y0 = +_[0][1], y1 = +_[1][1], zoom) : [[x0, y0], [x1, y1]];
			  };

			  zoom.duration = function(_) {
				return arguments.length ? (duration = +_, zoom) : duration;
			  };

			  zoom.on = function() {
				var value = listeners.on.apply(listeners, arguments);
				return value === listeners ? zoom : value;
			  };

			  return zoom;
			};

			var constant$11 = function(x) {
			  return function() {
				return x;
			  };
			};

			var BrushEvent = function(target, type, selection) {
			  this.target = target;
			  this.type = type;
			  this.selection = selection;
			};

			function nopropagation$2() {
			  exports.event.stopImmediatePropagation();
			}

			var noevent$2 = function() {
			  exports.event.preventDefault();
			  exports.event.stopImmediatePropagation();
			};

			var MODE_DRAG = {name: "drag"};
			var MODE_SPACE = {name: "space"};
			var MODE_HANDLE = {name: "handle"};
			var MODE_CENTER = {name: "center"};

			var X = {
			  name: "x",
			  handles: ["e", "w"].map(type$1),
			  input: function(x, e) { return x && [[x[0], e[0][1]], [x[1], e[1][1]]]; },
			  output: function(xy) { return xy && [xy[0][0], xy[1][0]]; }
			};

			var Y = {
			  name: "y",
			  handles: ["n", "s"].map(type$1),
			  input: function(y, e) { return y && [[e[0][0], y[0]], [e[1][0], y[1]]]; },
			  output: function(xy) { return xy && [xy[0][1], xy[1][1]]; }
			};

			var XY = {
			  name: "xy",
			  handles: ["n", "e", "s", "w", "nw", "ne", "se", "sw"].map(type$1),
			  input: function(xy) { return xy; },
			  output: function(xy) { return xy; }
			};

			var cursors = {
			  overlay: "crosshair",
			  selection: "move",
			  n: "ns-resize",
			  e: "ew-resize",
			  s: "ns-resize",
			  w: "ew-resize",
			  nw: "nwse-resize",
			  ne: "nesw-resize",
			  se: "nwse-resize",
			  sw: "nesw-resize"
			};

			var flipX = {
			  e: "w",
			  w: "e",
			  nw: "ne",
			  ne: "nw",
			  se: "sw",
			  sw: "se"
			};

			var flipY = {
			  n: "s",
			  s: "n",
			  nw: "sw",
			  ne: "se",
			  se: "ne",
			  sw: "nw"
			};

			var signsX = {
			  overlay: +1,
			  selection: +1,
			  n: null,
			  e: +1,
			  s: null,
			  w: -1,
			  nw: -1,
			  ne: +1,
			  se: +1,
			  sw: -1
			};

			var signsY = {
			  overlay: +1,
			  selection: +1,
			  n: -1,
			  e: null,
			  s: +1,
			  w: null,
			  nw: -1,
			  ne: -1,
			  se: +1,
			  sw: +1
			};

			function type$1(t) {
			  return {type: t};
			}

			// Ignore right-click, since that should open the context menu.
			function defaultFilter$2() {
			  return !exports.event.button;
			}

			function defaultExtent$1() {
			  var svg = this.ownerSVGElement || this;
			  return [[0, 0], [svg.width.baseVal.value, svg.height.baseVal.value]];
			}

			// Like d3.local, but with the name “__brush” rather than auto-generated.
			function local$1(node) {
			  while (!node.__brush) if (!(node = node.parentNode)) return;
			  return node.__brush;
			}

			function empty$1(extent) {
			  return extent[0][0] === extent[1][0]
				  || extent[0][1] === extent[1][1];
			}

			function brushSelection(node) {
			  var state = node.__brush;
			  return state ? state.dim.output(state.selection) : null;
			}

			function brushX() {
			  return brush$1(X);
			}

			function brushY() {
			  return brush$1(Y);
			}

			var brush = function() {
			  return brush$1(XY);
			};

			function brush$1(dim) {
			  var extent = defaultExtent$1,
				  filter = defaultFilter$2,
				  listeners = dispatch(brush, "start", "brush", "end"),
				  handleSize = 6,
				  touchending;

			  function brush(group) {
				var overlay = group
					.property("__brush", initialize)
				  .selectAll(".overlay")
				  .data([type$1("overlay")]);

				overlay.enter().append("rect")
					.attr("class", "overlay")
					.attr("pointer-events", "all")
					.attr("cursor", cursors.overlay)
				  .merge(overlay)
					.each(function() {
					  var extent = local$1(this).extent;
					  select(this)
						  .attr("x", extent[0][0])
						  .attr("y", extent[0][1])
						  .attr("width", extent[1][0] - extent[0][0])
						  .attr("height", extent[1][1] - extent[0][1]);
					});

				group.selectAll(".selection")
				  .data([type$1("selection")])
				  .enter().append("rect")
					.attr("class", "selection")
					.attr("cursor", cursors.selection)
					.attr("fill", "#777")
					.attr("fill-opacity", 0.3)
					.attr("stroke", "#fff")
					.attr("shape-rendering", "crispEdges");

				var handle = group.selectAll(".handle")
				  .data(dim.handles, function(d) { return d.type; });

				handle.exit().remove();

				handle.enter().append("rect")
					.attr("class", function(d) { return "handle handle--" + d.type; })
					.attr("cursor", function(d) { return cursors[d.type]; });

				group
					.each(redraw)
					.attr("fill", "none")
					.attr("pointer-events", "all")
					.style("-webkit-tap-highlight-color", "rgba(0,0,0,0)")
					.on("mousedown.brush touchstart.brush", started);
			  }

			  brush.move = function(group, selection$$1) {
				if (group.selection) {
				  group
					  .on("start.brush", function() { emitter(this, arguments).beforestart().start(); })
					  .on("interrupt.brush end.brush", function() { emitter(this, arguments).end(); })
					  .tween("brush", function() {
						var that = this,
							state = that.__brush,
							emit = emitter(that, arguments),
							selection0 = state.selection,
							selection1 = dim.input(typeof selection$$1 === "function" ? selection$$1.apply(this, arguments) : selection$$1, state.extent),
							i = interpolate(selection0, selection1);

						function tween(t) {
						  state.selection = t === 1 && empty$1(selection1) ? null : i(t);
						  redraw.call(that);
						  emit.brush();
						}

						return selection0 && selection1 ? tween : tween(1);
					  });
				} else {
				  group
					  .each(function() {
						var that = this,
							args = arguments,
							state = that.__brush,
							selection1 = dim.input(typeof selection$$1 === "function" ? selection$$1.apply(that, args) : selection$$1, state.extent),
							emit = emitter(that, args).beforestart();

						interrupt(that);
						state.selection = selection1 == null || empty$1(selection1) ? null : selection1;
						redraw.call(that);
						emit.start().brush().end();
					  });
				}
			  };

			  function redraw() {
				var group = select(this),
					selection$$1 = local$1(this).selection;

				if (selection$$1) {
				  group.selectAll(".selection")
					  .style("display", null)
					  .attr("x", selection$$1[0][0])
					  .attr("y", selection$$1[0][1])
					  .attr("width", selection$$1[1][0] - selection$$1[0][0])
					  .attr("height", selection$$1[1][1] - selection$$1[0][1]);

				  group.selectAll(".handle")
					  .style("display", null)
					  .attr("x", function(d) { return d.type[d.type.length - 1] === "e" ? selection$$1[1][0] - handleSize / 2 : selection$$1[0][0] - handleSize / 2; })
					  .attr("y", function(d) { return d.type[0] === "s" ? selection$$1[1][1] - handleSize / 2 : selection$$1[0][1] - handleSize / 2; })
					  .attr("width", function(d) { return d.type === "n" || d.type === "s" ? selection$$1[1][0] - selection$$1[0][0] + handleSize : handleSize; })
					  .attr("height", function(d) { return d.type === "e" || d.type === "w" ? selection$$1[1][1] - selection$$1[0][1] + handleSize : handleSize; });
				}

				else {
				  group.selectAll(".selection,.handle")
					  .style("display", "none")
					  .attr("x", null)
					  .attr("y", null)
					  .attr("width", null)
					  .attr("height", null);
				}
			  }

			  function emitter(that, args) {
				return that.__brush.emitter || new Emitter(that, args);
			  }

			  function Emitter(that, args) {
				this.that = that;
				this.args = args;
				this.state = that.__brush;
				this.active = 0;
			  }

			  Emitter.prototype = {
				beforestart: function() {
				  if (++this.active === 1) this.state.emitter = this, this.starting = true;
				  return this;
				},
				start: function() {
				  if (this.starting) this.starting = false, this.emit("start");
				  return this;
				},
				brush: function() {
				  this.emit("brush");
				  return this;
				},
				end: function() {
				  if (--this.active === 0) delete this.state.emitter, this.emit("end");
				  return this;
				},
				emit: function(type) {
				  customEvent(new BrushEvent(brush, type, dim.output(this.state.selection)), listeners.apply, listeners, [type, this.that, this.args]);
				}
			  };

			  function started() {
				if (exports.event.touches) { if (exports.event.changedTouches.length < exports.event.touches.length) return noevent$2(); }
				else if (touchending) return;
				if (!filter.apply(this, arguments)) return;

				var that = this,
					type = exports.event.target.__data__.type,
					mode = (exports.event.metaKey ? type = "overlay" : type) === "selection" ? MODE_DRAG : (exports.event.altKey ? MODE_CENTER : MODE_HANDLE),
					signX = dim === Y ? null : signsX[type],
					signY = dim === X ? null : signsY[type],
					state = local$1(that),
					extent = state.extent,
					selection$$1 = state.selection,
					W = extent[0][0], w0, w1,
					N = extent[0][1], n0, n1,
					E = extent[1][0], e0, e1,
					S = extent[1][1], s0, s1,
					dx,
					dy,
					moving,
					shifting = signX && signY && exports.event.shiftKey,
					lockX,
					lockY,
					point0 = mouse(that),
					point = point0,
					emit = emitter(that, arguments).beforestart();

				if (type === "overlay") {
				  state.selection = selection$$1 = [
					[w0 = dim === Y ? W : point0[0], n0 = dim === X ? N : point0[1]],
					[e0 = dim === Y ? E : w0, s0 = dim === X ? S : n0]
				  ];
				} else {
				  w0 = selection$$1[0][0];
				  n0 = selection$$1[0][1];
				  e0 = selection$$1[1][0];
				  s0 = selection$$1[1][1];
				}

				w1 = w0;
				n1 = n0;
				e1 = e0;
				s1 = s0;

				var group = select(that)
					.attr("pointer-events", "none");

				var overlay = group.selectAll(".overlay")
					.attr("cursor", cursors[type]);

				if (exports.event.touches) {
				  group
					  .on("touchmove.brush", moved, true)
					  .on("touchend.brush touchcancel.brush", ended, true);
				} else {
				  var view = select(exports.event.view)
					  .on("keydown.brush", keydowned, true)
					  .on("keyup.brush", keyupped, true)
					  .on("mousemove.brush", moved, true)
					  .on("mouseup.brush", ended, true);

				  dragDisable(exports.event.view);
				}

				nopropagation$2();
				interrupt(that);
				redraw.call(that);
				emit.start();

				function moved() {
				  var point1 = mouse(that);
				  if (shifting && !lockX && !lockY) {
					if (Math.abs(point1[0] - point[0]) > Math.abs(point1[1] - point[1])) lockY = true;
					else lockX = true;
				  }
				  point = point1;
				  moving = true;
				  noevent$2();
				  move();
				}

				function move() {
				  var t;

				  dx = point[0] - point0[0];
				  dy = point[1] - point0[1];

				  switch (mode) {
					case MODE_SPACE:
					case MODE_DRAG: {
					  if (signX) dx = Math.max(W - w0, Math.min(E - e0, dx)), w1 = w0 + dx, e1 = e0 + dx;
					  if (signY) dy = Math.max(N - n0, Math.min(S - s0, dy)), n1 = n0 + dy, s1 = s0 + dy;
					  break;
					}
					case MODE_HANDLE: {
					  if (signX < 0) dx = Math.max(W - w0, Math.min(E - w0, dx)), w1 = w0 + dx, e1 = e0;
					  else if (signX > 0) dx = Math.max(W - e0, Math.min(E - e0, dx)), w1 = w0, e1 = e0 + dx;
					  if (signY < 0) dy = Math.max(N - n0, Math.min(S - n0, dy)), n1 = n0 + dy, s1 = s0;
					  else if (signY > 0) dy = Math.max(N - s0, Math.min(S - s0, dy)), n1 = n0, s1 = s0 + dy;
					  break;
					}
					case MODE_CENTER: {
					  if (signX) w1 = Math.max(W, Math.min(E, w0 - dx * signX)), e1 = Math.max(W, Math.min(E, e0 + dx * signX));
					  if (signY) n1 = Math.max(N, Math.min(S, n0 - dy * signY)), s1 = Math.max(N, Math.min(S, s0 + dy * signY));
					  break;
					}
				  }

				  if (e1 < w1) {
					signX *= -1;
					t = w0, w0 = e0, e0 = t;
					t = w1, w1 = e1, e1 = t;
					if (type in flipX) overlay.attr("cursor", cursors[type = flipX[type]]);
				  }

				  if (s1 < n1) {
					signY *= -1;
					t = n0, n0 = s0, s0 = t;
					t = n1, n1 = s1, s1 = t;
					if (type in flipY) overlay.attr("cursor", cursors[type = flipY[type]]);
				  }

				  if (state.selection) selection$$1 = state.selection; // May be set by brush.move!
				  if (lockX) w1 = selection$$1[0][0], e1 = selection$$1[1][0];
				  if (lockY) n1 = selection$$1[0][1], s1 = selection$$1[1][1];

				  if (selection$$1[0][0] !== w1
					  || selection$$1[0][1] !== n1
					  || selection$$1[1][0] !== e1
					  || selection$$1[1][1] !== s1) {
					state.selection = [[w1, n1], [e1, s1]];
					redraw.call(that);
					emit.brush();
				  }
				}

				function ended() {
				  nopropagation$2();
				  if (exports.event.touches) {
					if (exports.event.touches.length) return;
					if (touchending) clearTimeout(touchending);
					touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed!
					group.on("touchmove.brush touchend.brush touchcancel.brush", null);
				  } else {
					yesdrag(exports.event.view, moving);
					view.on("keydown.brush keyup.brush mousemove.brush mouseup.brush", null);
				  }
				  group.attr("pointer-events", "all");
				  overlay.attr("cursor", cursors.overlay);
				  if (state.selection) selection$$1 = state.selection; // May be set by brush.move (on start)!
				  if (empty$1(selection$$1)) state.selection = null, redraw.call(that);
				  emit.end();
				}

				function keydowned() {
				  switch (exports.event.keyCode) {
					case 16: { // SHIFT
					  shifting = signX && signY;
					  break;
					}
					case 18: { // ALT
					  if (mode === MODE_HANDLE) {
						if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX;
						if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY;
						mode = MODE_CENTER;
						move();
					  }
					  break;
					}
					case 32: { // SPACE; takes priority over ALT
					  if (mode === MODE_HANDLE || mode === MODE_CENTER) {
						if (signX < 0) e0 = e1 - dx; else if (signX > 0) w0 = w1 - dx;
						if (signY < 0) s0 = s1 - dy; else if (signY > 0) n0 = n1 - dy;
						mode = MODE_SPACE;
						overlay.attr("cursor", cursors.selection);
						move();
					  }
					  break;
					}
					default: return;
				  }
				  noevent$2();
				}

				function keyupped() {
				  switch (exports.event.keyCode) {
					case 16: { // SHIFT
					  if (shifting) {
						lockX = lockY = shifting = false;
						move();
					  }
					  break;
					}
					case 18: { // ALT
					  if (mode === MODE_CENTER) {
						if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1;
						if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1;
						mode = MODE_HANDLE;
						move();
					  }
					  break;
					}
					case 32: { // SPACE
					  if (mode === MODE_SPACE) {
						if (exports.event.altKey) {
						  if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX;
						  if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY;
						  mode = MODE_CENTER;
						} else {
						  if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1;
						  if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1;
						  mode = MODE_HANDLE;
						}
						overlay.attr("cursor", cursors[type]);
						move();
					  }
					  break;
					}
					default: return;
				  }
				  noevent$2();
				}
			  }

			  function initialize() {
				var state = this.__brush || {selection: null};
				state.extent = extent.apply(this, arguments);
				state.dim = dim;
				return state;
			  }

			  brush.extent = function(_) {
				return arguments.length ? (extent = typeof _ === "function" ? _ : constant$11([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), brush) : extent;
			  };

			  brush.filter = function(_) {
				return arguments.length ? (filter = typeof _ === "function" ? _ : constant$11(!!_), brush) : filter;
			  };

			  brush.handleSize = function(_) {
				return arguments.length ? (handleSize = +_, brush) : handleSize;
			  };

			  brush.on = function() {
				var value = listeners.on.apply(listeners, arguments);
				return value === listeners ? brush : value;
			  };

			  return brush;
			}

			var cos = Math.cos;
			var sin = Math.sin;
			var pi$3 = Math.PI;
			var halfPi$2 = pi$3 / 2;
			var tau$3 = pi$3 * 2;
			var max$1 = Math.max;

			function compareValue(compare) {
			  return function(a, b) {
				return compare(
				  a.source.value + a.target.value,
				  b.source.value + b.target.value
				);
			  };
			}

			var chord = function() {
			  var padAngle = 0,
				  sortGroups = null,
				  sortSubgroups = null,
				  sortChords = null;

			  function chord(matrix) {
				var n = matrix.length,
					groupSums = [],
					groupIndex = range(n),
					subgroupIndex = [],
					chords = [],
					groups = chords.groups = new Array(n),
					subgroups = new Array(n * n),
					k,
					x,
					x0,
					dx,
					i,
					j;

				// Compute the sum.
				k = 0, i = -1; while (++i < n) {
				  x = 0, j = -1; while (++j < n) {
					x += matrix[i][j];
				  }
				  groupSums.push(x);
				  subgroupIndex.push(range(n));
				  k += x;
				}

				// Sort groups…
				if (sortGroups) groupIndex.sort(function(a, b) {
				  return sortGroups(groupSums[a], groupSums[b]);
				});

				// Sort subgroups…
				if (sortSubgroups) subgroupIndex.forEach(function(d, i) {
				  d.sort(function(a, b) {
					return sortSubgroups(matrix[i][a], matrix[i][b]);
				  });
				});

				// Convert the sum to scaling factor for [0, 2pi].
				// TODO Allow start and end angle to be specified?
				// TODO Allow padding to be specified as percentage?
				k = max$1(0, tau$3 - padAngle * n) / k;
				dx = k ? padAngle : tau$3 / n;

				// Compute the start and end angle for each group and subgroup.
				// Note: Opera has a bug reordering object literal properties!
				x = 0, i = -1; while (++i < n) {
				  x0 = x, j = -1; while (++j < n) {
					var di = groupIndex[i],
						dj = subgroupIndex[di][j],
						v = matrix[di][dj],
						a0 = x,
						a1 = x += v * k;
					subgroups[dj * n + di] = {
					  index: di,
					  subindex: dj,
					  startAngle: a0,
					  endAngle: a1,
					  value: v
					};
				  }
				  groups[di] = {
					index: di,
					startAngle: x0,
					endAngle: x,
					value: groupSums[di]
				  };
				  x += dx;
				}

				// Generate chords for each (non-empty) subgroup-subgroup link.
				i = -1; while (++i < n) {
				  j = i - 1; while (++j < n) {
					var source = subgroups[j * n + i],
						target = subgroups[i * n + j];
					if (source.value || target.value) {
					  chords.push(source.value < target.value
						  ? {source: target, target: source}
						  : {source: source, target: target});
					}
				  }
				}

				return sortChords ? chords.sort(sortChords) : chords;
			  }

			  chord.padAngle = function(_) {
				return arguments.length ? (padAngle = max$1(0, _), chord) : padAngle;
			  };

			  chord.sortGroups = function(_) {
				return arguments.length ? (sortGroups = _, chord) : sortGroups;
			  };

			  chord.sortSubgroups = function(_) {
				return arguments.length ? (sortSubgroups = _, chord) : sortSubgroups;
			  };

			  chord.sortChords = function(_) {
				return arguments.length ? (_ == null ? sortChords = null : (sortChords = compareValue(_))._ = _, chord) : sortChords && sortChords._;
			  };

			  return chord;
			};

			var slice$5 = Array.prototype.slice;

			var constant$12 = function(x) {
			  return function() {
				return x;
			  };
			};

			function defaultSource(d) {
			  return d.source;
			}

			function defaultTarget(d) {
			  return d.target;
			}

			function defaultRadius$1(d) {
			  return d.radius;
			}

			function defaultStartAngle(d) {
			  return d.startAngle;
			}

			function defaultEndAngle(d) {
			  return d.endAngle;
			}

			var ribbon = function() {
			  var source = defaultSource,
				  target = defaultTarget,
				  radius = defaultRadius$1,
				  startAngle = defaultStartAngle,
				  endAngle = defaultEndAngle,
				  context = null;

			  function ribbon() {
				var buffer,
					argv = slice$5.call(arguments),
					s = source.apply(this, argv),
					t = target.apply(this, argv),
					sr = +radius.apply(this, (argv[0] = s, argv)),
					sa0 = startAngle.apply(this, argv) - halfPi$2,
					sa1 = endAngle.apply(this, argv) - halfPi$2,
					sx0 = sr * cos(sa0),
					sy0 = sr * sin(sa0),
					tr = +radius.apply(this, (argv[0] = t, argv)),
					ta0 = startAngle.apply(this, argv) - halfPi$2,
					ta1 = endAngle.apply(this, argv) - halfPi$2;

				if (!context) context = buffer = path();

				context.moveTo(sx0, sy0);
				context.arc(0, 0, sr, sa0, sa1);
				if (sa0 !== ta0 || sa1 !== ta1) { // TODO sr !== tr?
				  context.quadraticCurveTo(0, 0, tr * cos(ta0), tr * sin(ta0));
				  context.arc(0, 0, tr, ta0, ta1);
				}
				context.quadraticCurveTo(0, 0, sx0, sy0);
				context.closePath();

				if (buffer) return context = null, buffer + "" || null;
			  }

			  ribbon.radius = function(_) {
				return arguments.length ? (radius = typeof _ === "function" ? _ : constant$12(+_), ribbon) : radius;
			  };

			  ribbon.startAngle = function(_) {
				return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$12(+_), ribbon) : startAngle;
			  };

			  ribbon.endAngle = function(_) {
				return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$12(+_), ribbon) : endAngle;
			  };

			  ribbon.source = function(_) {
				return arguments.length ? (source = _, ribbon) : source;
			  };

			  ribbon.target = function(_) {
				return arguments.length ? (target = _, ribbon) : target;
			  };

			  ribbon.context = function(_) {
				return arguments.length ? ((context = _ == null ? null : _), ribbon) : context;
			  };

			  return ribbon;
			};

			// Adds floating point numbers with twice the normal precision.
			// Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and
			// Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3)
			// 305–363 (1997).
			// Code adapted from GeographicLib by Charles F. F. Karney,
			// http://geographiclib.sourceforge.net/

			var adder = function() {
			  return new Adder;
			};

			function Adder() {
			  this.reset();
			}

			Adder.prototype = {
			  constructor: Adder,
			  reset: function() {
				this.s = // rounded value
				this.t = 0; // exact error
			  },
			  add: function(y) {
				add$1(temp, y, this.t);
				add$1(this, temp.s, this.s);
				if (this.s) this.t += temp.t;
				else this.s = temp.t;
			  },
			  valueOf: function() {
				return this.s;
			  }
			};

			var temp = new Adder;

			function add$1(adder, a, b) {
			  var x = adder.s = a + b,
				  bv = x - a,
				  av = x - bv;
			  adder.t = (a - av) + (b - bv);
			}

			var epsilon$4 = 1e-6;
			var epsilon2$2 = 1e-12;
			var pi$4 = Math.PI;
			var halfPi$3 = pi$4 / 2;
			var quarterPi = pi$4 / 4;
			var tau$4 = pi$4 * 2;

			var degrees$1 = 180 / pi$4;
			var radians = pi$4 / 180;

			var abs = Math.abs;
			var atan = Math.atan;
			var atan2 = Math.atan2;
			var cos$1 = Math.cos;
			var ceil = Math.ceil;
			var exp = Math.exp;

			var log$1 = Math.log;
			var pow$1 = Math.pow;
			var sin$1 = Math.sin;
			var sign$1 = Math.sign || function(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; };
			var sqrt$1 = Math.sqrt;
			var tan = Math.tan;

			function acos(x) {
			  return x > 1 ? 0 : x < -1 ? pi$4 : Math.acos(x);
			}

			function asin$1(x) {
			  return x > 1 ? halfPi$3 : x < -1 ? -halfPi$3 : Math.asin(x);
			}

			function haversin(x) {
			  return (x = sin$1(x / 2)) * x;
			}

			function noop$2() {}

			function streamGeometry(geometry, stream) {
			  if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) {
				streamGeometryType[geometry.type](geometry, stream);
			  }
			}

			var streamObjectType = {
			  Feature: function(feature, stream) {
				streamGeometry(feature.geometry, stream);
			  },
			  FeatureCollection: function(object, stream) {
				var features = object.features, i = -1, n = features.length;
				while (++i < n) streamGeometry(features[i].geometry, stream);
			  }
			};

			var streamGeometryType = {
			  Sphere: function(object, stream) {
				stream.sphere();
			  },
			  Point: function(object, stream) {
				object = object.coordinates;
				stream.point(object[0], object[1], object[2]);
			  },
			  MultiPoint: function(object, stream) {
				var coordinates = object.coordinates, i = -1, n = coordinates.length;
				while (++i < n) object = coordinates[i], stream.point(object[0], object[1], object[2]);
			  },
			  LineString: function(object, stream) {
				streamLine(object.coordinates, stream, 0);
			  },
			  MultiLineString: function(object, stream) {
				var coordinates = object.coordinates, i = -1, n = coordinates.length;
				while (++i < n) streamLine(coordinates[i], stream, 0);
			  },
			  Polygon: function(object, stream) {
				streamPolygon(object.coordinates, stream);
			  },
			  MultiPolygon: function(object, stream) {
				var coordinates = object.coordinates, i = -1, n = coordinates.length;
				while (++i < n) streamPolygon(coordinates[i], stream);
			  },
			  GeometryCollection: function(object, stream) {
				var geometries = object.geometries, i = -1, n = geometries.length;
				while (++i < n) streamGeometry(geometries[i], stream);
			  }
			};

			function streamLine(coordinates, stream, closed) {
			  var i = -1, n = coordinates.length - closed, coordinate;
			  stream.lineStart();
			  while (++i < n) coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]);
			  stream.lineEnd();
			}

			function streamPolygon(coordinates, stream) {
			  var i = -1, n = coordinates.length;
			  stream.polygonStart();
			  while (++i < n) streamLine(coordinates[i], stream, 1);
			  stream.polygonEnd();
			}

			var geoStream = function(object, stream) {
			  if (object && streamObjectType.hasOwnProperty(object.type)) {
				streamObjectType[object.type](object, stream);
			  } else {
				streamGeometry(object, stream);
			  }
			};

			var areaRingSum = adder();

			var areaSum = adder();
			var lambda00;
			var phi00;
			var lambda0;
			var cosPhi0;
			var sinPhi0;

			var areaStream = {
			  point: noop$2,
			  lineStart: noop$2,
			  lineEnd: noop$2,
			  polygonStart: function() {
				areaRingSum.reset();
				areaStream.lineStart = areaRingStart;
				areaStream.lineEnd = areaRingEnd;
			  },
			  polygonEnd: function() {
				var areaRing = +areaRingSum;
				areaSum.add(areaRing < 0 ? tau$4 + areaRing : areaRing);
				this.lineStart = this.lineEnd = this.point = noop$2;
			  },
			  sphere: function() {
				areaSum.add(tau$4);
			  }
			};

			function areaRingStart() {
			  areaStream.point = areaPointFirst;
			}

			function areaRingEnd() {
			  areaPoint(lambda00, phi00);
			}

			function areaPointFirst(lambda, phi) {
			  areaStream.point = areaPoint;
			  lambda00 = lambda, phi00 = phi;
			  lambda *= radians, phi *= radians;
			  lambda0 = lambda, cosPhi0 = cos$1(phi = phi / 2 + quarterPi), sinPhi0 = sin$1(phi);
			}

			function areaPoint(lambda, phi) {
			  lambda *= radians, phi *= radians;
			  phi = phi / 2 + quarterPi; // half the angular distance from south pole

			  // Spherical excess E for a spherical triangle with vertices: south pole,
			  // previous point, current point.  Uses a formula derived from Cagnoli’s
			  // theorem.  See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2).
			  var dLambda = lambda - lambda0,
				  sdLambda = dLambda >= 0 ? 1 : -1,
				  adLambda = sdLambda * dLambda,
				  cosPhi = cos$1(phi),
				  sinPhi = sin$1(phi),
				  k = sinPhi0 * sinPhi,
				  u = cosPhi0 * cosPhi + k * cos$1(adLambda),
				  v = k * sdLambda * sin$1(adLambda);
			  areaRingSum.add(atan2(v, u));

			  // Advance the previous points.
			  lambda0 = lambda, cosPhi0 = cosPhi, sinPhi0 = sinPhi;
			}

			var area$2 = function(object) {
			  areaSum.reset();
			  geoStream(object, areaStream);
			  return areaSum * 2;
			};

			function spherical(cartesian) {
			  return [atan2(cartesian[1], cartesian[0]), asin$1(cartesian[2])];
			}

			function cartesian(spherical) {
			  var lambda = spherical[0], phi = spherical[1], cosPhi = cos$1(phi);
			  return [cosPhi * cos$1(lambda), cosPhi * sin$1(lambda), sin$1(phi)];
			}

			function cartesianDot(a, b) {
			  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
			}

			function cartesianCross(a, b) {
			  return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
			}

			// TODO return a
			function cartesianAddInPlace(a, b) {
			  a[0] += b[0], a[1] += b[1], a[2] += b[2];
			}

			function cartesianScale(vector, k) {
			  return [vector[0] * k, vector[1] * k, vector[2] * k];
			}

			// TODO return d
			function cartesianNormalizeInPlace(d) {
			  var l = sqrt$1(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
			  d[0] /= l, d[1] /= l, d[2] /= l;
			}

			var lambda0$1;
			var phi0;
			var lambda1;
			var phi1;
			var lambda2;
			var lambda00$1;
			var phi00$1;
			var p0;
			var deltaSum = adder();
			var ranges;
			var range$1;

			var boundsStream = {
			  point: boundsPoint,
			  lineStart: boundsLineStart,
			  lineEnd: boundsLineEnd,
			  polygonStart: function() {
				boundsStream.point = boundsRingPoint;
				boundsStream.lineStart = boundsRingStart;
				boundsStream.lineEnd = boundsRingEnd;
				deltaSum.reset();
				areaStream.polygonStart();
			  },
			  polygonEnd: function() {
				areaStream.polygonEnd();
				boundsStream.point = boundsPoint;
				boundsStream.lineStart = boundsLineStart;
				boundsStream.lineEnd = boundsLineEnd;
				if (areaRingSum < 0) lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90);
				else if (deltaSum > epsilon$4) phi1 = 90;
				else if (deltaSum < -epsilon$4) phi0 = -90;
				range$1[0] = lambda0$1, range$1[1] = lambda1;
			  }
			};

			function boundsPoint(lambda, phi) {
			  ranges.push(range$1 = [lambda0$1 = lambda, lambda1 = lambda]);
			  if (phi < phi0) phi0 = phi;
			  if (phi > phi1) phi1 = phi;
			}

			function linePoint(lambda, phi) {
			  var p = cartesian([lambda * radians, phi * radians]);
			  if (p0) {
				var normal = cartesianCross(p0, p),
					equatorial = [normal[1], -normal[0], 0],
					inflection = cartesianCross(equatorial, normal);
				cartesianNormalizeInPlace(inflection);
				inflection = spherical(inflection);
				var delta = lambda - lambda2,
					sign$$1 = delta > 0 ? 1 : -1,
					lambdai = inflection[0] * degrees$1 * sign$$1,
					phii,
					antimeridian = abs(delta) > 180;
				if (antimeridian ^ (sign$$1 * lambda2 < lambdai && lambdai < sign$$1 * lambda)) {
				  phii = inflection[1] * degrees$1;
				  if (phii > phi1) phi1 = phii;
				} else if (lambdai = (lambdai + 360) % 360 - 180, antimeridian ^ (sign$$1 * lambda2 < lambdai && lambdai < sign$$1 * lambda)) {
				  phii = -inflection[1] * degrees$1;
				  if (phii < phi0) phi0 = phii;
				} else {
				  if (phi < phi0) phi0 = phi;
				  if (phi > phi1) phi1 = phi;
				}
				if (antimeridian) {
				  if (lambda < lambda2) {
					if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
				  } else {
					if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
				  }
				} else {
				  if (lambda1 >= lambda0$1) {
					if (lambda < lambda0$1) lambda0$1 = lambda;
					if (lambda > lambda1) lambda1 = lambda;
				  } else {
					if (lambda > lambda2) {
					  if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
					} else {
					  if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
					}
				  }
				}
			  } else {
				boundsPoint(lambda, phi);
			  }
			  p0 = p, lambda2 = lambda;
			}

			function boundsLineStart() {
			  boundsStream.point = linePoint;
			}

			function boundsLineEnd() {
			  range$1[0] = lambda0$1, range$1[1] = lambda1;
			  boundsStream.point = boundsPoint;
			  p0 = null;
			}

			function boundsRingPoint(lambda, phi) {
			  if (p0) {
				var delta = lambda - lambda2;
				deltaSum.add(abs(delta) > 180 ? delta + (delta > 0 ? 360 : -360) : delta);
			  } else {
				lambda00$1 = lambda, phi00$1 = phi;
			  }
			  areaStream.point(lambda, phi);
			  linePoint(lambda, phi);
			}

			function boundsRingStart() {
			  areaStream.lineStart();
			}

			function boundsRingEnd() {
			  boundsRingPoint(lambda00$1, phi00$1);
			  areaStream.lineEnd();
			  if (abs(deltaSum) > epsilon$4) lambda0$1 = -(lambda1 = 180);
			  range$1[0] = lambda0$1, range$1[1] = lambda1;
			  p0 = null;
			}

			// Finds the left-right distance between two longitudes.
			// This is almost the same as (lambda1 - lambda0 + 360°) % 360°, except that we want
			// the distance between ±180° to be 360°.
			function angle(lambda0, lambda1) {
			  return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1;
			}

			function rangeCompare(a, b) {
			  return a[0] - b[0];
			}

			function rangeContains(range, x) {
			  return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
			}

			var bounds = function(feature) {
			  var i, n, a, b, merged, deltaMax, delta;

			  phi1 = lambda1 = -(lambda0$1 = phi0 = Infinity);
			  ranges = [];
			  geoStream(feature, boundsStream);

			  // First, sort ranges by their minimum longitudes.
			  if (n = ranges.length) {
				ranges.sort(rangeCompare);

				// Then, merge any ranges that overlap.
				for (i = 1, a = ranges[0], merged = [a]; i < n; ++i) {
				  b = ranges[i];
				  if (rangeContains(a, b[0]) || rangeContains(a, b[1])) {
					if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
					if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
				  } else {
					merged.push(a = b);
				  }
				}

				// Finally, find the largest gap between the merged ranges.
				// The final bounding box will be the inverse of this gap.
				for (deltaMax = -Infinity, n = merged.length - 1, i = 0, a = merged[n]; i <= n; a = b, ++i) {
				  b = merged[i];
				  if ((delta = angle(a[1], b[0])) > deltaMax) deltaMax = delta, lambda0$1 = b[0], lambda1 = a[1];
				}
			  }

			  ranges = range$1 = null;

			  return lambda0$1 === Infinity || phi0 === Infinity
				  ? [[NaN, NaN], [NaN, NaN]]
				  : [[lambda0$1, phi0], [lambda1, phi1]];
			};

			var W0;
			var W1;
			var X0;
			var Y0;
			var Z0;
			var X1;
			var Y1;
			var Z1;
			var X2;
			var Y2;
			var Z2;
			var lambda00$2;
			var phi00$2;
			var x0;
			var y0;
			var z0; // previous point

			var centroidStream = {
			  sphere: noop$2,
			  point: centroidPoint,
			  lineStart: centroidLineStart,
			  lineEnd: centroidLineEnd,
			  polygonStart: function() {
				centroidStream.lineStart = centroidRingStart;
				centroidStream.lineEnd = centroidRingEnd;
			  },
			  polygonEnd: function() {
				centroidStream.lineStart = centroidLineStart;
				centroidStream.lineEnd = centroidLineEnd;
			  }
			};

			// Arithmetic mean of Cartesian vectors.
			function centroidPoint(lambda, phi) {
			  lambda *= radians, phi *= radians;
			  var cosPhi = cos$1(phi);
			  centroidPointCartesian(cosPhi * cos$1(lambda), cosPhi * sin$1(lambda), sin$1(phi));
			}

			function centroidPointCartesian(x, y, z) {
			  ++W0;
			  X0 += (x - X0) / W0;
			  Y0 += (y - Y0) / W0;
			  Z0 += (z - Z0) / W0;
			}

			function centroidLineStart() {
			  centroidStream.point = centroidLinePointFirst;
			}

			function centroidLinePointFirst(lambda, phi) {
			  lambda *= radians, phi *= radians;
			  var cosPhi = cos$1(phi);
			  x0 = cosPhi * cos$1(lambda);
			  y0 = cosPhi * sin$1(lambda);
			  z0 = sin$1(phi);
			  centroidStream.point = centroidLinePoint;
			  centroidPointCartesian(x0, y0, z0);
			}

			function centroidLinePoint(lambda, phi) {
			  lambda *= radians, phi *= radians;
			  var cosPhi = cos$1(phi),
				  x = cosPhi * cos$1(lambda),
				  y = cosPhi * sin$1(lambda),
				  z = sin$1(phi),
				  w = atan2(sqrt$1((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z);
			  W1 += w;
			  X1 += w * (x0 + (x0 = x));
			  Y1 += w * (y0 + (y0 = y));
			  Z1 += w * (z0 + (z0 = z));
			  centroidPointCartesian(x0, y0, z0);
			}

			function centroidLineEnd() {
			  centroidStream.point = centroidPoint;
			}

			// See J. E. Brock, The Inertia Tensor for a Spherical Triangle,
			// J. Applied Mechanics 42, 239 (1975).
			function centroidRingStart() {
			  centroidStream.point = centroidRingPointFirst;
			}

			function centroidRingEnd() {
			  centroidRingPoint(lambda00$2, phi00$2);
			  centroidStream.point = centroidPoint;
			}

			function centroidRingPointFirst(lambda, phi) {
			  lambda00$2 = lambda, phi00$2 = phi;
			  lambda *= radians, phi *= radians;
			  centroidStream.point = centroidRingPoint;
			  var cosPhi = cos$1(phi);
			  x0 = cosPhi * cos$1(lambda);
			  y0 = cosPhi * sin$1(lambda);
			  z0 = sin$1(phi);
			  centroidPointCartesian(x0, y0, z0);
			}

			function centroidRingPoint(lambda, phi) {
			  lambda *= radians, phi *= radians;
			  var cosPhi = cos$1(phi),
				  x = cosPhi * cos$1(lambda),
				  y = cosPhi * sin$1(lambda),
				  z = sin$1(phi),
				  cx = y0 * z - z0 * y,
				  cy = z0 * x - x0 * z,
				  cz = x0 * y - y0 * x,
				  m = sqrt$1(cx * cx + cy * cy + cz * cz),
				  u = x0 * x + y0 * y + z0 * z,
				  v = m && -acos(u) / m, // area weight
				  w = atan2(m, u); // line weight
			  X2 += v * cx;
			  Y2 += v * cy;
			  Z2 += v * cz;
			  W1 += w;
			  X1 += w * (x0 + (x0 = x));
			  Y1 += w * (y0 + (y0 = y));
			  Z1 += w * (z0 + (z0 = z));
			  centroidPointCartesian(x0, y0, z0);
			}

			var centroid$1 = function(object) {
			  W0 = W1 =
			  X0 = Y0 = Z0 =
			  X1 = Y1 = Z1 =
			  X2 = Y2 = Z2 = 0;
			  geoStream(object, centroidStream);

			  var x = X2,
				  y = Y2,
				  z = Z2,
				  m = x * x + y * y + z * z;

			  // If the area-weighted ccentroid is undefined, fall back to length-weighted ccentroid.
			  if (m < epsilon2$2) {
				x = X1, y = Y1, z = Z1;
				// If the feature has zero length, fall back to arithmetic mean of point vectors.
				if (W1 < epsilon$4) x = X0, y = Y0, z = Z0;
				m = x * x + y * y + z * z;
				// If the feature still has an undefined ccentroid, then return.
				if (m < epsilon2$2) return [NaN, NaN];
			  }

			  return [atan2(y, x) * degrees$1, asin$1(z / sqrt$1(m)) * degrees$1];
			};

			var constant$13 = function(x) {
			  return function() {
				return x;
			  };
			};

			var compose = function(a, b) {

			  function compose(x, y) {
				return x = a(x, y), b(x[0], x[1]);
			  }

			  if (a.invert && b.invert) compose.invert = function(x, y) {
				return x = b.invert(x, y), x && a.invert(x[0], x[1]);
			  };

			  return compose;
			};

			function rotationIdentity(lambda, phi) {
			  return [lambda > pi$4 ? lambda - tau$4 : lambda < -pi$4 ? lambda + tau$4 : lambda, phi];
			}

			rotationIdentity.invert = rotationIdentity;

			function rotateRadians(deltaLambda, deltaPhi, deltaGamma) {
			  return (deltaLambda %= tau$4) ? (deltaPhi || deltaGamma ? compose(rotationLambda(deltaLambda), rotationPhiGamma(deltaPhi, deltaGamma))
				: rotationLambda(deltaLambda))
				: (deltaPhi || deltaGamma ? rotationPhiGamma(deltaPhi, deltaGamma)
				: rotationIdentity);
			}

			function forwardRotationLambda(deltaLambda) {
			  return function(lambda, phi) {
				return lambda += deltaLambda, [lambda > pi$4 ? lambda - tau$4 : lambda < -pi$4 ? lambda + tau$4 : lambda, phi];
			  };
			}

			function rotationLambda(deltaLambda) {
			  var rotation = forwardRotationLambda(deltaLambda);
			  rotation.invert = forwardRotationLambda(-deltaLambda);
			  return rotation;
			}

			function rotationPhiGamma(deltaPhi, deltaGamma) {
			  var cosDeltaPhi = cos$1(deltaPhi),
				  sinDeltaPhi = sin$1(deltaPhi),
				  cosDeltaGamma = cos$1(deltaGamma),
				  sinDeltaGamma = sin$1(deltaGamma);

			  function rotation(lambda, phi) {
				var cosPhi = cos$1(phi),
					x = cos$1(lambda) * cosPhi,
					y = sin$1(lambda) * cosPhi,
					z = sin$1(phi),
					k = z * cosDeltaPhi + x * sinDeltaPhi;
				return [
				  atan2(y * cosDeltaGamma - k * sinDeltaGamma, x * cosDeltaPhi - z * sinDeltaPhi),
				  asin$1(k * cosDeltaGamma + y * sinDeltaGamma)
				];
			  }

			  rotation.invert = function(lambda, phi) {
				var cosPhi = cos$1(phi),
					x = cos$1(lambda) * cosPhi,
					y = sin$1(lambda) * cosPhi,
					z = sin$1(phi),
					k = z * cosDeltaGamma - y * sinDeltaGamma;
				return [
				  atan2(y * cosDeltaGamma + z * sinDeltaGamma, x * cosDeltaPhi + k * sinDeltaPhi),
				  asin$1(k * cosDeltaPhi - x * sinDeltaPhi)
				];
			  };

			  return rotation;
			}

			var rotation = function(rotate) {
			  rotate = rotateRadians(rotate[0] * radians, rotate[1] * radians, rotate.length > 2 ? rotate[2] * radians : 0);

			  function forward(coordinates) {
				coordinates = rotate(coordinates[0] * radians, coordinates[1] * radians);
				return coordinates[0] *= degrees$1, coordinates[1] *= degrees$1, coordinates;
			  }

			  forward.invert = function(coordinates) {
				coordinates = rotate.invert(coordinates[0] * radians, coordinates[1] * radians);
				return coordinates[0] *= degrees$1, coordinates[1] *= degrees$1, coordinates;
			  };

			  return forward;
			};

			// Generates a circle centered at [0°, 0°], with a given radius and precision.
			function circleStream(stream, radius, delta, direction, t0, t1) {
			  if (!delta) return;
			  var cosRadius = cos$1(radius),
				  sinRadius = sin$1(radius),
				  step = direction * delta;
			  if (t0 == null) {
				t0 = radius + direction * tau$4;
				t1 = radius - step / 2;
			  } else {
				t0 = circleRadius(cosRadius, t0);
				t1 = circleRadius(cosRadius, t1);
				if (direction > 0 ? t0 < t1 : t0 > t1) t0 += direction * tau$4;
			  }
			  for (var point, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) {
				point = spherical([cosRadius, -sinRadius * cos$1(t), -sinRadius * sin$1(t)]);
				stream.point(point[0], point[1]);
			  }
			}

			// Returns the signed angle of a cartesian point relative to [cosRadius, 0, 0].
			function circleRadius(cosRadius, point) {
			  point = cartesian(point), point[0] -= cosRadius;
			  cartesianNormalizeInPlace(point);
			  var radius = acos(-point[1]);
			  return ((-point[2] < 0 ? -radius : radius) + tau$4 - epsilon$4) % tau$4;
			}

			var circle$1 = function() {
			  var center = constant$13([0, 0]),
				  radius = constant$13(90),
				  precision = constant$13(6),
				  ring,
				  rotate,
				  stream = {point: point};

			  function point(x, y) {
				ring.push(x = rotate(x, y));
				x[0] *= degrees$1, x[1] *= degrees$1;
			  }

			  function circle() {
				var c = center.apply(this, arguments),
					r = radius.apply(this, arguments) * radians,
					p = precision.apply(this, arguments) * radians;
				ring = [];
				rotate = rotateRadians(-c[0] * radians, -c[1] * radians, 0).invert;
				circleStream(stream, r, p, 1);
				c = {type: "Polygon", coordinates: [ring]};
				ring = rotate = null;
				return c;
			  }

			  circle.center = function(_) {
				return arguments.length ? (center = typeof _ === "function" ? _ : constant$13([+_[0], +_[1]]), circle) : center;
			  };

			  circle.radius = function(_) {
				return arguments.length ? (radius = typeof _ === "function" ? _ : constant$13(+_), circle) : radius;
			  };

			  circle.precision = function(_) {
				return arguments.length ? (precision = typeof _ === "function" ? _ : constant$13(+_), circle) : precision;
			  };

			  return circle;
			};

			var clipBuffer = function() {
			  var lines = [],
				  line;
			  return {
				point: function(x, y) {
				  line.push([x, y]);
				},
				lineStart: function() {
				  lines.push(line = []);
				},
				lineEnd: noop$2,
				rejoin: function() {
				  if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
				},
				result: function() {
				  var result = lines;
				  lines = [];
				  line = null;
				  return result;
				}
			  };
			};

			var clipLine = function(a, b, x0, y0, x1, y1) {
			  var ax = a[0],
				  ay = a[1],
				  bx = b[0],
				  by = b[1],
				  t0 = 0,
				  t1 = 1,
				  dx = bx - ax,
				  dy = by - ay,
				  r;

			  r = x0 - ax;
			  if (!dx && r > 0) return;
			  r /= dx;
			  if (dx < 0) {
				if (r < t0) return;
				if (r < t1) t1 = r;
			  } else if (dx > 0) {
				if (r > t1) return;
				if (r > t0) t0 = r;
			  }

			  r = x1 - ax;
			  if (!dx && r < 0) return;
			  r /= dx;
			  if (dx < 0) {
				if (r > t1) return;
				if (r > t0) t0 = r;
			  } else if (dx > 0) {
				if (r < t0) return;
				if (r < t1) t1 = r;
			  }

			  r = y0 - ay;
			  if (!dy && r > 0) return;
			  r /= dy;
			  if (dy < 0) {
				if (r < t0) return;
				if (r < t1) t1 = r;
			  } else if (dy > 0) {
				if (r > t1) return;
				if (r > t0) t0 = r;
			  }

			  r = y1 - ay;
			  if (!dy && r < 0) return;
			  r /= dy;
			  if (dy < 0) {
				if (r > t1) return;
				if (r > t0) t0 = r;
			  } else if (dy > 0) {
				if (r < t0) return;
				if (r < t1) t1 = r;
			  }

			  if (t0 > 0) a[0] = ax + t0 * dx, a[1] = ay + t0 * dy;
			  if (t1 < 1) b[0] = ax + t1 * dx, b[1] = ay + t1 * dy;
			  return true;
			};

			var pointEqual = function(a, b) {
			  return abs(a[0] - b[0]) < epsilon$4 && abs(a[1] - b[1]) < epsilon$4;
			};

			function Intersection(point, points, other, entry) {
			  this.x = point;
			  this.z = points;
			  this.o = other; // another intersection
			  this.e = entry; // is an entry?
			  this.v = false; // visited
			  this.n = this.p = null; // next & previous
			}

			// A generalized polygon clipping algorithm: given a polygon that has been cut
			// into its visible line segments, and rejoins the segments by interpolating
			// along the clip edge.
			var clipPolygon = function(segments, compareIntersection, startInside, interpolate, stream) {
			  var subject = [],
				  clip = [],
				  i,
				  n;

			  segments.forEach(function(segment) {
				if ((n = segment.length - 1) <= 0) return;
				var n, p0 = segment[0], p1 = segment[n], x;

				// If the first and last points of a segment are coincident, then treat as a
				// closed ring. TODO if all rings are closed, then the winding order of the
				// exterior ring should be checked.
				if (pointEqual(p0, p1)) {
				  stream.lineStart();
				  for (i = 0; i < n; ++i) stream.point((p0 = segment[i])[0], p0[1]);
				  stream.lineEnd();
				  return;
				}

				subject.push(x = new Intersection(p0, segment, null, true));
				clip.push(x.o = new Intersection(p0, null, x, false));
				subject.push(x = new Intersection(p1, segment, null, false));
				clip.push(x.o = new Intersection(p1, null, x, true));
			  });

			  if (!subject.length) return;

			  clip.sort(compareIntersection);
			  link$1(subject);
			  link$1(clip);

			  for (i = 0, n = clip.length; i < n; ++i) {
				clip[i].e = startInside = !startInside;
			  }

			  var start = subject[0],
				  points,
				  point;

			  while (1) {
				// Find first unvisited intersection.
				var current = start,
					isSubject = true;
				while (current.v) if ((current = current.n) === start) return;
				points = current.z;
				stream.lineStart();
				do {
				  current.v = current.o.v = true;
				  if (current.e) {
					if (isSubject) {
					  for (i = 0, n = points.length; i < n; ++i) stream.point((point = points[i])[0], point[1]);
					} else {
					  interpolate(current.x, current.n.x, 1, stream);
					}
					current = current.n;
				  } else {
					if (isSubject) {
					  points = current.p.z;
					  for (i = points.length - 1; i >= 0; --i) stream.point((point = points[i])[0], point[1]);
					} else {
					  interpolate(current.x, current.p.x, -1, stream);
					}
					current = current.p;
				  }
				  current = current.o;
				  points = current.z;
				  isSubject = !isSubject;
				} while (!current.v);
				stream.lineEnd();
			  }
			};

			function link$1(array) {
			  if (!(n = array.length)) return;
			  var n,
				  i = 0,
				  a = array[0],
				  b;
			  while (++i < n) {
				a.n = b = array[i];
				b.p = a;
				a = b;
			  }
			  a.n = b = array[0];
			  b.p = a;
			}

			var clipMax = 1e9;
			var clipMin = -clipMax;

			// TODO Use d3-polygon’s polygonContains here for the ring check?
			// TODO Eliminate duplicate buffering in clipBuffer and polygon.push?

			function clipExtent(x0, y0, x1, y1) {

			  function visible(x, y) {
				return x0 <= x && x <= x1 && y0 <= y && y <= y1;
			  }

			  function interpolate(from, to, direction, stream) {
				var a = 0, a1 = 0;
				if (from == null
					|| (a = corner(from, direction)) !== (a1 = corner(to, direction))
					|| comparePoint(from, to) < 0 ^ direction > 0) {
				  do stream.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
				  while ((a = (a + direction + 4) % 4) !== a1);
				} else {
				  stream.point(to[0], to[1]);
				}
			  }

			  function corner(p, direction) {
				return abs(p[0] - x0) < epsilon$4 ? direction > 0 ? 0 : 3
					: abs(p[0] - x1) < epsilon$4 ? direction > 0 ? 2 : 1
					: abs(p[1] - y0) < epsilon$4 ? direction > 0 ? 1 : 0
					: direction > 0 ? 3 : 2; // abs(p[1] - y1) < epsilon
			  }

			  function compareIntersection(a, b) {
				return comparePoint(a.x, b.x);
			  }

			  function comparePoint(a, b) {
				var ca = corner(a, 1),
					cb = corner(b, 1);
				return ca !== cb ? ca - cb
					: ca === 0 ? b[1] - a[1]
					: ca === 1 ? a[0] - b[0]
					: ca === 2 ? a[1] - b[1]
					: b[0] - a[0];
			  }

			  return function(stream) {
				var activeStream = stream,
					bufferStream = clipBuffer(),
					segments,
					polygon,
					ring,
					x__, y__, v__, // first point
					x_, y_, v_, // previous point
					first,
					clean;

				var clipStream = {
				  point: point,
				  lineStart: lineStart,
				  lineEnd: lineEnd,
				  polygonStart: polygonStart,
				  polygonEnd: polygonEnd
				};

				function point(x, y) {
				  if (visible(x, y)) activeStream.point(x, y);
				}

				function polygonInside() {
				  var winding = 0;

				  for (var i = 0, n = polygon.length; i < n; ++i) {
					for (var ring = polygon[i], j = 1, m = ring.length, point = ring[0], a0, a1, b0 = point[0], b1 = point[1]; j < m; ++j) {
					  a0 = b0, a1 = b1, point = ring[j], b0 = point[0], b1 = point[1];
					  if (a1 <= y1) { if (b1 > y1 && (b0 - a0) * (y1 - a1) > (b1 - a1) * (x0 - a0)) ++winding; }
					  else { if (b1 <= y1 && (b0 - a0) * (y1 - a1) < (b1 - a1) * (x0 - a0)) --winding; }
					}
				  }

				  return winding;
				}

				// Buffer geometry within a polygon and then clip it en masse.
				function polygonStart() {
				  activeStream = bufferStream, segments = [], polygon = [], clean = true;
				}

				function polygonEnd() {
				  var startInside = polygonInside(),
					  cleanInside = clean && startInside,
					  visible = (segments = merge(segments)).length;
				  if (cleanInside || visible) {
					stream.polygonStart();
					if (cleanInside) {
					  stream.lineStart();
					  interpolate(null, null, 1, stream);
					  stream.lineEnd();
					}
					if (visible) {
					  clipPolygon(segments, compareIntersection, startInside, interpolate, stream);
					}
					stream.polygonEnd();
				  }
				  activeStream = stream, segments = polygon = ring = null;
				}

				function lineStart() {
				  clipStream.point = linePoint;
				  if (polygon) polygon.push(ring = []);
				  first = true;
				  v_ = false;
				  x_ = y_ = NaN;
				}

				// TODO rather than special-case polygons, simply handle them separately.
				// Ideally, coincident intersection points should be jittered to avoid
				// clipping issues.
				function lineEnd() {
				  if (segments) {
					linePoint(x__, y__);
					if (v__ && v_) bufferStream.rejoin();
					segments.push(bufferStream.result());
				  }
				  clipStream.point = point;
				  if (v_) activeStream.lineEnd();
				}

				function linePoint(x, y) {
				  var v = visible(x, y);
				  if (polygon) ring.push([x, y]);
				  if (first) {
					x__ = x, y__ = y, v__ = v;
					first = false;
					if (v) {
					  activeStream.lineStart();
					  activeStream.point(x, y);
					}
				  } else {
					if (v && v_) activeStream.point(x, y);
					else {
					  var a = [x_ = Math.max(clipMin, Math.min(clipMax, x_)), y_ = Math.max(clipMin, Math.min(clipMax, y_))],
						  b = [x = Math.max(clipMin, Math.min(clipMax, x)), y = Math.max(clipMin, Math.min(clipMax, y))];
					  if (clipLine(a, b, x0, y0, x1, y1)) {
						if (!v_) {
						  activeStream.lineStart();
						  activeStream.point(a[0], a[1]);
						}
						activeStream.point(b[0], b[1]);
						if (!v) activeStream.lineEnd();
						clean = false;
					  } else if (v) {
						activeStream.lineStart();
						activeStream.point(x, y);
						clean = false;
					  }
					}
				  }
				  x_ = x, y_ = y, v_ = v;
				}

				return clipStream;
			  };
			}

			var extent$1 = function() {
			  var x0 = 0,
				  y0 = 0,
				  x1 = 960,
				  y1 = 500,
				  cache,
				  cacheStream,
				  clip;

			  return clip = {
				stream: function(stream) {
				  return cache && cacheStream === stream ? cache : cache = clipExtent(x0, y0, x1, y1)(cacheStream = stream);
				},
				extent: function(_) {
				  return arguments.length ? (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1], cache = cacheStream = null, clip) : [[x0, y0], [x1, y1]];
				}
			  };
			};

			var lengthSum = adder();
			var lambda0$2;
			var sinPhi0$1;
			var cosPhi0$1;

			var lengthStream = {
			  sphere: noop$2,
			  point: noop$2,
			  lineStart: lengthLineStart,
			  lineEnd: noop$2,
			  polygonStart: noop$2,
			  polygonEnd: noop$2
			};

			function lengthLineStart() {
			  lengthStream.point = lengthPointFirst;
			  lengthStream.lineEnd = lengthLineEnd;
			}

			function lengthLineEnd() {
			  lengthStream.point = lengthStream.lineEnd = noop$2;
			}

			function lengthPointFirst(lambda, phi) {
			  lambda *= radians, phi *= radians;
			  lambda0$2 = lambda, sinPhi0$1 = sin$1(phi), cosPhi0$1 = cos$1(phi);
			  lengthStream.point = lengthPoint;
			}

			function lengthPoint(lambda, phi) {
			  lambda *= radians, phi *= radians;
			  var sinPhi = sin$1(phi),
				  cosPhi = cos$1(phi),
				  delta = abs(lambda - lambda0$2),
				  cosDelta = cos$1(delta),
				  sinDelta = sin$1(delta),
				  x = cosPhi * sinDelta,
				  y = cosPhi0$1 * sinPhi - sinPhi0$1 * cosPhi * cosDelta,
				  z = sinPhi0$1 * sinPhi + cosPhi0$1 * cosPhi * cosDelta;
			  lengthSum.add(atan2(sqrt$1(x * x + y * y), z));
			  lambda0$2 = lambda, sinPhi0$1 = sinPhi, cosPhi0$1 = cosPhi;
			}

			var length$2 = function(object) {
			  lengthSum.reset();
			  geoStream(object, lengthStream);
			  return +lengthSum;
			};

			var coordinates = [null, null];
			var object$1 = {type: "LineString", coordinates: coordinates};

			var distance = function(a, b) {
			  coordinates[0] = a;
			  coordinates[1] = b;
			  return length$2(object$1);
			};

			function graticuleX(y0, y1, dy) {
			  var y = range(y0, y1 - epsilon$4, dy).concat(y1);
			  return function(x) { return y.map(function(y) { return [x, y]; }); };
			}

			function graticuleY(x0, x1, dx) {
			  var x = range(x0, x1 - epsilon$4, dx).concat(x1);
			  return function(y) { return x.map(function(x) { return [x, y]; }); };
			}

			function graticule() {
			  var x1, x0, X1, X0,
				  y1, y0, Y1, Y0,
				  dx = 10, dy = dx, DX = 90, DY = 360,
				  x, y, X, Y,
				  precision = 2.5;

			  function graticule() {
				return {type: "MultiLineString", coordinates: lines()};
			  }

			  function lines() {
				return range(ceil(X0 / DX) * DX, X1, DX).map(X)
					.concat(range(ceil(Y0 / DY) * DY, Y1, DY).map(Y))
					.concat(range(ceil(x0 / dx) * dx, x1, dx).filter(function(x) { return abs(x % DX) > epsilon$4; }).map(x))
					.concat(range(ceil(y0 / dy) * dy, y1, dy).filter(function(y) { return abs(y % DY) > epsilon$4; }).map(y));
			  }

			  graticule.lines = function() {
				return lines().map(function(coordinates) { return {type: "LineString", coordinates: coordinates}; });
			  };

			  graticule.outline = function() {
				return {
				  type: "Polygon",
				  coordinates: [
					X(X0).concat(
					Y(Y1).slice(1),
					X(X1).reverse().slice(1),
					Y(Y0).reverse().slice(1))
				  ]
				};
			  };

			  graticule.extent = function(_) {
				if (!arguments.length) return graticule.extentMinor();
				return graticule.extentMajor(_).extentMinor(_);
			  };

			  graticule.extentMajor = function(_) {
				if (!arguments.length) return [[X0, Y0], [X1, Y1]];
				X0 = +_[0][0], X1 = +_[1][0];
				Y0 = +_[0][1], Y1 = +_[1][1];
				if (X0 > X1) _ = X0, X0 = X1, X1 = _;
				if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _;
				return graticule.precision(precision);
			  };

			  graticule.extentMinor = function(_) {
				if (!arguments.length) return [[x0, y0], [x1, y1]];
				x0 = +_[0][0], x1 = +_[1][0];
				y0 = +_[0][1], y1 = +_[1][1];
				if (x0 > x1) _ = x0, x0 = x1, x1 = _;
				if (y0 > y1) _ = y0, y0 = y1, y1 = _;
				return graticule.precision(precision);
			  };

			  graticule.step = function(_) {
				if (!arguments.length) return graticule.stepMinor();
				return graticule.stepMajor(_).stepMinor(_);
			  };

			  graticule.stepMajor = function(_) {
				if (!arguments.length) return [DX, DY];
				DX = +_[0], DY = +_[1];
				return graticule;
			  };

			  graticule.stepMinor = function(_) {
				if (!arguments.length) return [dx, dy];
				dx = +_[0], dy = +_[1];
				return graticule;
			  };

			  graticule.precision = function(_) {
				if (!arguments.length) return precision;
				precision = +_;
				x = graticuleX(y0, y1, 90);
				y = graticuleY(x0, x1, precision);
				X = graticuleX(Y0, Y1, 90);
				Y = graticuleY(X0, X1, precision);
				return graticule;
			  };

			  return graticule
				  .extentMajor([[-180, -90 + epsilon$4], [180, 90 - epsilon$4]])
				  .extentMinor([[-180, -80 - epsilon$4], [180, 80 + epsilon$4]]);
			}

			function graticule10() {
			  return graticule()();
			}

			var interpolate$2 = function(a, b) {
			  var x0 = a[0] * radians,
				  y0 = a[1] * radians,
				  x1 = b[0] * radians,
				  y1 = b[1] * radians,
				  cy0 = cos$1(y0),
				  sy0 = sin$1(y0),
				  cy1 = cos$1(y1),
				  sy1 = sin$1(y1),
				  kx0 = cy0 * cos$1(x0),
				  ky0 = cy0 * sin$1(x0),
				  kx1 = cy1 * cos$1(x1),
				  ky1 = cy1 * sin$1(x1),
				  d = 2 * asin$1(sqrt$1(haversin(y1 - y0) + cy0 * cy1 * haversin(x1 - x0))),
				  k = sin$1(d);

			  var interpolate = d ? function(t) {
				var B = sin$1(t *= d) / k,
					A = sin$1(d - t) / k,
					x = A * kx0 + B * kx1,
					y = A * ky0 + B * ky1,
					z = A * sy0 + B * sy1;
				return [
				  atan2(y, x) * degrees$1,
				  atan2(z, sqrt$1(x * x + y * y)) * degrees$1
				];
			  } : function() {
				return [x0 * degrees$1, y0 * degrees$1];
			  };

			  interpolate.distance = d;

			  return interpolate;
			};

			var identity$7 = function(x) {
			  return x;
			};

			var areaSum$1 = adder();
			var areaRingSum$1 = adder();
			var x00;
			var y00;
			var x0$1;
			var y0$1;

			var areaStream$1 = {
			  point: noop$2,
			  lineStart: noop$2,
			  lineEnd: noop$2,
			  polygonStart: function() {
				areaStream$1.lineStart = areaRingStart$1;
				areaStream$1.lineEnd = areaRingEnd$1;
			  },
			  polygonEnd: function() {
				areaStream$1.lineStart = areaStream$1.lineEnd = areaStream$1.point = noop$2;
				areaSum$1.add(abs(areaRingSum$1));
				areaRingSum$1.reset();
			  },
			  result: function() {
				var area = areaSum$1 / 2;
				areaSum$1.reset();
				return area;
			  }
			};

			function areaRingStart$1() {
			  areaStream$1.point = areaPointFirst$1;
			}

			function areaPointFirst$1(x, y) {
			  areaStream$1.point = areaPoint$1;
			  x00 = x0$1 = x, y00 = y0$1 = y;
			}

			function areaPoint$1(x, y) {
			  areaRingSum$1.add(y0$1 * x - x0$1 * y);
			  x0$1 = x, y0$1 = y;
			}

			function areaRingEnd$1() {
			  areaPoint$1(x00, y00);
			}

			var x0$2 = Infinity;
			var y0$2 = x0$2;
			var x1 = -x0$2;
			var y1 = x1;

			var boundsStream$1 = {
			  point: boundsPoint$1,
			  lineStart: noop$2,
			  lineEnd: noop$2,
			  polygonStart: noop$2,
			  polygonEnd: noop$2,
			  result: function() {
				var bounds = [[x0$2, y0$2], [x1, y1]];
				x1 = y1 = -(y0$2 = x0$2 = Infinity);
				return bounds;
			  }
			};

			function boundsPoint$1(x, y) {
			  if (x < x0$2) x0$2 = x;
			  if (x > x1) x1 = x;
			  if (y < y0$2) y0$2 = y;
			  if (y > y1) y1 = y;
			}

			// TODO Enforce positive area for exterior, negative area for interior?

			var X0$1 = 0;
			var Y0$1 = 0;
			var Z0$1 = 0;
			var X1$1 = 0;
			var Y1$1 = 0;
			var Z1$1 = 0;
			var X2$1 = 0;
			var Y2$1 = 0;
			var Z2$1 = 0;
			var x00$1;
			var y00$1;
			var x0$3;
			var y0$3;

			var centroidStream$1 = {
			  point: centroidPoint$1,
			  lineStart: centroidLineStart$1,
			  lineEnd: centroidLineEnd$1,
			  polygonStart: function() {
				centroidStream$1.lineStart = centroidRingStart$1;
				centroidStream$1.lineEnd = centroidRingEnd$1;
			  },
			  polygonEnd: function() {
				centroidStream$1.point = centroidPoint$1;
				centroidStream$1.lineStart = centroidLineStart$1;
				centroidStream$1.lineEnd = centroidLineEnd$1;
			  },
			  result: function() {
				var centroid = Z2$1 ? [X2$1 / Z2$1, Y2$1 / Z2$1]
					: Z1$1 ? [X1$1 / Z1$1, Y1$1 / Z1$1]
					: Z0$1 ? [X0$1 / Z0$1, Y0$1 / Z0$1]
					: [NaN, NaN];
				X0$1 = Y0$1 = Z0$1 =
				X1$1 = Y1$1 = Z1$1 =
				X2$1 = Y2$1 = Z2$1 = 0;
				return centroid;
			  }
			};

			function centroidPoint$1(x, y) {
			  X0$1 += x;
			  Y0$1 += y;
			  ++Z0$1;
			}

			function centroidLineStart$1() {
			  centroidStream$1.point = centroidPointFirstLine;
			}

			function centroidPointFirstLine(x, y) {
			  centroidStream$1.point = centroidPointLine;
			  centroidPoint$1(x0$3 = x, y0$3 = y);
			}

			function centroidPointLine(x, y) {
			  var dx = x - x0$3, dy = y - y0$3, z = sqrt$1(dx * dx + dy * dy);
			  X1$1 += z * (x0$3 + x) / 2;
			  Y1$1 += z * (y0$3 + y) / 2;
			  Z1$1 += z;
			  centroidPoint$1(x0$3 = x, y0$3 = y);
			}

			function centroidLineEnd$1() {
			  centroidStream$1.point = centroidPoint$1;
			}

			function centroidRingStart$1() {
			  centroidStream$1.point = centroidPointFirstRing;
			}

			function centroidRingEnd$1() {
			  centroidPointRing(x00$1, y00$1);
			}

			function centroidPointFirstRing(x, y) {
			  centroidStream$1.point = centroidPointRing;
			  centroidPoint$1(x00$1 = x0$3 = x, y00$1 = y0$3 = y);
			}

			function centroidPointRing(x, y) {
			  var dx = x - x0$3,
				  dy = y - y0$3,
				  z = sqrt$1(dx * dx + dy * dy);

			  X1$1 += z * (x0$3 + x) / 2;
			  Y1$1 += z * (y0$3 + y) / 2;
			  Z1$1 += z;

			  z = y0$3 * x - x0$3 * y;
			  X2$1 += z * (x0$3 + x);
			  Y2$1 += z * (y0$3 + y);
			  Z2$1 += z * 3;
			  centroidPoint$1(x0$3 = x, y0$3 = y);
			}

			function PathContext(context) {
			  this._context = context;
			}

			PathContext.prototype = {
			  _radius: 4.5,
			  pointRadius: function(_) {
				return this._radius = _, this;
			  },
			  polygonStart: function() {
				this._line = 0;
			  },
			  polygonEnd: function() {
				this._line = NaN;
			  },
			  lineStart: function() {
				this._point = 0;
			  },
			  lineEnd: function() {
				if (this._line === 0) this._context.closePath();
				this._point = NaN;
			  },
			  point: function(x, y) {
				switch (this._point) {
				  case 0: {
					this._context.moveTo(x, y);
					this._point = 1;
					break;
				  }
				  case 1: {
					this._context.lineTo(x, y);
					break;
				  }
				  default: {
					this._context.moveTo(x + this._radius, y);
					this._context.arc(x, y, this._radius, 0, tau$4);
					break;
				  }
				}
			  },
			  result: noop$2
			};

			function PathString() {
			  this._string = [];
			}

			PathString.prototype = {
			  _circle: circle$2(4.5),
			  pointRadius: function(_) {
				return this._circle = circle$2(_), this;
			  },
			  polygonStart: function() {
				this._line = 0;
			  },
			  polygonEnd: function() {
				this._line = NaN;
			  },
			  lineStart: function() {
				this._point = 0;
			  },
			  lineEnd: function() {
				if (this._line === 0) this._string.push("Z");
				this._point = NaN;
			  },
			  point: function(x, y) {
				switch (this._point) {
				  case 0: {
					this._string.push("M", x, ",", y);
					this._point = 1;
					break;
				  }
				  case 1: {
					this._string.push("L", x, ",", y);
					break;
				  }
				  default: {
					this._string.push("M", x, ",", y, this._circle);
					break;
				  }
				}
			  },
			  result: function() {
				if (this._string.length) {
				  var result = this._string.join("");
				  this._string = [];
				  return result;
				}
			  }
			};

			function circle$2(radius) {
			  return "m0," + radius
				  + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius
				  + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius
				  + "z";
			}

			var index$3 = function(projection, context) {
			  var pointRadius = 4.5,
				  projectionStream,
				  contextStream;

			  function path(object) {
				if (object) {
				  if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
				  geoStream(object, projectionStream(contextStream));
				}
				return contextStream.result();
			  }

			  path.area = function(object) {
				geoStream(object, projectionStream(areaStream$1));
				return areaStream$1.result();
			  };

			  path.bounds = function(object) {
				geoStream(object, projectionStream(boundsStream$1));
				return boundsStream$1.result();
			  };

			  path.centroid = function(object) {
				geoStream(object, projectionStream(centroidStream$1));
				return centroidStream$1.result();
			  };

			  path.projection = function(_) {
				return arguments.length ? (projectionStream = (projection = _) == null ? identity$7 : _.stream, path) : projection;
			  };

			  path.context = function(_) {
				if (!arguments.length) return context;
				contextStream = (context = _) == null ? new PathString : new PathContext(_);
				if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
				return path;
			  };

			  path.pointRadius = function(_) {
				if (!arguments.length) return pointRadius;
				pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
				return path;
			  };

			  return path.projection(projection).context(context);
			};

			var sum$2 = adder();

			var polygonContains = function(polygon, point) {
			  var lambda = point[0],
				  phi = point[1],
				  normal = [sin$1(lambda), -cos$1(lambda), 0],
				  angle = 0,
				  winding = 0;

			  sum$2.reset();

			  for (var i = 0, n = polygon.length; i < n; ++i) {
				if (!(m = (ring = polygon[i]).length)) continue;
				var ring,
					m,
					point0 = ring[m - 1],
					lambda0 = point0[0],
					phi0 = point0[1] / 2 + quarterPi,
					sinPhi0 = sin$1(phi0),
					cosPhi0 = cos$1(phi0);

				for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) {
				  var point1 = ring[j],
					  lambda1 = point1[0],
					  phi1 = point1[1] / 2 + quarterPi,
					  sinPhi1 = sin$1(phi1),
					  cosPhi1 = cos$1(phi1),
					  delta = lambda1 - lambda0,
					  sign$$1 = delta >= 0 ? 1 : -1,
					  absDelta = sign$$1 * delta,
					  antimeridian = absDelta > pi$4,
					  k = sinPhi0 * sinPhi1;

				  sum$2.add(atan2(k * sign$$1 * sin$1(absDelta), cosPhi0 * cosPhi1 + k * cos$1(absDelta)));
				  angle += antimeridian ? delta + sign$$1 * tau$4 : delta;

				  // Are the longitudes either side of the point’s meridian (lambda),
				  // and are the latitudes smaller than the parallel (phi)?
				  if (antimeridian ^ lambda0 >= lambda ^ lambda1 >= lambda) {
					var arc = cartesianCross(cartesian(point0), cartesian(point1));
					cartesianNormalizeInPlace(arc);
					var intersection = cartesianCross(normal, arc);
					cartesianNormalizeInPlace(intersection);
					var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * asin$1(intersection[2]);
					if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) {
					  winding += antimeridian ^ delta >= 0 ? 1 : -1;
					}
				  }
				}
			  }

			  // First, determine whether the South pole is inside or outside:
			  //
			  // It is inside if:
			  // * the polygon winds around it in a clockwise direction.
			  // * the polygon does not (cumulatively) wind around it, but has a negative
			  //   (counter-clockwise) area.
			  //
			  // Second, count the (signed) number of times a segment crosses a lambda
			  // from the point to the South pole.  If it is zero, then the point is the
			  // same side as the South pole.

			  return (angle < -epsilon$4 || angle < epsilon$4 && sum$2 < -epsilon$4) ^ (winding & 1);
			};

			var clip = function(pointVisible, clipLine, interpolate, start) {
			  return function(rotate, sink) {
				var line = clipLine(sink),
					rotatedStart = rotate.invert(start[0], start[1]),
					ringBuffer = clipBuffer(),
					ringSink = clipLine(ringBuffer),
					polygonStarted = false,
					polygon,
					segments,
					ring;

				var clip = {
				  point: point,
				  lineStart: lineStart,
				  lineEnd: lineEnd,
				  polygonStart: function() {
					clip.point = pointRing;
					clip.lineStart = ringStart;
					clip.lineEnd = ringEnd;
					segments = [];
					polygon = [];
				  },
				  polygonEnd: function() {
					clip.point = point;
					clip.lineStart = lineStart;
					clip.lineEnd = lineEnd;
					segments = merge(segments);
					var startInside = polygonContains(polygon, rotatedStart);
					if (segments.length) {
					  if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
					  clipPolygon(segments, compareIntersection, startInside, interpolate, sink);
					} else if (startInside) {
					  if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
					  sink.lineStart();
					  interpolate(null, null, 1, sink);
					  sink.lineEnd();
					}
					if (polygonStarted) sink.polygonEnd(), polygonStarted = false;
					segments = polygon = null;
				  },
				  sphere: function() {
					sink.polygonStart();
					sink.lineStart();
					interpolate(null, null, 1, sink);
					sink.lineEnd();
					sink.polygonEnd();
				  }
				};

				function point(lambda, phi) {
				  var point = rotate(lambda, phi);
				  if (pointVisible(lambda = point[0], phi = point[1])) sink.point(lambda, phi);
				}

				function pointLine(lambda, phi) {
				  var point = rotate(lambda, phi);
				  line.point(point[0], point[1]);
				}

				function lineStart() {
				  clip.point = pointLine;
				  line.lineStart();
				}

				function lineEnd() {
				  clip.point = point;
				  line.lineEnd();
				}

				function pointRing(lambda, phi) {
				  ring.push([lambda, phi]);
				  var point = rotate(lambda, phi);
				  ringSink.point(point[0], point[1]);
				}

				function ringStart() {
				  ringSink.lineStart();
				  ring = [];
				}

				function ringEnd() {
				  pointRing(ring[0][0], ring[0][1]);
				  ringSink.lineEnd();

				  var clean = ringSink.clean(),
					  ringSegments = ringBuffer.result(),
					  i, n = ringSegments.length, m,
					  segment,
					  point;

				  ring.pop();
				  polygon.push(ring);
				  ring = null;

				  if (!n) return;

				  // No intersections.
				  if (clean & 1) {
					segment = ringSegments[0];
					if ((m = segment.length - 1) > 0) {
					  if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
					  sink.lineStart();
					  for (i = 0; i < m; ++i) sink.point((point = segment[i])[0], point[1]);
					  sink.lineEnd();
					}
					return;
				  }

				  // Rejoin connected segments.
				  // TODO reuse ringBuffer.rejoin()?
				  if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));

				  segments.push(ringSegments.filter(validSegment));
				}

				return clip;
			  };
			};

			function validSegment(segment) {
			  return segment.length > 1;
			}

			// Intersections are sorted along the clip edge. For both antimeridian cutting
			// and circle clipping, the same comparison is used.
			function compareIntersection(a, b) {
			  return ((a = a.x)[0] < 0 ? a[1] - halfPi$3 - epsilon$4 : halfPi$3 - a[1])
				   - ((b = b.x)[0] < 0 ? b[1] - halfPi$3 - epsilon$4 : halfPi$3 - b[1]);
			}

			var clipAntimeridian = clip(
			  function() { return true; },
			  clipAntimeridianLine,
			  clipAntimeridianInterpolate,
			  [-pi$4, -halfPi$3]
			);

			// Takes a line and cuts into visible segments. Return values: 0 - there were
			// intersections or the line was empty; 1 - no intersections; 2 - there were
			// intersections, and the first and last segments should be rejoined.
			function clipAntimeridianLine(stream) {
			  var lambda0 = NaN,
				  phi0 = NaN,
				  sign0 = NaN,
				  clean; // no intersections

			  return {
				lineStart: function() {
				  stream.lineStart();
				  clean = 1;
				},
				point: function(lambda1, phi1) {
				  var sign1 = lambda1 > 0 ? pi$4 : -pi$4,
					  delta = abs(lambda1 - lambda0);
				  if (abs(delta - pi$4) < epsilon$4) { // line crosses a pole
					stream.point(lambda0, phi0 = (phi0 + phi1) / 2 > 0 ? halfPi$3 : -halfPi$3);
					stream.point(sign0, phi0);
					stream.lineEnd();
					stream.lineStart();
					stream.point(sign1, phi0);
					stream.point(lambda1, phi0);
					clean = 0;
				  } else if (sign0 !== sign1 && delta >= pi$4) { // line crosses antimeridian
					if (abs(lambda0 - sign0) < epsilon$4) lambda0 -= sign0 * epsilon$4; // handle degeneracies
					if (abs(lambda1 - sign1) < epsilon$4) lambda1 -= sign1 * epsilon$4;
					phi0 = clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1);
					stream.point(sign0, phi0);
					stream.lineEnd();
					stream.lineStart();
					stream.point(sign1, phi0);
					clean = 0;
				  }
				  stream.point(lambda0 = lambda1, phi0 = phi1);
				  sign0 = sign1;
				},
				lineEnd: function() {
				  stream.lineEnd();
				  lambda0 = phi0 = NaN;
				},
				clean: function() {
				  return 2 - clean; // if intersections, rejoin first and last segments
				}
			  };
			}

			function clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1) {
			  var cosPhi0,
				  cosPhi1,
				  sinLambda0Lambda1 = sin$1(lambda0 - lambda1);
			  return abs(sinLambda0Lambda1) > epsilon$4
				  ? atan((sin$1(phi0) * (cosPhi1 = cos$1(phi1)) * sin$1(lambda1)
					  - sin$1(phi1) * (cosPhi0 = cos$1(phi0)) * sin$1(lambda0))
					  / (cosPhi0 * cosPhi1 * sinLambda0Lambda1))
				  : (phi0 + phi1) / 2;
			}

			function clipAntimeridianInterpolate(from, to, direction, stream) {
			  var phi;
			  if (from == null) {
				phi = direction * halfPi$3;
				stream.point(-pi$4, phi);
				stream.point(0, phi);
				stream.point(pi$4, phi);
				stream.point(pi$4, 0);
				stream.point(pi$4, -phi);
				stream.point(0, -phi);
				stream.point(-pi$4, -phi);
				stream.point(-pi$4, 0);
				stream.point(-pi$4, phi);
			  } else if (abs(from[0] - to[0]) > epsilon$4) {
				var lambda = from[0] < to[0] ? pi$4 : -pi$4;
				phi = direction * lambda / 2;
				stream.point(-lambda, phi);
				stream.point(0, phi);
				stream.point(lambda, phi);
			  } else {
				stream.point(to[0], to[1]);
			  }
			}

			var clipCircle = function(radius, delta) {
			  var cr = cos$1(radius),
				  smallRadius = cr > 0,
				  notHemisphere = abs(cr) > epsilon$4; // TODO optimise for this common case

			  function interpolate(from, to, direction, stream) {
				circleStream(stream, radius, delta, direction, from, to);
			  }

			  function visible(lambda, phi) {
				return cos$1(lambda) * cos$1(phi) > cr;
			  }

			  // Takes a line and cuts into visible segments. Return values used for polygon
			  // clipping: 0 - there were intersections or the line was empty; 1 - no
			  // intersections 2 - there were intersections, and the first and last segments
			  // should be rejoined.
			  function clipLine(stream) {
				var point0, // previous point
					c0, // code for previous point
					v0, // visibility of previous point
					v00, // visibility of first point
					clean; // no intersections
				return {
				  lineStart: function() {
					v00 = v0 = false;
					clean = 1;
				  },
				  point: function(lambda, phi) {
					var point1 = [lambda, phi],
						point2,
						v = visible(lambda, phi),
						c = smallRadius
						  ? v ? 0 : code(lambda, phi)
						  : v ? code(lambda + (lambda < 0 ? pi$4 : -pi$4), phi) : 0;
					if (!point0 && (v00 = v0 = v)) stream.lineStart();
					// Handle degeneracies.
					// TODO ignore if not clipping polygons.
					if (v !== v0) {
					  point2 = intersect(point0, point1);
					  if (pointEqual(point0, point2) || pointEqual(point1, point2)) {
						point1[0] += epsilon$4;
						point1[1] += epsilon$4;
						v = visible(point1[0], point1[1]);
					  }
					}
					if (v !== v0) {
					  clean = 0;
					  if (v) {
						// outside going in
						stream.lineStart();
						point2 = intersect(point1, point0);
						stream.point(point2[0], point2[1]);
					  } else {
						// inside going out
						point2 = intersect(point0, point1);
						stream.point(point2[0], point2[1]);
						stream.lineEnd();
					  }
					  point0 = point2;
					} else if (notHemisphere && point0 && smallRadius ^ v) {
					  var t;
					  // If the codes for two points are different, or are both zero,
					  // and there this segment intersects with the small circle.
					  if (!(c & c0) && (t = intersect(point1, point0, true))) {
						clean = 0;
						if (smallRadius) {
						  stream.lineStart();
						  stream.point(t[0][0], t[0][1]);
						  stream.point(t[1][0], t[1][1]);
						  stream.lineEnd();
						} else {
						  stream.point(t[1][0], t[1][1]);
						  stream.lineEnd();
						  stream.lineStart();
						  stream.point(t[0][0], t[0][1]);
						}
					  }
					}
					if (v && (!point0 || !pointEqual(point0, point1))) {
					  stream.point(point1[0], point1[1]);
					}
					point0 = point1, v0 = v, c0 = c;
				  },
				  lineEnd: function() {
					if (v0) stream.lineEnd();
					point0 = null;
				  },
				  // Rejoin first and last segments if there were intersections and the first
				  // and last points were visible.
				  clean: function() {
					return clean | ((v00 && v0) << 1);
				  }
				};
			  }

			  // Intersects the great circle between a and b with the clip circle.
			  function intersect(a, b, two) {
				var pa = cartesian(a),
					pb = cartesian(b);

				// We have two planes, n1.p = d1 and n2.p = d2.
				// Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).
				var n1 = [1, 0, 0], // normal
					n2 = cartesianCross(pa, pb),
					n2n2 = cartesianDot(n2, n2),
					n1n2 = n2[0], // cartesianDot(n1, n2),
					determinant = n2n2 - n1n2 * n1n2;

				// Two polar points.
				if (!determinant) return !two && a;

				var c1 =  cr * n2n2 / determinant,
					c2 = -cr * n1n2 / determinant,
					n1xn2 = cartesianCross(n1, n2),
					A = cartesianScale(n1, c1),
					B = cartesianScale(n2, c2);
				cartesianAddInPlace(A, B);

				// Solve |p(t)|^2 = 1.
				var u = n1xn2,
					w = cartesianDot(A, u),
					uu = cartesianDot(u, u),
					t2 = w * w - uu * (cartesianDot(A, A) - 1);

				if (t2 < 0) return;

				var t = sqrt$1(t2),
					q = cartesianScale(u, (-w - t) / uu);
				cartesianAddInPlace(q, A);
				q = spherical(q);

				if (!two) return q;

				// Two intersection points.
				var lambda0 = a[0],
					lambda1 = b[0],
					phi0 = a[1],
					phi1 = b[1],
					z;

				if (lambda1 < lambda0) z = lambda0, lambda0 = lambda1, lambda1 = z;

				var delta = lambda1 - lambda0,
					polar = abs(delta - pi$4) < epsilon$4,
					meridian = polar || delta < epsilon$4;

				if (!polar && phi1 < phi0) z = phi0, phi0 = phi1, phi1 = z;

				// Check that the first point is between a and b.
				if (meridian
					? polar
					  ? phi0 + phi1 > 0 ^ q[1] < (abs(q[0] - lambda0) < epsilon$4 ? phi0 : phi1)
					  : phi0 <= q[1] && q[1] <= phi1
					: delta > pi$4 ^ (lambda0 <= q[0] && q[0] <= lambda1)) {
				  var q1 = cartesianScale(u, (-w + t) / uu);
				  cartesianAddInPlace(q1, A);
				  return [q, spherical(q1)];
				}
			  }

			  // Generates a 4-bit vector representing the location of a point relative to
			  // the small circle's bounding box.
			  function code(lambda, phi) {
				var r = smallRadius ? radius : pi$4 - radius,
					code = 0;
				if (lambda < -r) code |= 1; // left
				else if (lambda > r) code |= 2; // right
				if (phi < -r) code |= 4; // below
				else if (phi > r) code |= 8; // above
				return code;
			  }

			  return clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-pi$4, radius - pi$4]);
			};

			var transform$1 = function(methods) {
			  return {
				stream: transformer(methods)
			  };
			};

			function transformer(methods) {
			  return function(stream) {
				var s = new TransformStream;
				for (var key in methods) s[key] = methods[key];
				s.stream = stream;
				return s;
			  };
			}

			function TransformStream() {}

			TransformStream.prototype = {
			  constructor: TransformStream,
			  point: function(x, y) { this.stream.point(x, y); },
			  sphere: function() { this.stream.sphere(); },
			  lineStart: function() { this.stream.lineStart(); },
			  lineEnd: function() { this.stream.lineEnd(); },
			  polygonStart: function() { this.stream.polygonStart(); },
			  polygonEnd: function() { this.stream.polygonEnd(); }
			};

			function fitExtent(projection, extent, object) {
			  var w = extent[1][0] - extent[0][0],
				  h = extent[1][1] - extent[0][1],
				  clip = projection.clipExtent && projection.clipExtent();

			  projection
				  .scale(150)
				  .translate([0, 0]);

			  if (clip != null) projection.clipExtent(null);

			  geoStream(object, projection.stream(boundsStream$1));

			  var b = boundsStream$1.result(),
				  k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1])),
				  x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2,
				  y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2;

			  if (clip != null) projection.clipExtent(clip);

			  return projection
				  .scale(k * 150)
				  .translate([x, y]);
			}

			function fitSize(projection, size, object) {
			  return fitExtent(projection, [[0, 0], size], object);
			}

			var maxDepth = 16;
			var cosMinDistance = cos$1(30 * radians); // cos(minimum angular distance)

			var resample = function(project, delta2) {
			  return +delta2 ? resample$1(project, delta2) : resampleNone(project);
			};

			function resampleNone(project) {
			  return transformer({
				point: function(x, y) {
				  x = project(x, y);
				  this.stream.point(x[0], x[1]);
				}
			  });
			}

			function resample$1(project, delta2) {

			  function resampleLineTo(x0, y0, lambda0, a0, b0, c0, x1, y1, lambda1, a1, b1, c1, depth, stream) {
				var dx = x1 - x0,
					dy = y1 - y0,
					d2 = dx * dx + dy * dy;
				if (d2 > 4 * delta2 && depth--) {
				  var a = a0 + a1,
					  b = b0 + b1,
					  c = c0 + c1,
					  m = sqrt$1(a * a + b * b + c * c),
					  phi2 = asin$1(c /= m),
					  lambda2 = abs(abs(c) - 1) < epsilon$4 || abs(lambda0 - lambda1) < epsilon$4 ? (lambda0 + lambda1) / 2 : atan2(b, a),
					  p = project(lambda2, phi2),
					  x2 = p[0],
					  y2 = p[1],
					  dx2 = x2 - x0,
					  dy2 = y2 - y0,
					  dz = dy * dx2 - dx * dy2;
				  if (dz * dz / d2 > delta2 // perpendicular projected distance
					  || abs((dx * dx2 + dy * dy2) / d2 - 0.5) > 0.3 // midpoint close to an end
					  || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance
					resampleLineTo(x0, y0, lambda0, a0, b0, c0, x2, y2, lambda2, a /= m, b /= m, c, depth, stream);
					stream.point(x2, y2);
					resampleLineTo(x2, y2, lambda2, a, b, c, x1, y1, lambda1, a1, b1, c1, depth, stream);
				  }
				}
			  }
			  return function(stream) {
				var lambda00, x00, y00, a00, b00, c00, // first point
					lambda0, x0, y0, a0, b0, c0; // previous point

				var resampleStream = {
				  point: point,
				  lineStart: lineStart,
				  lineEnd: lineEnd,
				  polygonStart: function() { stream.polygonStart(); resampleStream.lineStart = ringStart; },
				  polygonEnd: function() { stream.polygonEnd(); resampleStream.lineStart = lineStart; }
				};

				function point(x, y) {
				  x = project(x, y);
				  stream.point(x[0], x[1]);
				}

				function lineStart() {
				  x0 = NaN;
				  resampleStream.point = linePoint;
				  stream.lineStart();
				}

				function linePoint(lambda, phi) {
				  var c = cartesian([lambda, phi]), p = project(lambda, phi);
				  resampleLineTo(x0, y0, lambda0, a0, b0, c0, x0 = p[0], y0 = p[1], lambda0 = lambda, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream);
				  stream.point(x0, y0);
				}

				function lineEnd() {
				  resampleStream.point = point;
				  stream.lineEnd();
				}

				function ringStart() {
				  lineStart();
				  resampleStream.point = ringPoint;
				  resampleStream.lineEnd = ringEnd;
				}

				function ringPoint(lambda, phi) {
				  linePoint(lambda00 = lambda, phi), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
				  resampleStream.point = linePoint;
				}

				function ringEnd() {
				  resampleLineTo(x0, y0, lambda0, a0, b0, c0, x00, y00, lambda00, a00, b00, c00, maxDepth, stream);
				  resampleStream.lineEnd = lineEnd;
				  lineEnd();
				}

				return resampleStream;
			  };
			}

			var transformRadians = transformer({
			  point: function(x, y) {
				this.stream.point(x * radians, y * radians);
			  }
			});

			function projection(project) {
			  return projectionMutator(function() { return project; })();
			}

			function projectionMutator(projectAt) {
			  var project,
				  k = 150, // scale
				  x = 480, y = 250, // translate
				  dx, dy, lambda = 0, phi = 0, // center
				  deltaLambda = 0, deltaPhi = 0, deltaGamma = 0, rotate, projectRotate, // rotate
				  theta = null, preclip = clipAntimeridian, // clip angle
				  x0 = null, y0, x1, y1, postclip = identity$7, // clip extent
				  delta2 = 0.5, projectResample = resample(projectTransform, delta2), // precision
				  cache,
				  cacheStream;

			  function projection(point) {
				point = projectRotate(point[0] * radians, point[1] * radians);
				return [point[0] * k + dx, dy - point[1] * k];
			  }

			  function invert(point) {
				point = projectRotate.invert((point[0] - dx) / k, (dy - point[1]) / k);
				return point && [point[0] * degrees$1, point[1] * degrees$1];
			  }

			  function projectTransform(x, y) {
				return x = project(x, y), [x[0] * k + dx, dy - x[1] * k];
			  }

			  projection.stream = function(stream) {
				return cache && cacheStream === stream ? cache : cache = transformRadians(preclip(rotate, projectResample(postclip(cacheStream = stream))));
			  };

			  projection.clipAngle = function(_) {
				return arguments.length ? (preclip = +_ ? clipCircle(theta = _ * radians, 6 * radians) : (theta = null, clipAntimeridian), reset()) : theta * degrees$1;
			  };

			  projection.clipExtent = function(_) {
				return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity$7) : clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];
			  };

			  projection.scale = function(_) {
				return arguments.length ? (k = +_, recenter()) : k;
			  };

			  projection.translate = function(_) {
				return arguments.length ? (x = +_[0], y = +_[1], recenter()) : [x, y];
			  };

			  projection.center = function(_) {
				return arguments.length ? (lambda = _[0] % 360 * radians, phi = _[1] % 360 * radians, recenter()) : [lambda * degrees$1, phi * degrees$1];
			  };

			  projection.rotate = function(_) {
				return arguments.length ? (deltaLambda = _[0] % 360 * radians, deltaPhi = _[1] % 360 * radians, deltaGamma = _.length > 2 ? _[2] % 360 * radians : 0, recenter()) : [deltaLambda * degrees$1, deltaPhi * degrees$1, deltaGamma * degrees$1];
			  };

			  projection.precision = function(_) {
				return arguments.length ? (projectResample = resample(projectTransform, delta2 = _ * _), reset()) : sqrt$1(delta2);
			  };

			  projection.fitExtent = function(extent, object) {
				return fitExtent(projection, extent, object);
			  };

			  projection.fitSize = function(size, object) {
				return fitSize(projection, size, object);
			  };

			  function recenter() {
				projectRotate = compose(rotate = rotateRadians(deltaLambda, deltaPhi, deltaGamma), project);
				var center = project(lambda, phi);
				dx = x - center[0] * k;
				dy = y + center[1] * k;
				return reset();
			  }

			  function reset() {
				cache = cacheStream = null;
				return projection;
			  }

			  return function() {
				project = projectAt.apply(this, arguments);
				projection.invert = project.invert && invert;
				return recenter();
			  };
			}

			function conicProjection(projectAt) {
			  var phi0 = 0,
				  phi1 = pi$4 / 3,
				  m = projectionMutator(projectAt),
				  p = m(phi0, phi1);

			  p.parallels = function(_) {
				return arguments.length ? m(phi0 = _[0] * radians, phi1 = _[1] * radians) : [phi0 * degrees$1, phi1 * degrees$1];
			  };

			  return p;
			}

			function cylindricalEqualAreaRaw(phi0) {
			  var cosPhi0 = cos$1(phi0);

			  function forward(lambda, phi) {
				return [lambda * cosPhi0, sin$1(phi) / cosPhi0];
			  }

			  forward.invert = function(x, y) {
				return [x / cosPhi0, asin$1(y * cosPhi0)];
			  };

			  return forward;
			}

			function conicEqualAreaRaw(y0, y1) {
			  var sy0 = sin$1(y0), n = (sy0 + sin$1(y1)) / 2;

			  // Are the parallels symmetrical around the Equator?
			  if (abs(n) < epsilon$4) return cylindricalEqualAreaRaw(y0);

			  var c = 1 + sy0 * (2 * n - sy0), r0 = sqrt$1(c) / n;

			  function project(x, y) {
				var r = sqrt$1(c - 2 * n * sin$1(y)) / n;
				return [r * sin$1(x *= n), r0 - r * cos$1(x)];
			  }

			  project.invert = function(x, y) {
				var r0y = r0 - y;
				return [atan2(x, abs(r0y)) / n * sign$1(r0y), asin$1((c - (x * x + r0y * r0y) * n * n) / (2 * n))];
			  };

			  return project;
			}

			var conicEqualArea = function() {
			  return conicProjection(conicEqualAreaRaw)
				  .scale(155.424)
				  .center([0, 33.6442]);
			};

			var albers = function() {
			  return conicEqualArea()
				  .parallels([29.5, 45.5])
				  .scale(1070)
				  .translate([480, 250])
				  .rotate([96, 0])
				  .center([-0.6, 38.7]);
			};

			// The projections must have mutually exclusive clip regions on the sphere,
			// as this will avoid emitting interleaving lines and polygons.
			function multiplex(streams) {
			  var n = streams.length;
			  return {
				point: function(x, y) { var i = -1; while (++i < n) streams[i].point(x, y); },
				sphere: function() { var i = -1; while (++i < n) streams[i].sphere(); },
				lineStart: function() { var i = -1; while (++i < n) streams[i].lineStart(); },
				lineEnd: function() { var i = -1; while (++i < n) streams[i].lineEnd(); },
				polygonStart: function() { var i = -1; while (++i < n) streams[i].polygonStart(); },
				polygonEnd: function() { var i = -1; while (++i < n) streams[i].polygonEnd(); }
			  };
			}

			// A composite projection for the United States, configured by default for
			// 960×500. The projection also works quite well at 960×600 if you change the
			// scale to 1285 and adjust the translate accordingly. The set of standard
			// parallels for each region comes from USGS, which is published here:
			// http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers
			var albersUsa = function() {
			  var cache,
				  cacheStream,
				  lower48 = albers(), lower48Point,
				  alaska = conicEqualArea().rotate([154, 0]).center([-2, 58.5]).parallels([55, 65]), alaskaPoint, // EPSG:3338
				  hawaii = conicEqualArea().rotate([157, 0]).center([-3, 19.9]).parallels([8, 18]), hawaiiPoint, // ESRI:102007
				  point, pointStream = {point: function(x, y) { point = [x, y]; }};

			  function albersUsa(coordinates) {
				var x = coordinates[0], y = coordinates[1];
				return point = null,
					(lower48Point.point(x, y), point)
					|| (alaskaPoint.point(x, y), point)
					|| (hawaiiPoint.point(x, y), point);
			  }

			  albersUsa.invert = function(coordinates) {
				var k = lower48.scale(),
					t = lower48.translate(),
					x = (coordinates[0] - t[0]) / k,
					y = (coordinates[1] - t[1]) / k;
				return (y >= 0.120 && y < 0.234 && x >= -0.425 && x < -0.214 ? alaska
					: y >= 0.166 && y < 0.234 && x >= -0.214 && x < -0.115 ? hawaii
					: lower48).invert(coordinates);
			  };

			  albersUsa.stream = function(stream) {
				return cache && cacheStream === stream ? cache : cache = multiplex([lower48.stream(cacheStream = stream), alaska.stream(stream), hawaii.stream(stream)]);
			  };

			  albersUsa.precision = function(_) {
				if (!arguments.length) return lower48.precision();
				lower48.precision(_), alaska.precision(_), hawaii.precision(_);
				return reset();
			  };

			  albersUsa.scale = function(_) {
				if (!arguments.length) return lower48.scale();
				lower48.scale(_), alaska.scale(_ * 0.35), hawaii.scale(_);
				return albersUsa.translate(lower48.translate());
			  };

			  albersUsa.translate = function(_) {
				if (!arguments.length) return lower48.translate();
				var k = lower48.scale(), x = +_[0], y = +_[1];

				lower48Point = lower48
					.translate(_)
					.clipExtent([[x - 0.455 * k, y - 0.238 * k], [x + 0.455 * k, y + 0.238 * k]])
					.stream(pointStream);

				alaskaPoint = alaska
					.translate([x - 0.307 * k, y + 0.201 * k])
					.clipExtent([[x - 0.425 * k + epsilon$4, y + 0.120 * k + epsilon$4], [x - 0.214 * k - epsilon$4, y + 0.234 * k - epsilon$4]])
					.stream(pointStream);

				hawaiiPoint = hawaii
					.translate([x - 0.205 * k, y + 0.212 * k])
					.clipExtent([[x - 0.214 * k + epsilon$4, y + 0.166 * k + epsilon$4], [x - 0.115 * k - epsilon$4, y + 0.234 * k - epsilon$4]])
					.stream(pointStream);

				return reset();
			  };

			  albersUsa.fitExtent = function(extent, object) {
				return fitExtent(albersUsa, extent, object);
			  };

			  albersUsa.fitSize = function(size, object) {
				return fitSize(albersUsa, size, object);
			  };

			  function reset() {
				cache = cacheStream = null;
				return albersUsa;
			  }

			  return albersUsa.scale(1070);
			};

			function azimuthalRaw(scale) {
			  return function(x, y) {
				var cx = cos$1(x),
					cy = cos$1(y),
					k = scale(cx * cy);
				return [
				  k * cy * sin$1(x),
				  k * sin$1(y)
				];
			  }
			}

			function azimuthalInvert(angle) {
			  return function(x, y) {
				var z = sqrt$1(x * x + y * y),
					c = angle(z),
					sc = sin$1(c),
					cc = cos$1(c);
				return [
				  atan2(x * sc, z * cc),
				  asin$1(z && y * sc / z)
				];
			  }
			}

			var azimuthalEqualAreaRaw = azimuthalRaw(function(cxcy) {
			  return sqrt$1(2 / (1 + cxcy));
			});

			azimuthalEqualAreaRaw.invert = azimuthalInvert(function(z) {
			  return 2 * asin$1(z / 2);
			});

			var azimuthalEqualArea = function() {
			  return projection(azimuthalEqualAreaRaw)
				  .scale(124.75)
				  .clipAngle(180 - 1e-3);
			};

			var azimuthalEquidistantRaw = azimuthalRaw(function(c) {
			  return (c = acos(c)) && c / sin$1(c);
			});

			azimuthalEquidistantRaw.invert = azimuthalInvert(function(z) {
			  return z;
			});

			var azimuthalEquidistant = function() {
			  return projection(azimuthalEquidistantRaw)
				  .scale(79.4188)
				  .clipAngle(180 - 1e-3);
			};

			function mercatorRaw(lambda, phi) {
			  return [lambda, log$1(tan((halfPi$3 + phi) / 2))];
			}

			mercatorRaw.invert = function(x, y) {
			  return [x, 2 * atan(exp(y)) - halfPi$3];
			};

			var mercator = function() {
			  return mercatorProjection(mercatorRaw)
				  .scale(961 / tau$4);
			};

			function mercatorProjection(project) {
			  var m = projection(project),
				  scale = m.scale,
				  translate = m.translate,
				  clipExtent = m.clipExtent,
				  clipAuto;

			  m.scale = function(_) {
				return arguments.length ? (scale(_), clipAuto && m.clipExtent(null), m) : scale();
			  };

			  m.translate = function(_) {
				return arguments.length ? (translate(_), clipAuto && m.clipExtent(null), m) : translate();
			  };

			  m.clipExtent = function(_) {
				if (!arguments.length) return clipAuto ? null : clipExtent();
				if (clipAuto = _ == null) {
				  var k = pi$4 * scale(),
					  t = translate();
				  _ = [[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]];
				}
				clipExtent(_);
				return m;
			  };

			  return m.clipExtent(null);
			}

			function tany(y) {
			  return tan((halfPi$3 + y) / 2);
			}

			function conicConformalRaw(y0, y1) {
			  var cy0 = cos$1(y0),
				  n = y0 === y1 ? sin$1(y0) : log$1(cy0 / cos$1(y1)) / log$1(tany(y1) / tany(y0)),
				  f = cy0 * pow$1(tany(y0), n) / n;

			  if (!n) return mercatorRaw;

			  function project(x, y) {
				if (f > 0) { if (y < -halfPi$3 + epsilon$4) y = -halfPi$3 + epsilon$4; }
				else { if (y > halfPi$3 - epsilon$4) y = halfPi$3 - epsilon$4; }
				var r = f / pow$1(tany(y), n);
				return [r * sin$1(n * x), f - r * cos$1(n * x)];
			  }

			  project.invert = function(x, y) {
				var fy = f - y, r = sign$1(n) * sqrt$1(x * x + fy * fy);
				return [atan2(x, abs(fy)) / n * sign$1(fy), 2 * atan(pow$1(f / r, 1 / n)) - halfPi$3];
			  };

			  return project;
			}

			var conicConformal = function() {
			  return conicProjection(conicConformalRaw)
				  .scale(109.5)
				  .parallels([30, 30]);
			};

			function equirectangularRaw(lambda, phi) {
			  return [lambda, phi];
			}

			equirectangularRaw.invert = equirectangularRaw;

			var equirectangular = function() {
			  return projection(equirectangularRaw)
				  .scale(152.63);
			};

			function conicEquidistantRaw(y0, y1) {
			  var cy0 = cos$1(y0),
				  n = y0 === y1 ? sin$1(y0) : (cy0 - cos$1(y1)) / (y1 - y0),
				  g = cy0 / n + y0;

			  if (abs(n) < epsilon$4) return equirectangularRaw;

			  function project(x, y) {
				var gy = g - y, nx = n * x;
				return [gy * sin$1(nx), g - gy * cos$1(nx)];
			  }

			  project.invert = function(x, y) {
				var gy = g - y;
				return [atan2(x, abs(gy)) / n * sign$1(gy), g - sign$1(n) * sqrt$1(x * x + gy * gy)];
			  };

			  return project;
			}

			var conicEquidistant = function() {
			  return conicProjection(conicEquidistantRaw)
				  .scale(131.154)
				  .center([0, 13.9389]);
			};

			function gnomonicRaw(x, y) {
			  var cy = cos$1(y), k = cos$1(x) * cy;
			  return [cy * sin$1(x) / k, sin$1(y) / k];
			}

			gnomonicRaw.invert = azimuthalInvert(atan);

			var gnomonic = function() {
			  return projection(gnomonicRaw)
				  .scale(144.049)
				  .clipAngle(60);
			};

			function scaleTranslate(k, tx, ty) {
			  return k === 1 && tx === 0 && ty === 0 ? identity$7 : transformer({
				point: function(x, y) {
				  this.stream.point(x * k + tx, y * k + ty);
				}
			  });
			}

			var identity$8 = function() {
			  var k = 1, tx = 0, ty = 0, transform = identity$7, // scale and translate
				  x0 = null, y0, x1, y1, clip = identity$7, // clip extent
				  cache,
				  cacheStream,
				  projection;

			  function reset() {
				cache = cacheStream = null;
				return projection;
			  }

			  return projection = {
				stream: function(stream) {
				  return cache && cacheStream === stream ? cache : cache = transform(clip(cacheStream = stream));
				},
				clipExtent: function(_) {
				  return arguments.length ? (clip = _ == null ? (x0 = y0 = x1 = y1 = null, identity$7) : clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];
				},
				scale: function(_) {
				  return arguments.length ? (transform = scaleTranslate(k = +_, tx, ty), reset()) : k;
				},
				translate: function(_) {
				  return arguments.length ? (transform = scaleTranslate(k, tx = +_[0], ty = +_[1]), reset()) : [tx, ty];
				},
				fitExtent: function(extent, object) {
				  return fitExtent(projection, extent, object);
				},
				fitSize: function(size, object) {
				  return fitSize(projection, size, object);
				}
			  };
			};

			function orthographicRaw(x, y) {
			  return [cos$1(y) * sin$1(x), sin$1(y)];
			}

			orthographicRaw.invert = azimuthalInvert(asin$1);

			var orthographic = function() {
			  return projection(orthographicRaw)
				  .scale(249.5)
				  .clipAngle(90 + epsilon$4);
			};

			function stereographicRaw(x, y) {
			  var cy = cos$1(y), k = 1 + cos$1(x) * cy;
			  return [cy * sin$1(x) / k, sin$1(y) / k];
			}

			stereographicRaw.invert = azimuthalInvert(function(z) {
			  return 2 * atan(z);
			});

			var stereographic = function() {
			  return projection(stereographicRaw)
				  .scale(250)
				  .clipAngle(142);
			};

			function transverseMercatorRaw(lambda, phi) {
			  return [log$1(tan((halfPi$3 + phi) / 2)), -lambda];
			}

			transverseMercatorRaw.invert = function(x, y) {
			  return [-y, 2 * atan(exp(x)) - halfPi$3];
			};

			var transverseMercator = function() {
			  var m = mercatorProjection(transverseMercatorRaw),
				  center = m.center,
				  rotate = m.rotate;

			  m.center = function(_) {
				return arguments.length ? center([-_[1], _[0]]) : (_ = center(), [_[1], -_[0]]);
			  };

			  m.rotate = function(_) {
				return arguments.length ? rotate([_[0], _[1], _.length > 2 ? _[2] + 90 : 90]) : (_ = rotate(), [_[0], _[1], _[2] - 90]);
			  };

			  return rotate([0, 0, 90])
				  .scale(159.155);
			};

			exports.version = version;
			exports.bisect = bisectRight;
			exports.bisectRight = bisectRight;
			exports.bisectLeft = bisectLeft;
			exports.ascending = ascending;
			exports.bisector = bisector;
			exports.descending = descending;
			exports.deviation = deviation;
			exports.extent = extent;
			exports.histogram = histogram;
			exports.thresholdFreedmanDiaconis = freedmanDiaconis;
			exports.thresholdScott = scott;
			exports.thresholdSturges = sturges;
			exports.max = max;
			exports.mean = mean;
			exports.median = median;
			exports.merge = merge;
			exports.min = min;
			exports.pairs = pairs;
			exports.permute = permute;
			exports.quantile = threshold;
			exports.range = range;
			exports.scan = scan;
			exports.shuffle = shuffle;
			exports.sum = sum;
			exports.ticks = ticks;
			exports.tickStep = tickStep;
			exports.transpose = transpose;
			exports.variance = variance;
			exports.zip = zip;
			exports.entries = entries;
			exports.keys = keys;
			exports.values = values;
			exports.map = map$1;
			exports.set = set;
			exports.nest = nest;
			exports.randomUniform = uniform;
			exports.randomNormal = normal;
			exports.randomLogNormal = logNormal;
			exports.randomBates = bates;
			exports.randomIrwinHall = irwinHall;
			exports.randomExponential = exponential;
			exports.easeLinear = linear;
			exports.easeQuad = quadInOut;
			exports.easeQuadIn = quadIn;
			exports.easeQuadOut = quadOut;
			exports.easeQuadInOut = quadInOut;
			exports.easeCubic = cubicInOut;
			exports.easeCubicIn = cubicIn;
			exports.easeCubicOut = cubicOut;
			exports.easeCubicInOut = cubicInOut;
			exports.easePoly = polyInOut;
			exports.easePolyIn = polyIn;
			exports.easePolyOut = polyOut;
			exports.easePolyInOut = polyInOut;
			exports.easeSin = sinInOut;
			exports.easeSinIn = sinIn;
			exports.easeSinOut = sinOut;
			exports.easeSinInOut = sinInOut;
			exports.easeExp = expInOut;
			exports.easeExpIn = expIn;
			exports.easeExpOut = expOut;
			exports.easeExpInOut = expInOut;
			exports.easeCircle = circleInOut;
			exports.easeCircleIn = circleIn;
			exports.easeCircleOut = circleOut;
			exports.easeCircleInOut = circleInOut;
			exports.easeBounce = bounceOut;
			exports.easeBounceIn = bounceIn;
			exports.easeBounceOut = bounceOut;
			exports.easeBounceInOut = bounceInOut;
			exports.easeBack = backInOut;
			exports.easeBackIn = backIn;
			exports.easeBackOut = backOut;
			exports.easeBackInOut = backInOut;
			exports.easeElastic = elasticOut;
			exports.easeElasticIn = elasticIn;
			exports.easeElasticOut = elasticOut;
			exports.easeElasticInOut = elasticInOut;
			exports.polygonArea = area;
			exports.polygonCentroid = centroid;
			exports.polygonHull = hull;
			exports.polygonContains = contains;
			exports.polygonLength = length$1;
			exports.path = path;
			exports.quadtree = quadtree;
			exports.queue = queue;
			exports.arc = arc;
			exports.area = area$1;
			exports.line = line;
			exports.pie = pie;
			exports.radialArea = radialArea;
			exports.radialLine = radialLine$1;
			exports.symbol = symbol;
			exports.symbols = symbols;
			exports.symbolCircle = circle;
			exports.symbolCross = cross$1;
			exports.symbolDiamond = diamond;
			exports.symbolSquare = square;
			exports.symbolStar = star;
			exports.symbolTriangle = triangle;
			exports.symbolWye = wye;
			exports.curveBasisClosed = basisClosed;
			exports.curveBasisOpen = basisOpen;
			exports.curveBasis = basis;
			exports.curveBundle = bundle;
			exports.curveCardinalClosed = cardinalClosed;
			exports.curveCardinalOpen = cardinalOpen;
			exports.curveCardinal = cardinal;
			exports.curveCatmullRomClosed = catmullRomClosed;
			exports.curveCatmullRomOpen = catmullRomOpen;
			exports.curveCatmullRom = catmullRom;
			exports.curveLinearClosed = linearClosed;
			exports.curveLinear = curveLinear;
			exports.curveMonotoneX = monotoneX;
			exports.curveMonotoneY = monotoneY;
			exports.curveNatural = natural;
			exports.curveStep = step;
			exports.curveStepAfter = stepAfter;
			exports.curveStepBefore = stepBefore;
			exports.stack = stack;
			exports.stackOffsetExpand = expand;
			exports.stackOffsetNone = none;
			exports.stackOffsetSilhouette = silhouette;
			exports.stackOffsetWiggle = wiggle;
			exports.stackOrderAscending = ascending$1;
			exports.stackOrderDescending = descending$2;
			exports.stackOrderInsideOut = insideOut;
			exports.stackOrderNone = none$1;
			exports.stackOrderReverse = reverse;
			exports.color = color;
			exports.rgb = rgb;
			exports.hsl = hsl;
			exports.lab = lab;
			exports.hcl = hcl;
			exports.cubehelix = cubehelix;
			exports.interpolate = interpolate;
			exports.interpolateArray = array$1;
			exports.interpolateDate = date;
			exports.interpolateNumber = interpolateNumber;
			exports.interpolateObject = object;
			exports.interpolateRound = interpolateRound;
			exports.interpolateString = interpolateString;
			exports.interpolateTransformCss = interpolateTransformCss;
			exports.interpolateTransformSvg = interpolateTransformSvg;
			exports.interpolateZoom = interpolateZoom;
			exports.interpolateRgb = interpolateRgb;
			exports.interpolateRgbBasis = rgbBasis;
			exports.interpolateRgbBasisClosed = rgbBasisClosed;
			exports.interpolateHsl = hsl$2;
			exports.interpolateHslLong = hslLong;
			exports.interpolateLab = lab$1;
			exports.interpolateHcl = hcl$2;
			exports.interpolateHclLong = hclLong;
			exports.interpolateCubehelix = cubehelix$2;
			exports.interpolateCubehelixLong = cubehelixLong;
			exports.interpolateBasis = basis$2;
			exports.interpolateBasisClosed = basisClosed$1;
			exports.quantize = quantize;
			exports.dispatch = dispatch;
			exports.dsvFormat = dsv;
			exports.csvParse = csvParse;
			exports.csvParseRows = csvParseRows;
			exports.csvFormat = csvFormat;
			exports.csvFormatRows = csvFormatRows;
			exports.tsvParse = tsvParse;
			exports.tsvParseRows = tsvParseRows;
			exports.tsvFormat = tsvFormat;
			exports.tsvFormatRows = tsvFormatRows;
			exports.request = request;
			exports.html = html;
			exports.json = json;
			exports.text = text;
			exports.xml = xml;
			exports.csv = csv$1;
			exports.tsv = tsv$1;
			exports.now = now;
			exports.timer = timer;
			exports.timerFlush = timerFlush;
			exports.timeout = timeout$1;
			exports.interval = interval$1;
			exports.timeInterval = newInterval;
			exports.timeMillisecond = millisecond;
			exports.timeMilliseconds = milliseconds;
			exports.timeSecond = second;
			exports.timeSeconds = seconds;
			exports.timeMinute = minute;
			exports.timeMinutes = minutes;
			exports.timeHour = hour;
			exports.timeHours = hours;
			exports.timeDay = day;
			exports.timeDays = days;
			exports.timeWeek = sunday;
			exports.timeWeeks = sundays;
			exports.timeSunday = sunday;
			exports.timeSundays = sundays;
			exports.timeMonday = monday;
			exports.timeMondays = mondays;
			exports.timeTuesday = tuesday;
			exports.timeTuesdays = tuesdays;
			exports.timeWednesday = wednesday;
			exports.timeWednesdays = wednesdays;
			exports.timeThursday = thursday;
			exports.timeThursdays = thursdays;
			exports.timeFriday = friday;
			exports.timeFridays = fridays;
			exports.timeSaturday = saturday;
			exports.timeSaturdays = saturdays;
			exports.timeMonth = month;
			exports.timeMonths = months;
			exports.timeYear = year;
			exports.timeYears = years;
			exports.utcMillisecond = millisecond;
			exports.utcMilliseconds = milliseconds;
			exports.utcSecond = second;
			exports.utcSeconds = seconds;
			exports.utcMinute = utcMinute;
			exports.utcMinutes = utcMinutes;
			exports.utcHour = utcHour;
			exports.utcHours = utcHours;
			exports.utcDay = utcDay;
			exports.utcDays = utcDays;
			exports.utcWeek = utcSunday;
			exports.utcWeeks = utcSundays;
			exports.utcSunday = utcSunday;
			exports.utcSundays = utcSundays;
			exports.utcMonday = utcMonday;
			exports.utcMondays = utcMondays;
			exports.utcTuesday = utcTuesday;
			exports.utcTuesdays = utcTuesdays;
			exports.utcWednesday = utcWednesday;
			exports.utcWednesdays = utcWednesdays;
			exports.utcThursday = utcThursday;
			exports.utcThursdays = utcThursdays;
			exports.utcFriday = utcFriday;
			exports.utcFridays = utcFridays;
			exports.utcSaturday = utcSaturday;
			exports.utcSaturdays = utcSaturdays;
			exports.utcMonth = utcMonth;
			exports.utcMonths = utcMonths;
			exports.utcYear = utcYear;
			exports.utcYears = utcYears;
			exports.formatLocale = formatLocale;
			exports.formatDefaultLocale = defaultLocale;
			exports.formatSpecifier = formatSpecifier;
			exports.precisionFixed = precisionFixed;
			exports.precisionPrefix = precisionPrefix;
			exports.precisionRound = precisionRound;
			exports.isoFormat = formatIso;
			exports.isoParse = parseIso;
			exports.timeFormatLocale = formatLocale$1;
			exports.timeFormatDefaultLocale = defaultLocale$1;
			exports.scaleBand = band;
			exports.scalePoint = point$4;
			exports.scaleIdentity = identity$4;
			exports.scaleLinear = linear$2;
			exports.scaleLog = log;
			exports.scaleOrdinal = ordinal;
			exports.scaleImplicit = implicit;
			exports.scalePow = pow;
			exports.scaleSqrt = sqrt;
			exports.scaleQuantile = quantile$$1;
			exports.scaleQuantize = quantize$1;
			exports.scaleThreshold = threshold$1;
			exports.scaleTime = time;
			exports.scaleUtc = utcTime;
			exports.schemeCategory10 = category10;
			exports.schemeCategory20b = category20b;
			exports.schemeCategory20c = category20c;
			exports.schemeCategory20 = category20;
			exports.scaleSequential = sequential;
			exports.interpolateCubehelixDefault = cubehelix$3;
			exports.interpolateRainbow = rainbow$1;
			exports.interpolateWarm = warm;
			exports.interpolateCool = cool;
			exports.interpolateViridis = viridis;
			exports.interpolateMagma = magma;
			exports.interpolateInferno = inferno;
			exports.interpolatePlasma = plasma;
			exports.creator = creator;
			exports.customEvent = customEvent;
			exports.local = local;
			exports.matcher = matcher$1;
			exports.mouse = mouse;
			exports.namespace = namespace;
			exports.namespaces = namespaces;
			exports.select = select;
			exports.selectAll = selectAll;
			exports.selection = selection;
			exports.selector = selector;
			exports.selectorAll = selectorAll;
			exports.touch = touch;
			exports.touches = touches;
			exports.window = window;
			exports.active = active;
			exports.interrupt = interrupt;
			exports.transition = transition;
			exports.axisTop = axisTop;
			exports.axisRight = axisRight;
			exports.axisBottom = axisBottom;
			exports.axisLeft = axisLeft;
			exports.cluster = cluster;
			exports.hierarchy = hierarchy;
			exports.pack = index;
			exports.packSiblings = siblings;
			exports.packEnclose = enclose;
			exports.partition = partition;
			exports.stratify = stratify;
			exports.tree = tree;
			exports.treemap = index$1;
			exports.treemapBinary = binary;
			exports.treemapDice = treemapDice;
			exports.treemapSlice = treemapSlice;
			exports.treemapSliceDice = sliceDice;
			exports.treemapSquarify = squarify;
			exports.treemapResquarify = resquarify;
			exports.forceCenter = center$1;
			exports.forceCollide = collide;
			exports.forceLink = link;
			exports.forceManyBody = manyBody;
			exports.forceSimulation = simulation;
			exports.forceX = x$3;
			exports.forceY = y$3;
			exports.drag = drag;
			exports.dragDisable = dragDisable;
			exports.dragEnable = yesdrag;
			exports.voronoi = voronoi;
			exports.zoom = zoom;
			exports.zoomIdentity = identity$6;
			exports.zoomTransform = transform;
			exports.brush = brush;
			exports.brushX = brushX;
			exports.brushY = brushY;
			exports.brushSelection = brushSelection;
			exports.chord = chord;
			exports.ribbon = ribbon;
			exports.geoAlbers = albers;
			exports.geoAlbersUsa = albersUsa;
			exports.geoArea = area$2;
			exports.geoAzimuthalEqualArea = azimuthalEqualArea;
			exports.geoAzimuthalEqualAreaRaw = azimuthalEqualAreaRaw;
			exports.geoAzimuthalEquidistant = azimuthalEquidistant;
			exports.geoAzimuthalEquidistantRaw = azimuthalEquidistantRaw;
			exports.geoBounds = bounds;
			exports.geoCentroid = centroid$1;
			exports.geoCircle = circle$1;
			exports.geoClipExtent = extent$1;
			exports.geoConicConformal = conicConformal;
			exports.geoConicConformalRaw = conicConformalRaw;
			exports.geoConicEqualArea = conicEqualArea;
			exports.geoConicEqualAreaRaw = conicEqualAreaRaw;
			exports.geoConicEquidistant = conicEquidistant;
			exports.geoConicEquidistantRaw = conicEquidistantRaw;
			exports.geoDistance = distance;
			exports.geoEquirectangular = equirectangular;
			exports.geoEquirectangularRaw = equirectangularRaw;
			exports.geoGnomonic = gnomonic;
			exports.geoGnomonicRaw = gnomonicRaw;
			exports.geoGraticule = graticule;
			exports.geoGraticule10 = graticule10;
			exports.geoIdentity = identity$8;
			exports.geoInterpolate = interpolate$2;
			exports.geoLength = length$2;
			exports.geoMercator = mercator;
			exports.geoMercatorRaw = mercatorRaw;
			exports.geoOrthographic = orthographic;
			exports.geoOrthographicRaw = orthographicRaw;
			exports.geoPath = index$3;
			exports.geoProjection = projection;
			exports.geoProjectionMutator = projectionMutator;
			exports.geoRotation = rotation;
			exports.geoStereographic = stereographic;
			exports.geoStereographicRaw = stereographicRaw;
			exports.geoStream = geoStream;
			exports.geoTransform = transform$1;
			exports.geoTransverseMercator = transverseMercator;
			exports.geoTransverseMercatorRaw = transverseMercatorRaw;

			Object.defineProperty(exports, '__esModule', { value: true });

			})));


			//aigner: HERE IS WHERE IT ALL STARTS!!!
			},{}],2:[function(require,module,exports){
				


				
			var d3 = require('d3');

			var PULSE_DURATION = 700;
			var SUB_ANIM_DELAY = 200;
			var ACTIVE_COLOR = d3.rgb("seagreen");

			function Animation(xdsms, rootId, delay) {
			  this.rootId = rootId;
			  if (typeof (rootId) === 'undefined') {
				this.rootId = 'root';
			  }
			  this.root = xdsms[this.rootId];
			  this.xdsms = xdsms;
			  this.duration = PULSE_DURATION;
			  this.initialDelay = delay || 0;
			}

			Animation.prototype._pulse = function(delay, toBeSelected, option) {
			  var sel = d3.select("svg." + this.rootId)
						  .selectAll(toBeSelected)
						  .transition().delay(delay);
			  if (option !== "out") {
				sel = sel.transition().duration(200)
						.style('stroke-width', '8px')
						.style('stroke', ACTIVE_COLOR)
						.style('fill', function(d) {
						  if (d.id) {
							return ACTIVE_COLOR.brighter();
						  }});
			  }
			  if (option !== "in") {
				sel.transition().duration(3 * PULSE_DURATION)
						.style('stroke-width', null)
						.style('stroke', null)
						.style('fill', null);
			  }
			};

			Animation.prototype._animate = function() {
			  var self = this;
			  var delay = this.initialDelay;
			  var animDelay = SUB_ANIM_DELAY;
			  var graph = self.xdsms[self.rootId].graph;
			  
			  var title = d3.select("svg." + self.rootId).select("g.title");
			  title.select("text").transition().delay(delay).style("fill", ACTIVE_COLOR);
			  d3.select("svg." + self.rootId).select("rect.border")
				.transition().delay(delay)
				  .style("stroke-width", '5px').duration(200)
				.transition().duration(1000)
				  .style("stroke", 'black').style("stroke-width", '0px');

			  graph.nodesByStep.forEach(function(nodesAtStep, n, nodesByStep) {
				var offsets = [];
				nodesAtStep.forEach(function(nodeId) {
				  var elapsed = delay + n * PULSE_DURATION;
				  if (n > 0) {
					nodesByStep[n-1].forEach(function(prevNodeId) { // eslint-disable-line space-infix-ops
					  var from = graph.idxOf(prevNodeId);
					  var to = graph.idxOf(nodeId);
					  self._pulse(elapsed,"polyline.link_" + from + "_" + to);
					});
				  }
				  //aigner: Look for existing xdsms/Sub-xdsms. Only show those, which are expanded                  
				  var gnode = "g." + nodeId;
				  var nodeSel = d3.select("svg." + self.rootId).select(gnode);
				  var scnId = graph.getNode(nodeId).getScenarioId();
				  if (nodeSel.classed("mdo") && self.xdsms[scnId]) 
				  {
					  self._pulse(elapsed, gnode + " > rect", "in");
					  var anim = new Animation(self.xdsms, scnId, elapsed + animDelay);
					  var offset = anim._animate();
					  offsets.push(offset);
					  self._pulse(offset + elapsed + animDelay, gnode + " > rect", "out");
				  } 
				  else 
				  {
					  self._pulse(elapsed, gnode + " > rect");
				  }
				}, this);

				if (offsets.length > 0) {
				  delay += Math.max.apply(null, offsets);
				}
				delay += animDelay;
			  }, this);
				
			  title.select("text").transition()
				.delay(graph.nodesByStep.length * PULSE_DURATION + delay)
				.style("fill", null);

			  return graph.nodesByStep.length * PULSE_DURATION;
			};

			Animation.prototype.run = function() {
			  this._animate();
			};

			module.exports = Animation;

			},{"d3":1}],3:[function(require,module,exports){
			var _U_ = "_U_";
			var MULTI_TYPE = "_multi";

			//aigner: Node() constructor additionally gets the "xdsm"
			function Node(xdsm, id, uID, name, type, metadata) {                
			  var splitUndef = name.split("undefined: ")
			  if (splitUndef.length>1) {name=splitUndef[1]}
			  else {name=splitUndef[0]}
			  
			  if (typeof (name) === 'undefined') {
				name = id;
			  }
			  if (typeof (type) === 'undefined') {
				type = 'analysis';
			  }
			  this.xdsm = xdsm;
			  this.id = id;
			  this.uID = uID;
			  this.name = name;
			  this.metadata = metadata;
			  this.isMulti = (type.search(/_multi$/) >= 0);
			  this.type = this.isMulti ?
				type.substr(0, type.length - MULTI_TYPE.length) : type;
			}

			Node.prototype.isMdo = function() {
			  return this.type === "mdo";
			};

			Node.prototype.getScenarioId = function() {
			  if (this.isMdo()) {
				var idxscn = this.name.indexOf("_scn-");
				if (idxscn === -1) {
				  console.log("Warning: MDO Scenario not found. " +
							  "Bad type or name for node: " + JSON.stringify(this));
				  return null;
				}
				return this.name.substr(idxscn + 1);
			  }
			  return null;
			};

			function Edge(from, to, from_uID, to_uID, name, row, col, isMulti) {
			  this.id = "link_" + from + "_" + to;
			  this.name = name;
			  this.row = row;
			  this.col = col;
			  this.iotype = row < col ? "in" : "out";
			  this.io = {
				fromU: (from === _U_),
				toU: (to === _U_)
			  };
			  this.from = from;
			  this.from_uID = from_uID;
			  this.to = to;
			  this.to_uID = to_uID;
			  this.isMulti = isMulti;
			}

			Edge.prototype.isIO = function() {
			  return this.io.fromU || this.io.toU;
			};

			//aigner: Function creates the xdsm graph
			function Graph(mdo, refname) {
			  this.nodes = [new Node(_U_, _U_, _U_, _U_, "user",[])];
			  this.edges = [];
			  this.chains = [];
			  this.refname = refname || "";
			  
			  //aigner: New numbering due to new workflow definition in KADMOS (Old stuff commented out)
			  var numbering = Graph.new_number(mdo.workflow);
			  //var numbering = Graph.number(mdo.workflow);
			  var numPrefixes = numbering.toNum;
			  this.nodesByStep = numbering.toNode;

			  mdo.nodes.forEach(function(item) {
				name = document.createElement("TEXTAREA");
				name = numPrefixes[item.id] + ": " + item.name;
				this.nodes.push(new Node(item.xdsm, item.id, item.uID, name, item.type, item.metadata));    
			  }, this);

			  this.ids = this.nodes.map(function(elt) {
				return elt.id;
			  });
			  
			  mdo.edges.forEach(function(item) {
				var idA = this.idxOf(item.from);
				var idB = this.idxOf(item.to);
				
				var isMulti = this.nodes[idA].isMulti || this.nodes[idB].isMulti;
				this.edges.push(new Edge(item.from, item.to, item.from_uID, item.to_uID, item.name, idA, idB, isMulti));
			  }, this);

			  
			  //aigner: new chains with new workflow definition for KADMOS (Old stuff commented out)
			  this.chains = Graph.new_expand(mdo.workflow,mdo.nodes);
			  // var echain = Graph.expand(mdo.workflow); 
			  // echain.forEach(function(leafChain) {
				// if (leafChain.length < 2) {
				  // throw new Error("Bad process chain (" + leafChain.length + "elt)");
				// } else {
				  // this.chains.push([]);
				  // var ids = this.nodes.map(function(elt) {
					// return elt.id;
				  // });
				  // leafChain.forEach(function(item, j) {
					// if (j !== 0) {
					  // var idA = ids.indexOf(leafChain[j - 1]);
					  // if (idA < 0) {
						// throw new Error("Process chain element (" +
										// leafChain[j - 1] + ") not found");
					  // }
					  // var idB = ids.indexOf(leafChain[j]);
					  // if (idB < 0) {
						// throw new Error("Process chain element (" +
										// leafChain[j] + ") not found");
					  // }
					  // if (idA !== idB) {
						// this.chains[this.chains.length - 1].push([idA, idB]);
					  // }
					// }
				  // }, this);
				// }
			  // }, this);
			  
			  
			}

			Graph.prototype.idxOf = function(nodeId) {
			  return this.ids.indexOf(nodeId);
			};
			Graph.prototype.getNode = function(nodeId) {
			  return this.nodes[this.ids.indexOf(nodeId)];
			};

			function _expand(workflow) {
			  var ret = [];
			  var prev;
			  workflow.forEach(function(item) {
				if (item instanceof Array) {
				  if (item[0].hasOwnProperty('parallel')) {
					if (prev) {
					  ret = ret.slice(0, ret.length - 1).concat(item[0].parallel.map(
						  function(elt) {
							return [prev].concat(_expand([elt]), prev);
						  }));
					} else {
					  throw new Error("Bad workflow structure : " +
						  "cannot parallel loop without previous starting point.");
					}
				  } else if (prev) {
					ret = ret.concat(_expand(item), prev);
				  } else {
					ret = ret.concat(_expand(item));
				  }
				  prev = ret[ret.length - 1];
				} else if (item.hasOwnProperty('parallel')) {
				  if (prev) {
					ret = ret.slice(0, ret.length - 1).concat(
						item.parallel.map(function(elt) {
						  return [prev].concat(_expand([elt]));
						}));
				  } else {
					ret = ret.concat(item.parallel.map(
						function(elt) {
						  return _expand([elt]);
						}));
				  }
				  prev = undefined;
				} else {
				  var i = ret.length - 1;
				  var flagParallel = false;
				  while (i >= 0 && (ret[i] instanceof Array)) {
					ret[i] = ret[i].concat(item);
					i -= 1;
					flagParallel = true;
				  }
				  if (!flagParallel) {
					ret.push(item);
				  }
				  prev = item;
				}
			  }, this);
			  return ret;
			}

			Graph.expand = function(item) {
			  var expanded = _expand(item);
			  var result = [];
			  var current = [];
			  expanded.forEach(function(elt) {
				if (elt instanceof Array) {
				  if (current.length > 0) {
					current.push(elt[0]);
					result.push(current);
					current = [];
				  }
				  result.push(elt);
				} else {
				  if (result.length > 0 && current.length === 0) {
					var lastChain = result[result.length - 1];
					var lastElt = lastChain[lastChain.length - 1];
					current.push(lastElt);
				  }
				  current.push(elt);
				}
			  }, this);
			  if (current.length > 0) {
				result.push(current);
			  }
			  return result;
			};

			Graph.new_expand = function(workflow,nodes) {
				var ids = nodes.map(function(elt) {
					return elt.id;
				});
				var result_tmp = [];
				for (var i=0; i < workflow.length; i++)
				{
					var stepNumber = workflow[i].step_number
					var edges = workflow[i].edges;
					for (var j=0; j < edges.length; j++)
					{
						var fromNode = edges[j][0];
						var toNode = edges[j][1];			
						var fromId = ids.indexOf(fromNode)+1;
						var toId = ids.indexOf(toNode)+1;
						result_tmp.push([fromId,toId]);
					}
				}
				var result = [];
				result.push(result_tmp)
				return result;
			};

			Graph.number = function(workflow, num) {
			  num = (typeof num === 'undefined') ? 0 : num;
			  var toNum = {};
			  var toNode = [];

			  function setStep(step, nodeId) {
				if (step in toNode) {
				  toNode[step].push(nodeId);
				} else {
				  toNode[step] = [nodeId];
				}
			  }

			  function setNum(nodeId, beg, end) {
				if (end === undefined) {
				  num = String(beg);
				  setStep(beg, nodeId);
				} else {
				  num = end + "-" + beg;
				  setStep(end, nodeId);
				}
				if (nodeId in toNum) {
				  toNum[nodeId] += "," + num;
				} else {
				  toNum[nodeId] = num;
				}
			  }

			  function _number(wks, num) {
				var ret = 0;
				if (wks instanceof Array) {
				  if (wks.length === 0) {
					ret = num;
				  } else if (wks.length === 1) {
					ret = _number(wks[0], num);
				  } else {
					var head = wks[0];
					var tail = wks.slice(1);
					var beg = _number(head, num);
					if (tail[0] instanceof Array) {
					  var end = _number(tail[0], beg);
					  setNum(head, beg, end);
					  beg = end + 1;
					  tail.shift();
					}
					ret = _number(tail, beg);
				  }
				} else if ((wks instanceof Object) && 'parallel' in wks) {
				  var nums = wks.parallel.map(function(branch) {
					return _number(branch, num);
				  });
				  ret = Math.max.apply(null, nums);
				} else {
				  setNum(wks, num);
				  ret = num + 1;
				}
				return ret;
			  }

			  _number(workflow, num);
			  // console.log('toNodes=', JSON.stringify(toNode));
			  // console.log('toNum=',JSON.stringify(toNum));
			  return {toNum: toNum, toNode: toNode};
			};

			//aigner: New numbering for the workflow nodes
			Graph.new_number = function(workflow, num) {
				num = (typeof num === 'undefined') ? 0 : num;
				var toNum = {};
				var toNode = [];
				
				for (var i=0; i < workflow.length; i++)
				{
					//aigner: Number the nodes with respect to the defined process steps
					//--> toNum
					//##########################################
					for (var j=0; j < workflow[i].process_step_blocks.length; j++)
					{
						var proc_id_to = workflow[i].process_step_blocks[j];
						if (proc_id_to)
						{

							if (!toNum[proc_id_to])
							{
								toNum[proc_id_to] = workflow[i].step_number;
							}
							else
							{
								toNum[proc_id_to] += "," + workflow[i].step_number;
							}
						}
					}
					for (var j=0; j < workflow[i].converger_step_blocks.length; j++)
					{
						var conv_id_to = workflow[i].converger_step_blocks[j];
						if (conv_id_to)
						{
							toNum[conv_id_to] += "," + workflow[i].step_number;
							var stepID = parseInt(toNum[conv_id_to].split(',')[0])+1;
							//aigner: CAUTION hard coded exception for coordinator
							if (conv_id_to!="Coordinator"){toNum[conv_id_to] += "-" + stepID;}
						}
					}
					//##########################################		
					//--> toNode
					//##########################################
					var stepNumber = workflow[i].step_number
					var nodeId;
					if (stepNumber==0){nodeId = [workflow[i].process_step_blocks[0]];}
					else
					{
						var nodeIds = [];
						for (var j=0; j<workflow[i].edges.length ;j++)
						{
							nodeIds.push(workflow[i].edges[j][1]);
						}
						nodeId = nodeIds;
					}
					toNode[stepNumber] = nodeId;
					//##########################################
				}	
				return {toNum: toNum, toNode: toNode};
			};

			module.exports = Graph;

			},{}],4:[function(require,module,exports){
			function Labelizer() {}

			Labelizer.strParse = function(str) {
			  if (str === "") {
				return [{base: '', sub: undefined, sup: undefined}];
			  }

			  //console.log("STR")
			  //console.log(str);
			  //aigner: Space sign added
			  var lstr = str.split(',');
			  var underscores = /_/g;
			  var rg = /([0-9\-]+: )?([A-Za-z0-9\{\}\(\)\[\]\-\.]+)(_[A-Za-z0-9\-\._]+)?(\^.+)?/;

			  var res = lstr.map(function(s) {
				var base;
				var sub;
				var sup;
				
				//aigner: Modification. xpath is split after each "/"
				var s_split = s.split("/");
				s = s_split[s_split.length-1];

				if ((s.match(underscores) || []).length > 1) {
				  var mu = s.match(/(.+)^(.+)/);
				  if (mu) {
					return {base: mu[1], sub: undefined, sup: mu[2]};
				  }
				  return {base: s, sub: undefined, sup: undefined};
				}
				var m = s.match(rg);
				if (m) {
				  base = (m[1] ? m[1] : "") + m[2];
				  if (m[3]) {
					sub = m[3].substring(1);
				  }
				  if (m[4]) {
					sup = m[4].substring(1);
				  }
				} else {
				  throw new Error("Labelizer.strParse: Can not parse '" + s + "'");
				}
				return {base: base, sub: sub, sup: sup};
			  }, this);
			 
			  return res;
			};

			Labelizer.labelize = function() {
			  var ellipsis = 0;

			  //aigner: createLabel modified. Hide variable names if too long.
			  function createLabel(selection) 
			  {
				selection.each(function(d) {
					//console.log("Labelize-->");
					//console.log(d);
				  var tokens = Labelizer.strParse(d.name);
				  var text = selection.append("text");

				  var className = selection._groups[0][0].getAttribute("class");

				  if (className.includes("node"))
				  {
					tokens.every(function(token, i, ary)
					{
						var offsetSub = 0;
						var offsetSup = 0;
						//aigner: If the node is too big, it will not be shown at first, only when hovering
						if (ellipsis < 1 || i < 15) 
						{
							text.append("tspan").text(token.base);
							if (token.sub) 
							{
							offsetSub = 10;
							text.append("tspan")
							  .attr("class", "sub")
							  .attr("dy", offsetSub)
							  .text(token.sub);
							}
							if (token.sup) {
								offsetSup = -10;
								text.append("tspan")
								  .attr("class", "sup")
								  .attr("dx", -5)
								  .attr("dy", -offsetSub + offsetSup)
								  .text(token.sup);
								offsetSub = 0;
							}
						} else 
						{
							text.append("tspan")
								.attr("dy", -offsetSub - offsetSup)
								.text("...");
							selection.classed("ellipsized", true);
							return false;
						}
						if (i < ary.length - 1) 
						{
							  text.append("tspan")
								.attr("dy", -offsetSub - offsetSup)
								.text(", ");
						}
						return true;
					}, this);
				  }
				  else if(className.includes("edge"))
				  {	  
						var offsetSub = 0;
						var offsetSup = 0;
						//aigner: If there are more than 5 variables in an edge, they are not shown!
						tokens.every(function(token, i, ary)
						{           			
							if(d.io.fromU || d.from =="Coordinator"){text_tmp = "inp."}
							else if(d.io.toU || d.to=="Coordinator"){text_tmp = "outp."}
							else{text_tmp = "conn."}
							//if (ary.length>1){text_tmp = text_tmp+"s"};
							text.append("tspan")
							.attr("dy", -offsetSub - offsetSup)
							.text(ary.length + " " + text_tmp);                    
							
							selection.classed("ellipsized", true);
							return false;
						}, this);
				  }
				});
			  }

			  createLabel.ellipsis = function(value) {  
				if (!arguments.length) {
				  return ellipsis;
				}
				ellipsis = value;
				return createLabel;
			  };

			  return createLabel;
			};

			Labelizer.tooltipize = function() {
			  var text = "";

			  function createTooltip(selection) {
				var tokens = Labelizer.strParse(text);
				var html = [];
				tokens.forEach(function(token) {
				  var item = token.base;
				  if (token.sub) {
					item += "<sub>" + token.sub + "</sub>";
				  }
				  if (token.sup) {
					item += "<sup>" + token.sup + "</sup>";
				  }
				  html.push(item);
				}, this);
				selection.html(html.join(", "));
			  }

			  createTooltip.text = function(value) {
				if (!arguments.length) {
				  return text;
				}
				text = value;
				return createTooltip;
			  };

			  return createTooltip;
			};

			module.exports = Labelizer;

			},{}],5:[function(require,module,exports){
			var d3 = require('d3');
			var Labelizer = require('./labelizer.js');

			var WIDTH = 1000;
			var HEIGHT = 500;
			var X_ORIG = 70;
			var Y_ORIG = 20;
			var PADDING = 20;
			var CELL_W = 250;
			var CELL_H = 75;
			var MULTI_OFFSET = 3;

			function Cell(x, y, width, height) {
			  this.x = x;
			  this.y = y;
			  this.width = width;
			  this.height = height;
			}

			function Xdsm(graph, svgid, tooltip) {
			  this.graph = graph;
			  this.tooltip = tooltip;
			  this.svg = d3.select(".xdsm")
						   .append("svg")
						 .attr("width", WIDTH)
						 .attr("height", HEIGHT)
						 .attr("class", svgid);

			  this.grid = [];
			  this.nodes = [];
			  this.edges = [];
			}

			Xdsm.prototype.draw = function(graphName) {
			  var self = this;

			  if (self.graph.refname) {
				var ref = self.svg.append('g').classed('title', true);

				var text_tmp = self.graph.refname.replace(/\w/g, function(l){ return l.toUpperCase() })
					
				//ref.append("text").text(graphName);
				var bbox = ref.nodes()[0].getBBox();
				ref.insert("rect", "text")
					.attr('x', bbox.x)
					.attr('y', bbox.y)
					.attr('width', bbox.width)
					.attr('height', bbox.height);

				ref.attr('transform',
						 'translate(' + X_ORIG + ',' + (Y_ORIG + bbox.height-10) + ')');
			  }

			  self.nodes = self._createTextGroup("node");
			  self.edges = self._createTextGroup("edge");

			  // Workflow
			  self._createWorkflow();

			  // Layout texts
			  self._layoutText(self.nodes);
			  self._layoutText(self.edges);

			  // Rectangles for nodes
			  self.nodes.each(function(d, i) {
				var that = d3.select(this);
				that.call(self._customRect.bind(self), d, i, 0);
				if (d.isMulti) {
				  that.call(self._customRect.bind(self), d, i, 1 * Number(MULTI_OFFSET));
				  that.call(self._customRect.bind(self), d, i, 2 * Number(MULTI_OFFSET));
				}
			  });

			  // Trapezium for edges
			  self.edges.each(function(d, i) {
				var that = d3.select(this);
				that.call(self._customTrapz.bind(self), d, i, 0);
				if (d.isMulti) {
				  that.call(self._customTrapz.bind(self), d, i, 1 * Number(MULTI_OFFSET));
				  that.call(self._customTrapz.bind(self), d, i, 2 * Number(MULTI_OFFSET));
				}
			  });

			  // Dataflow
			  self._createDataflow(self.edges);
				
			  //aigner: set svg size
			  var w = CELL_W * (self.graph.nodes.length + 1);
			  var h = CELL_H * (self.graph.nodes.length + 1);
			  self.svg.attr("width", w).attr("height", h);

			  var bordercolor = 'black';
			  self.svg.append("rect")
						.classed("border", true)
						.attr("x", 4)
						.attr("y", 4)
						.attr("height", h - 4)
						.attr("width", w - 4)
						.style("stroke", bordercolor)
						.style("fill", "none")
						.style("stroke-width", 0);
						
			  //aigner: PlayButton for animation
			  var playButton = self.svg.append("g").attr("class", "playButton")
				  .attr("transform", "translate("+ 10 +","+ 10 +")")
				  
			  playButton
				.append("rect")
				  .attr("class", "playButton_rect")
				  .attr("width", 50)
				  .attr("height", 50)
				  .attr("rx", 4)
				  .style("fill", "#555555");
				  
			  playButton
				.append("path")
				  .attr("class", "playButton_triangle")
				  .attr("d", "M15 10 L15 40 L35 25 Z")
				  .style("fill", "white");
				  
			  playButton.append("text")
				  .attr("y",20)
				  .attr("x",60)
				  .text("Play Workflow")

			  //aigner: Hide playbutton if there is no workflow process
			  if(self.graph.nodesByStep.length<1)
			  {
				  playButton.attr("visibility","hidden")
			  }
			  playButton.append("svg:title").text("Click to play workflow");
			};

			Xdsm.prototype._createTextGroup = function(kind) {
			  var self = this;

			  var group = self.svg.append('g').attr("class", kind + "s");

			  var textGroups =
				group.selectAll("." + kind)
				  .data(this.graph[kind + "s"])
				.enter()
				  .append("g").attr("class", function(d) {
					var klass = kind === "node" ? d.type : "dataInter";
					if (klass === "dataInter" && (d.isIO()||d.from=="Coordinator"||d.to=="Coordinator")) {
					  klass = "dataIO";
					}
					return d.id + " " + kind + " " + klass;
				  }).each(function() {
					var labelize = Labelizer.labelize().ellipsis(5);
					d3.select(this).call(labelize);
				  });

			  d3.selectAll(".ellipsized")
				.on("mouseover", function(d) {
					d3.select(this).style("cursor", "pointer")
					self.tooltip.transition().duration(200).style("opacity", 0.9);
					var pipeData = d.name.split(",");
					var pipeVariables=[];
					for (var i=0; i<pipeData.length; i++)
					{
						if (pipeData[i].includes("/"))
						{
							var splitPipeData = pipeData[i].split("/");
							pipeVariables.push(splitPipeData[splitPipeData.length-1]);
						}
					}
					

					var pruned_pipeVars = {};
					var tooltipText="";
					pipeVariables.forEach(function(x) { pruned_pipeVars[x] = (pruned_pipeVars[x] || 0)+1; });
					for(var keyName in pruned_pipeVars) {
						var textTmp;
						var value = pruned_pipeVars[keyName]
						if (value>1){textTmp = String(keyName+"("+value+")")}
						else{textTmp=String(keyName)}
						if (tooltipText==""){tooltipText+=textTmp}
						else{tooltipText+=","+textTmp}
					}
					
					var tooltipize = Labelizer.tooltipize().text(tooltipText);
					self.tooltip.call(tooltipize)
					  .style("width", "200px")
					  .style("left", (d3.event.pageX) + "px")
					  .style("top", (d3.event.pageY - 28) + "px");
				})
				.on("mouseout", function() {
					self.tooltip.transition().duration(500).style("opacity", 0);
				});

			  return textGroups;
			};

			Xdsm.prototype._createWorkflow = function() {
			  //  console.log(JSON.stringify(this.graph.chains));
			  var workflow = this.svg.insert("g", ":first-child")
								.attr("class", "workflow");
			  workflow.selectAll("g")
				.data(this.graph.chains)
			  .enter()
				.insert('g').attr("class", "workflow-chain")
				.selectAll('polyline')
				  .data(function(d) { return d; })  // eslint-disable-line brace-style
				.enter()
				  .append("polyline")
					.attr("class", function(d) {
					  return "link_" + d[0] + "_" + d[1];
					})
					.attr("points", function(d) {
					  var w = CELL_W * Math.abs(d[0] - d[1]);
					  var h = CELL_H * Math.abs(d[0] - d[1]);
					  var points = [];
					  if (d[0] < d[1]) {
						if (d[0] !== 0) {
						  points.push((-w) + ",0");
						}
						points.push("0,0");
						if (d[1] !== 0) {
						  points.push("0," + h);
						}
					  } else {
						if (d[0] !== 0) {
						  points.push(w + ",0");
						}
						points.push("0,0");
						if (d[1] !== 0) {
						  points.push("0," + (-h));
						}
					  }
					  return points.join(" ");
					})
				  .attr("transform", function(d) {
					var max = Math.max(d[0], d[1]);
					var min = Math.min(d[0], d[1]);
					var w;
					var h;
					if (d[0] < d[1]) {
					  w = CELL_W * max + X_ORIG;
					  h = CELL_H * min + Y_ORIG;
					} else {
					  w = CELL_W * min + X_ORIG;
					  h = CELL_H * max + Y_ORIG;
					}
					return "translate(" + (X_ORIG + w) + "," + (Y_ORIG + h) + ")";
				  });
			};

			Xdsm.prototype._createDataflow = function(edges) {
			  var dataflow = this.svg.insert("g", ":first-child")
							   .attr("class", "dataflow");

			  edges.each(function(d, i) {
				dataflow.insert("polyline", ":first-child")
				  .attr("points", function() {
					var w = CELL_W * Math.abs(d.col - d.row);
					var h = CELL_H * Math.abs(d.col - d.row);
					var points = [];
					if (d.iotype === "in") {
					  if (!d.io.fromU) {
						points.push((-w) + ",0");
					  }
					  points.push("0,0");
					  if (!d.io.toU) {
						points.push("0," + h);
					  }
					} else {
					  if (!d.io.fromU) {
						points.push(w + ",0");
					  }
					  points.push("0,0");
					  if (!d.io.toU) {
						points.push("0," + (-h));
					  }
					}
					return points.join(" ");
				  }).attr("transform", function() {
					var m = (d.col === undefined) ? i : d.col;
					var n = (d.row === undefined) ? i : d.row;
					var w = CELL_W * m + X_ORIG;
					var h = CELL_H * n + Y_ORIG;
					return "translate(" + (X_ORIG + w) + "," + (Y_ORIG + h) + ")";
				  });
			  });
			};

			Xdsm.prototype._layoutText = function(items) {
			  var grid = this.grid;
			  items.each(function(d, i) {
				var item = d3.select(this);
				if (grid[i] === undefined) {
				  grid[i] = new Array(items.length);
				}
				item.select("text").each(function(d, j) {
				  var that = d3.select(this);
				  var data = item.data()[0];
				  var m = (data.row === undefined) ? i : data.row;
				  var n = (data.col === undefined) ? i : data.col;
				  var bbox = that.nodes()[j].getBBox();
				  grid[m][n] = new Cell(-bbox.width / 2, 0, bbox.width, bbox.height);
				  that.attr("x", function() {
					return grid[m][n].x;
				  }).attr("y", function() {
					return grid[m][n].y;
				  }).attr("width", function() {
					return grid[m][n].width;
				  }).attr("height", function() {
					return grid[m][n].height;
				  });
				});
			  });

			  items.attr("transform", function(d, i) {
				var m = (d.col === undefined) ? i : d.col;
				var n = (d.row === undefined) ? i : d.row;
				var w = CELL_W * m + X_ORIG;
				var h = CELL_H * n + Y_ORIG;
				return "translate(" + (X_ORIG + w) + "," + (Y_ORIG + h) + ")";
			  });
			};

			Xdsm.prototype._customRect = function(node, d, i, offset) {
			  var grid = this.grid;
			  node.insert("rect", ":first-child").attr("x", function() {
				return grid[i][i].x + offset - PADDING;
			  }).attr("y", function() {
				return -grid[i][i].height * 2 / 3 - PADDING - offset;
			  }).attr("width", function() {
				return grid[i][i].width + (PADDING * 2);
			  }).attr("height", function() {
				return grid[i][i].height + (PADDING * 2);
			  }).attr("rx", function() {
				var rounded = d.type === 'optimization' ||
							  d.type === 'mda' ||
							  d.type === 'doe' ||
							  d.type === 'converger';
				return rounded ? (grid[i][i].height + (PADDING * 2)) / 2 : 0;
			  }).attr("ry", function() {
				var rounded = d.type === 'optimization' ||
							  d.type === 'mda' ||
							  d.type === 'doe' ||
							  d.type === 'converger';
				return rounded ? (grid[i][i].height + (PADDING * 2)) / 2 : 0;
			  });
			};

			Xdsm.prototype._customTrapz = function(edge, d, i, offset) {
			  var grid = this.grid;
			  edge.insert("polygon", ":first-child").attr("points", function(d) {
				var pad = 5;
				var w = grid[d.row][d.col].width;
				var h = grid[d.row][d.col].height;
				var topleft = (-pad - w / 2 + offset) + ", " +
							  (-pad - h * 2 / 3 - offset);
				var topright = (w / 2 + pad + offset + 5) + ", " +
							   (-pad - h * 2 / 3 - offset);
				var botright = (w / 2 + pad + offset - 5 + 5) + ", " +
							   (pad + h / 3 - offset);
				var botleft = (-pad - w / 2 + offset - 5) + ", " +
							  (pad + h / 3 - offset);
				var tpz = [topleft, topright, botright, botleft].join(" ");
				return tpz;
			  });
			};

			module.exports = Xdsm;

			},{"./labelizer.js":4,"d3":1}],6:[function(require,module,exports){
			/*
			 * XDSMjs
			 * Copyright 2016 Rémi Lafage
			 */
			"use strict";




			var d3 = require('d3');
			var Graph = require('./src/graph');
			var Xdsm = require('./src/xdsm');
			var Animation = require('./src/animation');


			//aigner: NEW!
			//#####################################################################//
			//aigner: functions for drawing expand- and collapse-symbols
			function drawExpandSymbol(aCircle, line1, line2)
			{        
				 aCircle = aCircle
					.attr("r", 10)
					.style("stroke", "black")
					.style("stroke-width", 2)
					.style("fill", "#99CC00")
					.style("fill-opacity", .6)
				line1 = line1
					.attr("x1", parseFloat(aCircle.attr("cx"))-5)
					.attr("y1", parseFloat(aCircle.attr("cy")))
					.attr("x2", parseFloat(aCircle.attr("cx"))+5)
					.attr("y2", parseFloat(aCircle.attr("cy")))
					.style("stroke", "black")
					.style("stroke-width", 2);
				line2 = line2
					.attr("x1", parseFloat(aCircle.attr("cx")))
					.attr("y1", parseFloat(aCircle.attr("cy"))-5)
					.attr("x2", parseFloat(aCircle.attr("cx")))
					.attr("y2", parseFloat(aCircle.attr("cy"))+5)
					.style("stroke", "black")
					.style("stroke-width", 2);
			}
			function drawRemoveSymbol(anXdsm, aCircle, aMinus)
			{        
				 var xOffset=150;
				 var yOffset=12;
				 aCircle = aCircle
					.attr("cx", anXdsm.svg.attr("width")-xOffset)
					.attr("cy", yOffset)
					.attr("r", 10)
					.classed("remCircle",true)
				aMinus = aMinus
					.attr("x1", anXdsm.svg.attr("width")-xOffset+5)
					.attr("y1", yOffset)
					.attr("x2", anXdsm.svg.attr("width")-xOffset-5)
					.attr("y2", yOffset)
					.classed("remMinus",true)
			}
			function drawTreeRemoveSymbol(aRemoveSymbol)
			{
				aRemoveSymbol = aRemoveSymbol
					.attr("class", "treeRemoveSymbol")
					.attr("transform", "translate(38,12)")
				var circle = aRemoveSymbol.append("circle");
				var minus = aRemoveSymbol.append("line");
				var cx = -20;
				var cy = 12;
				circle = circle
					.attr("cx", cx+5)
					.attr("cy", cy)
					.attr("r", 10)
					.classed("remCircle",true)
				minus = minus
					.attr("x1", cx)
					.attr("y1", cy)
					.attr("x2", cx+10)
					.attr("y2", cy)
					.classed("remMinus",true)
				aRemoveSymbol
					.on("mouseover", function(){
						d3.select(this).style("cursor", "pointer")
						circle.style("fill", "red").style("fill-opacity", .8);
						})
					.on("mouseout", function(){circle.style("fill", "red").style("fill-opacity", .6)})
			}

			//aigner: Update of the sub workflows
			function update_subXdsm(xdsms, refName)
			{
				var playButton = d3.select(".xdsm").select("."+refName).select(".playButton");
				var rect = playButton.select(".playButton_rect");
				playButton.on("mouseover",function(){
						d3.select(this).style("cursor", "pointer")
						rect.style("fill","#3399FF");
					})
					.on("mousedown", function()
					{
						rect.style("fill","#3399FF");
						var anim = new Animation(xdsms, refName);
						anim.run();
					})
					.on("mouseup", function(){rect.style("fill","#4B4B4B");})
					.on("mouseout", function(){rect.style("fill","#555555");})
			}
            
            //aigner: Move to front function
			d3.selection.prototype.moveToFront = function() {  
			  return this.each(function(){
				this.parentNode.appendChild(this);
			  });
			};

			//d3-context-menu for right-click-option
			d3.contextMenu = function (menu, openCallback) {

				// create the div element that will hold the context menu
				d3.selectAll('.d3-context-menu').data([1])
					.enter()
					.append('div')
					.attr('class', 'd3-context-menu');

				// close menu
				d3.select('body').on('click.d3-context-menu', function() {
					d3.select('.d3-context-menu').style('display', 'none');
				});

				// this gets executed when a contextmenu event occurs
				return function(data, index) {	
					var elm = this;

					d3.selectAll('.d3-context-menu').html('');
					var list = d3.selectAll('.d3-context-menu').append('ul');
						list.selectAll('li').data(menu).enter()
						.append('li')
						.html(function(d) {
							return d.title;
						})
						.on('mousedown', function(d, i) {
							d.onMouseDown(elm, data, index);
						})
						.on('mouseup', function(d, i) {
							d.onMouseUp(elm, data, index);
							d3.select('.d3-context-menu').style('display', 'none');
						})
						.on('mouseenter',function(d,i){
							d.onMouseOver(elm,data,index);
							if(d.childrenItems.length>0 )
								 {
                                  var li = this
								  d3.select(this).selectAll("ul").remove(); 
								  d3.select(this)
									.append("ul")
                                    .style("top",String(li.offsetTop-5)+"px")
									.selectAll("li")
									   .data(d.childrenItems)
										.enter().append("li")
										  .text(function(d) { return d.title; })
									 .on("mouseenter", function(d,i){
											d.onMouseOver(elm,data,index);
										})
									 .on('click',  function(d, i) {
											d.onMouseClick(elm, d, index);
										})
									 .on('mouseleave',function(d,i){
										
										});
								 }
							 else
								 return false;
						})
						.on('mouseleave',function(d,i){
							d3.select(this).selectAll("ul").style('display', 'none')                  
						});
					
					  

					// the openCallback allows an action to fire before the menu is displayed
					// an example usage would be closing a tooltip
					if (openCallback) openCallback(data, index);

					// display context menu
					d3.select('.d3-context-menu')
						.style('left', (d3.event.pageX - 2) + 'px')
						.style('top', (d3.event.pageY - 2) + 'px')
						.style('display', 'block');
                    
                    //Prevent the default event, which is the left-click. 
                    //This means, the context-menu will only appear on right mouse clicks
					d3.event.preventDefault();
                    
                    //Place context-menu always on top of everything esle
                    d3.select(".d3-context-menu").style("z-index",Number.MAX_SAFE_INTEGER);
				};
			};
			

			//aigner: Function creates a table
			function tabulate(aTable,data,columns) {
				var thead = aTable.append('thead');
				var	tbody = aTable.append('tbody');  

				// create a row for each object in the data
				var rows = tbody.selectAll('tr')
				  .data(data)
				  .enter()
				  .append('tr');

				// create a cell in each row for each column
				var cells = rows.selectAll('td')
				  .data(function (row) {
					return columns.map(function (column) {
					  return {column: column, value: row[column]};
					});
				  })
				  .enter()
				  .append('td')
					.html(function (d) { return d.value; });

				return aTable;
			}

			function loader(config) {
			  return function() {
				var radius = Math.min(config.width, config.height) / 2;
				var tau = 2 * Math.PI;

				var arc = d3.svg.arc()
						.innerRadius(radius*0.5)
						.outerRadius(radius*0.9)
						.startAngle(0);

				var svg = d3.select(config.container).append("svg")
					.attr("id", config.id)
					.attr("width", config.width)
					.attr("height", config.height)
				  .append("g")
					.attr("transform", "translate(" + config.width / 2 + "," + config.height / 2 + ")")

				var background = svg.append("path")
						.datum({endAngle: 0.33*tau})
						.style("fill", "#4D4D4D")
						.attr("d", arc)
						.call(spin, 1500)

				function spin(selection, duration) {
					selection.transition()
						.ease("linear")
						.duration(duration)
						.attrTween("transform", function() {
							return d3.interpolateString("rotate(0)", "rotate(360)");
						});

					setTimeout(function() { spin(selection, duration); }, duration);
				}

				function transitionFunction(path) {
					path.transition()
						.duration(7500)
						.attrTween("stroke-dasharray", tweenDash)
						.each("end", function() { d3.select(this).call(transition); });
				}

			  };
			}

			var myLoader = loader({width: 960, height: 500, container: "#loader_container", id: "loader"});
			
			//aigner: NEW!
			//aigner: Here, the data is read and the XDSM is created
			//#####################################################################//
			function startXDSM(data, graphID) 
			{	
				//Highlight function, that shows usage of a node in the XDSM
				function highlight(data)
				{
					var xPath = data.data.xPath;
					
                    scenarioKeys.forEach(function(k)
                    {
                        var xdsm_tmp;
                        xdsm_tmp = xdsms[k];
                        if (xdsm_tmp)
                        {
                            xdsm_tmp.svg.selectAll(".edge").each(function(p) 
                            {
                                var firstElement_tmp = p.name.split("/")[1]
                                var xPath_fromFirst = "/"+firstElement_tmp+xPath.split(firstElement_tmp)[1]
                                var this_edge  = this
                                var p_name_list = p.name.split(",")
                                p_name_list.forEach(function(p_name) 
                                {
                                    if (include(p_name+"__",xPath_fromFirst+"__") || include(p_name,xPath_fromFirst+"/"))
                                    {
                                        var highlightEdge = d3.select(this_edge).select("polygon");
                                        highlightEdge
                                            .style("stroke-width",5.)
                                            .style("stroke","#CC0000")
                                        d3.selectAll(".treeFrame")
                                            .attr("fill-opacity", 0.5)
                                            .attr("stroke-opacity", 0.5);
                                        d3.selectAll(".nodeText").style("fill-opacity",0.5);
                                    }
                                })
                            })
                        }
                    })
					
				}
				
				//Unhighlight function again
				function unhighlight(data)
				{
					var xPath = data.data.xPath;
					
                    scenarioKeys.forEach(function(k)
                    {
                        var xdsm_tmp;
                        xdsm_tmp = xdsms[k];
                        if (xdsm_tmp)
                        {
                            xdsm_tmp.svg.selectAll(".edge").each(function(p) 
                            {
                                var firstElement_tmp = p.name.split("/")[1]
                                var xPath_fromFirst = "/"+firstElement_tmp+xPath.split(firstElement_tmp)[1]
                                var this_edge  = this
                                var p_name_list = p.name.split(",")
                                p_name_list.forEach(function(p_name) 
                                {
                                    if (include(p_name+"__",xPath_fromFirst+"__") || include(p_name,xPath_fromFirst+"/"))
                                    {
                                        var highlightEdge = d3.select(this_edge).select("polygon");
                                        highlightEdge
                                            .style("stroke-width",1.)
                                            .style("stroke","black");
                                        d3.selectAll(".treeFrame")
                                            .attr("fill-opacity", 0.8)
                                            .attr("stroke-opacity", 0.8);
                                        d3.selectAll(".nodeText").style("fill-opacity",1);
                                    }
                                })
                            })
                        }
                    })
					
				}							
				  
				
				function showVariableTable(aVariable)
				{						
					var headLine = "Node Information (" + aVariable.data.name + ")";
					var data = [];
					// render the table(s)
					data.push({ "name" : "Name", "value" : "\""+aVariable.data.name+"\"" })
					data.push({ "name" : "xPath", "value" : aVariable.data.xPath })
					if (aVariable.data.type){data.push({ "name" : "Type", "value" : aVariable.data.type })}
					if (aVariable.data.level){data.push({ "name" : "Level", "value" : aVariable.data.level })}
					if (aVariable.data.children){data.push({ "name" : "Number of children", "value" : aVariable.data.children.length })}
					if (aVariable.data.dimension){data.push({ "name" : "Dimension", "value" : aVariable.data.dimension })}
					else if(aVariable.data.dimension===null){data.push({ "name" : "Dimension", "value" : "undefined" })}
					if (aVariable.data.value){data.push({ "name" : "Value(s)", "value" : aVariable.data.value })}
					
					var d3_body = d3.select("body");
					
					var panel_div = d3_body.append("div").attr("class", "myPanel panel-default")
					panel_div.append("div").attr("class","panel-heading")
						.append("div").attr("class","panel_title").append("h4").text(headLine)
					var listGroup = panel_div.append("div").attr("class","panel-body")
						.append("table").attr("id","myTable")
						.append("tbody")
						
					data.forEach(function(listElement)
					{
						var row = listGroup.append("tr")
						row.append("td").text(listElement.name)
						row.append("td").text(listElement.value)
						
					})
					$('.myPanel').lobiPanel({
						reload: false,
						editTitle: false,
						expand: false,
						unpin: false,
						resize: "none",
						minWidth: 200,
						minHeight: 200,
						maxWidth: 1100,
						maxHeight: 1200,
					});
					$('.myPanel').lobiPanel('unpin');
				}
				
                var markedVariables = [];
				function preMarkVariable(variableData)
                {
                    markedVariables.push(variableData)
                    bootbox.hideAll();
                    var bootboxContent = {title: 'Pre-assign parameter role', message: "<p><b>Successfully pre-assigned parameter.</b></p>"
                    +"<p>To actually assign the parameter role you have to press the <b>'Assign parameter roles'</b> button from the <b>'FPG Manipulations'</b> menu."
                    +"<p>Note: You can first premark all parameters and the assign them altogether to save time</p>"};
                    kadmosSuccessMessage(bootboxContent)
                }
                
                function markVariable(variableData_arr)
				{
					d3.select('.d3-context-menu').style('display', 'none');    
                 
                    //Stringify variable data before sending it to kadmos
                    var variableData_str = JSON.stringify(variableData_arr)
                    
					var bootboxContent = {title: 'Assign parameter roles', message: '<p>Please be patient...</p>'};
					var xhr = $.ajax({
						type: 'POST',
						url: '/kadmos_mark_variable',
							data: {
							'graphID':graphID, 
							'variableData_str':variableData_str,
							'currentOrder':nodeOrder, 'sessionId': sessionId
						},
						success: function(result)
						{
							if (result.includes("ERROR:"))
							{
								bootboxContent.message = result
								kadmosErrorMessage(bootboxContent);
							}
							else
							{
								var updatedData = {};
								updatedData = data;
								var graphData = JSON.parse(result);             
								for (var i = 0; i < updatedData.graphs.length; i++)
								{
									if (graphID == updatedData.graphs[i].id)
									{
										updatedData.graphs[i] = graphData.graphs[0];   
									}
								}
																			
								clearView();
								makeKadmosMenu(updatedData);
								xdsm_script(updatedData,graphID);
								
								bootboxContent.message = "Success!"
								kadmosSuccessMessage(bootboxContent)
							}
						},
						error: function(result)
						{
							bootboxContent.message = result
							kadmosErrorMessage(bootboxContent);
						}
					});
					kadmosHavePatience(xhr, bootboxContent)
				}
				
				function unmarkVariable(xPath)
				{
					d3.select('.d3-context-menu').style('display', 'none');                                
					
					var bootboxContent = {title: 'Unmarking variable<p>Please be patient...</p>'};
					var xhr = $.ajax({
						type: 'POST',
						url: '/kadmos_unmark_variable',
							data: {
							'graphID':graphID, 
							'xPath':xPath, 
							'currentOrder':nodeOrder, 'sessionId': sessionId
						},
						success: function(result)
						{
							if (result.includes("ERROR:"))
							{
								bootboxContent.message = result
								kadmosErrorMessage(bootboxContent);
							}
							else
							{
								var updatedData = {};
								updatedData = data;
								var graphData = JSON.parse(result);             
								for (var i = 0; i < updatedData.graphs.length; i++)
								{
									if (graphID == updatedData.graphs[i].id)
									{
										updatedData.graphs[i] = graphData.graphs[0];   
									}
								}
																			
								clearView();
								makeKadmosMenu(updatedData);
								xdsm_script(updatedData,graphID);
								
								bootboxContent.message = "Success!"
								kadmosSuccessMessage(bootboxContent)
							}
						},
						error: function(result)
						{
							bootboxContent.message = result
							kadmosErrorMessage(bootboxContent);
						}
					});
					kadmosHavePatience(xhr, bootboxContent)
				}
				
				//aigner: Marking variables as ... in KADMOS
				var markingOptions = [
					{  
						title: 'design variable',
						onMouseClick: function(elm, d, i) {
							var theVariableData = {};
							theVariableData.variableType = "designVariable";
							theVariableData.operator = "";
							theVariableData.upperBound = 0.;
							theVariableData.lowerBound = 0.;
							theVariableData.nominalValue = 0.;
							theVariableData.samples = "";
							
							bootbox.hideAll();
							bootbox.confirm({
                            closeButton: false,
							title: 'Marking variable as ' + theVariableData.variableType,
							message:"<form id='infos' action=''>"
									+"\Nominal Value: <input type='number' id='nominalValue' name='nominalValue' value='0' /><br/>"
									+"\Upper Bound:   <input type='number' id='upperBound' name='upperBound' value='0' /><br/>"
									+"\Lower Bound:   <input type='number' id='lowerBound' name='lowerBound' value='0' />\</form>"
									+"\Sample list (optional):   <input type='text' id='samples' name='samples' />\</form>", 
							callback: function(result){
									if(result){
										theVariableData.nominalValue = parseFloat($('#nominalValue').submit()[0].value.replace(/,/g, ''));
										theVariableData.upperBound = parseFloat($('#upperBound').submit()[0].value.replace(/,/g, ''));
										theVariableData.lowerBound = parseFloat($('#lowerBound').submit()[0].value.replace(/,/g, ''));
										theVariableData.lowerBound = parseFloat($('#lowerBound').submit()[0].value.replace(/,/g, ''));
										theVariableData.samples = $('#samples').submit()[0].value;
                                        
                                        
                                        
                                        if (isNaN(parseInt(theVariableData.nominalValue))
                                                || isNaN(parseInt(theVariableData.lowerBound))
                                                || isNaN(parseInt(theVariableData.upperBound)))
                                        {
                                            bootbox.hideAll();
                                            bootbox.dialog({
                                                closeButton: false,
                                                title: "Assign parameter role",
                                                message: "<b>ERROR:</b> Invalid variable type! Variables can only be numbers!",
                                                buttons : { cancel:
                                                {
                                                    label: "OK",
                                                    className: 'btn-danger',
                                                    callback: function(){}
                                                }
                                                }})
                                        }
                                        else if(theVariableData.nominalValue > theVariableData.upperBound)
                                        {
                                            bootbox.hideAll();
                                            bootbox.dialog({
                                                closeButton: false,
                                                title: "Assign parameter role",
                                                message: "<b>ERROR:</b> Nominal value cannot be larger than upper bound!",
                                                buttons : { cancel:
                                                {
                                                    label: "OK",
                                                    className: 'btn-danger',
                                                    callback: function(){}
                                                }
                                                }})
                                        }
                                        else if (theVariableData.nominalValue < theVariableData.lowerBound)
                                        {
                                            bootbox.hideAll();
                                            bootbox.dialog({
                                                closeButton: false,
                                                title: "Assign parameter role",
                                                message: "<b>ERROR:</b> Nominal value cannot be smaller than lower bound!",
                                                buttons : { cancel:
                                                {
                                                    label: "OK",
                                                    className: 'btn-danger',
                                                    callback: function(){}
                                                }
                                                }})
                                        }
                                        
                                        else
                                        {
                                            var firstFromSchema = currentGraph.variableSchemes["schema"][0].xPath.split("/")[1]
                                            var xPath = "/"+firstFromSchema+elm.__data__.data.xPath.split(firstFromSchema)[1];
                                            theVariableData.xPath = xPath;
                                            preMarkVariable(theVariableData);
                                        }
									}
								}
							});
						},
						onMouseOver: function(elm,d,i){}
					},
					{  
						title: 'objective',
						onMouseClick: function(elm, d, i) {
							var theVariableData = {};
							theVariableData.variableType = "objective";
							theVariableData.operator = "";
							theVariableData.upperBound = 0.;
							theVariableData.lowerBound = 0.;
							theVariableData.nominalValue = 0.;
							var firstFromSchema = currentGraph.variableSchemes["schema"][0].xPath.split("/")[1]
                            var xPath = "/"+firstFromSchema+elm.__data__.data.xPath.split(firstFromSchema)[1];
                            theVariableData.xPath = xPath;
                            preMarkVariable(theVariableData);
						},
						onMouseOver: function(elm,d,i){}
					},
					{  
						title: 'constraint',
						onMouseClick: function(elm, d, i) {
							var theVariableData = {};
							theVariableData.variableType = "constraint";
							theVariableData.operator = "";
							theVariableData.upperBound = 0.;
							theVariableData.lowerBound = 0.;
							theVariableData.nominalValue = 0.;
							
							bootbox.hideAll();
							bootbox.confirm({
                            closeButton: false,
							title: 'Marking variable as ' + theVariableData.variableType,
							message:"<form id='infos' action=''>"
									+"\Nominal Value: <input type='text' id='nominalValue' name='nominalValue' /><br/>"
									+"\Mathematical operator (<, <=, = ,>=, >):   "
                                    +"<select id='operator' name='operator'>"
                                    +"<option value='<'><</option>"
                                    +"<option value='<='><=</option>"
                                    +"<option value='=='>==</option>"
                                    +"<option value='>='>>=</option>"
                                    +"<option value='>'>></option>"
                                    +"</select>\</form>", 
							callback: function(result){
									if(result){
										theVariableData.nominalValue = parseFloat($('#nominalValue').submit()[0].value.replace(/,/g, ''));
										theVariableData.operator = $('#operator').submit()[0].value;
                                                                                    
                                        if(theVariableData.operator != "<" 
                                           && theVariableData.operator != "<=" 
                                           && theVariableData.operator != "==" 
                                           && theVariableData.operator != ">=" 
                                           && theVariableData.operator != ">")                                           
                                        {
                                            bootbox.hideAll();
                                            bootbox.dialog({
                                                closeButton: false,
                                                title: "Assign parameter role",
                                                message: "<b>ERROR:</b> Invalid operator \"" + theVariableData.operator + "\"!",
                                                buttons : { cancel:
                                                {
                                                    label: "OK",
                                                    className: 'btn-danger',
                                                    callback: function(){}
                                                }
                                                }})
                                        }
                                        else if(isNaN(parseInt(theVariableData.nominalValue)))
                                        {
                                            bootbox.hideAll();
                                            bootbox.dialog({
                                                closeButton: false,
                                                title: "Assign parameter role",
                                                message: "<b>ERROR:</b> Nominal value must be a number!",
                                                buttons : { cancel:
                                                {
                                                    label: "OK",
                                                    className: 'btn-danger',
                                                    callback: function(){}
                                                }
                                                }})
                                        }
                                        else
                                        {
                                            var firstFromSchema = currentGraph.variableSchemes["schema"][0].xPath.split("/")[1]
                                            var xPath = "/"+firstFromSchema+elm.__data__.data.xPath.split(firstFromSchema)[1];
                                            theVariableData.xPath = xPath;
                                            preMarkVariable(theVariableData);
                                        }
									}
								}
							});
						},
						onMouseOver: function(elm,d,i){}
					},
					{  
						title: 'quantity of interest',
						onMouseClick: function(elm, d, i) {
							var theVariableData = {};
							theVariableData.variableType = "quantityOfInterest";
							theVariableData.operator = "";
							theVariableData.upperBound = 0.;
							theVariableData.lowerBound = 0.;
							theVariableData.nominalValue = 0.;
							var firstFromSchema = currentGraph.variableSchemes["schema"][0].xPath.split("/")[1]
                            var xPath = "/"+firstFromSchema+elm.__data__.data.xPath.split(firstFromSchema)[1];
                            theVariableData.xPath = xPath;
                            preMarkVariable(theVariableData);
						},
						onMouseOver: function(elm,d,i){}
					},
				];
				
			   
				//menu --> functions for right click options
				var nodeMenu = [
				{
					title: 'Show node information',
					onMouseDown: function(elm, d, i) {
						showVariableTable(d);
					},
					onMouseUp: function(elm, d, i) {},
					onMouseOver: function(elm, d, i) {},
					childrenItems: []
				},
				{
					title: 'Show usage of node in XDSM',
					onMouseDown: function(elm, d, i) {
						highlight(d);
					},
					onMouseUp: function(elm, d, i) {
						unhighlight(d);
					},
					onMouseOver: function(elm, d, i) {
					},
					childrenItems: []
				},
				{
					title: 'Copy x-path to clipboard',
					onMouseDown: function(elm, d, i) {
                        window.prompt("Copy to clipboard: Ctrl+C, Enter", d.data.xPath);
						d3.select('.d3-context-menu').style('display', 'none');
					},
					onMouseUp: function(elm, d, i) {
					},
					onMouseOver: function(elm, d, i) {
					},
					childrenItems: []
				},
				{ 
					title: 'Pre-assign parameter',
					onMouseDown: function(elm, d, i) {},
					onMouseUp: function(elm, d, i) {},
					onMouseOver: function(elm, d, i) {},
					childrenItems: markingOptions
				},
				{ 
					title: 'Un-assign parameter role',
				   onMouseDown: function(elm, d, i) {
						bootbox.hideAll();
						bootbox.confirm("Are you sure you want to do this?", function(sure)
						{
							if (sure)
							{
                                var firstFromSchema = currentGraph.variableSchemes["schema"][0].xPath.split("/")[1]
                                var xPath = "/"+firstFromSchema+elm.__data__.data.xPath.split(firstFromSchema)[1];
                                unmarkVariable(xPath)
                            }
						})
					},
					onMouseUp: function(elm, d, i) {},
					onMouseOver: function(elm, d, i) {},
					childrenItems: []
				}
				]		                
                
			   
				var graphs, currentGraph, varCategories;
				
				graphs =  data.graphs;
				for (var i=0;i<graphs.length;i++)
				{
					if (graphs[i].id==graphID)
					{
						currentGraph = graphs[i]
					}
				}
				varCategories =  data.categories;
                
				//aigner: Get xdsm data
				var mdo = currentGraph.xdsm;
				
				d3.select("body").append("div").attr("class","xdsmDiv")	
				//################################################################################################//	
				var headerDiv = d3.select(".xdsmDiv").append("div").attr("class","panel panel-primary")
				headerDiv.append("div").attr("class","panel-heading text-center")
					.append("h3")
					.attr("class","panel-title")
					.style("font-family","Arial")
					.style("font-size","20pt")
					.text("XDSM View")
				var name_tmp="";
				if (currentGraph.name){name_tmp=currentGraph.name}
				else{name_tmp="Graph " + currentGraph.id}
				headerDiv.append("div").attr("class","panel-body")
					.style("font-family","Arial")
					.style("font-size","16pt")
					.text("Graph name: " + name_tmp)
				headerDiv.append("div").attr("class","panel-body")
					.style("font-family","Arial")
					.style("font-size","16pt")
					.text("Graph description: " + currentGraph.description)
				//################################################################################################//	
                
                
                
                
                //aigner: Functions for toolMenu
                //################################################################################################//
                //aigner: Creation of input/output tree
                //############################################################
                function showIOTree(aCategory, categoryDescr, aNode, io)
                {                        
                    var edges = d3.selectAll(".edge");
                    var array="";
                    var name;
                    edges.each(function(edge)
                    {			
                        if (io=="in")
                        {
                            name = "Input tree view: " + aNode.id + "; Categorization: " + categoryDescr;
                            if (edge.to == aNode.id)
                            {
                                array = array + "," + edge.name;
                            }
                        }
                        else if (io=="out")
                        {
                            name = "Output tree view:" + aNode.id + "; Categorization: " + categoryDescr;
                            if (edge.from == aNode.id)
                            {
                                array = array + "," + edge.name;
                            }
                        }
                    })
                    
                    
                    var headLine = name;
                    var d3_body = d3.select("body");
                    var lobiID = String(getRandomInt(0,1000))
                    var divClassName = "treeDiv" + lobiID;
                    var treeLayoutdiv = d3_body.append("div").attr("class",divClassName + " panel-default")
                                                    .style("left",(d3.event.pageX) + "px")
                                                    .style("top",(d3.event.pageY - 28) + "px")
                                                    .style("position", "absolute")
                    treeLayoutdiv.append("div").attr("class","panel-heading")
                        .append("div").attr("class","panel_title").append("h4").text(headLine)
                    $('.'+divClassName).lobiPanel({
                        reload: false,
                        editTitle: false,
                        unpin: false,
                        minWidth: 1000,
                        maxWidth: 100000,
                        minHeight: 500,
                        maxHeight: 100000,
                    });
                    $('.'+divClassName).lobiPanel('unpin');
                    var treeLayoutSVG = treeLayoutdiv.append("svg").attr("class","treeLayoutSVG")
                    var treeLayout = treeLayoutSVG.append("g").attr("class","treeLayout");
                    maketreeLayout(array, treeLayout, treeLayoutSVG, treeLayoutdiv, divClassName, headLine, aCategory);
                }
                //############################################################
                
                //aigner: Creation of input/output list
                //############################################################
                function showIOList(aCategory, categoryDescr, aNode, io)
                {                        
                    var edges = d3.selectAll(".edge");
                    var array="";
                    var title;
                    edges.each(function(edge)
                    {			
                        if (io=="in")
                        {
                            title = "List view of all inputs for " + aNode.id + "; Categorization: " + categoryDescr;
                            if (edge.to == aNode.id)
                            {
                                array = array + "," + edge.name;
                            }
                        }
                        else if (io=="out")
                        {
                            title = "List view of all outputs for " + aNode.id + "; Categorization: " + categoryDescr;
                            if (edge.from == aNode.id)
                            {
                                array = array + "," + edge.name;
                            }
                        }
                    })
                    
                    var variables = [];
                    var pipeData = array;
                    variables = JSON.parse(JSON.stringify(currentGraph.variableSchemes[aCategory]))
                    prune_tree(pipeData,variables)
                    variables.forEach(function(variable)
                    {
                        variable.name = variable.xPath
                        //work around because nodeMenu expexts the data, to have a "data" object inside
                        variable.data = variable
                        variable.data.name = variable.xPath.split("/")[variable.xPath.split("/").length-1]
                    })

                    showList(title,variables,nodeMenu);
                    
                }
                //############################################################                
                
              
                function showToolTable(aTool)
                {
                    var aToolNameSplit = aTool.name.split(': ')			
                    var headLine;
                    if (aToolNameSplit.length>1){headLine = "Competence Information: (" + aToolNameSplit[1] + ")";}
                    else {headLine = "Competence Information: (" + aToolNameSplit[0] + ")";}
                    
                    
                    var data = [];
                    // render the table(s)
                    if (aTool.metadata.length==0)
                    {
                        data.push({ "name" : "NO TOOL METADATA AVAILABLE", "value" : "..." })
                    }
                    function findSubMetaData(aMetaData)
                    {
                        for(var key in aMetaData)
                        {
                            if (typeof aMetaData[key] === 'object')
                            {
                                data.push({ "name" : key, "value" : ''}) ;
                                findSubMetaData(aMetaData[key]);
                            }
                            else
                            {
                                data.push({ "name" : key, "value" : aMetaData[key] })
                            }
                        }
                    }
                    for (var j=0; j < aTool.metadata.length;j++)
                    {
                        var metaData = aTool.metadata[j];
                        findSubMetaData(metaData);						
                    }
                
                    
                    var d3_body = d3.select("body");
                    
                    var panel_div = d3_body.append("div").attr("class", "myPanel panel-default")
                    panel_div.append("div").attr("class","panel-heading")
                        .append("div").attr("class","panel_title").append("h4").text(headLine)
                    var listGroup = panel_div.append("div").attr("class","panel-body")
                        .append("table").attr("id","myTable")
                        .append("tbody")
                        
                    data.forEach(function(listElement)
                    {
                        var row = listGroup.append("tr")
                        row.append("td").text(listElement.name)
                        row.append("td").text(listElement.value)
                        
                    })
                    $('.myPanel').lobiPanel({
                        reload: false,
                        editTitle: false,
                        expand: false,
                        unpin: false,
                        resize: "none",
                        minWidth: 200,
                        minHeight: 200,
                        maxWidth: 1100,
                        maxHeight: 1200,
                    });
                    $('.myPanel').lobiPanel('unpin');
                }
                
                
                function changeNodePosition(aNode)
                {               
                    var theOptions = [{text: 'Choose new position...', value: '',}];
                    for (var j=1; j<currentGraph.xdsm.nodes.length;j++)
                    {
                        //aigner: value: j-1 because in KADMOS the coordinator is not in the node list, therefore all other competences' indices are decreased by 1!
                        theOptions.push({text: String(j) + " ("+currentGraph.xdsm.nodes[j].name+")", value: j-1});
                    }
                    bootbox.hideAll();
                    bootbox.prompt(
                    {
                        closeButton: false,
                        title: "Please specify a new position for the competence \""+aNode.name+"\"",
                        inputType: 'select',
                        inputOptions: theOptions,
                        callback: function (newPos) {
                            if (newPos!=null && newPos !='')
                            {
                                bootbox.hideAll();
                                bootbox.confirm("Are you sure you want to do this?", function(sure){
                                    if (sure)
                                    {
                                        var bootboxContent = {title: "Change competence position", message: '<p>Please be patient...</p>'};
                                        var xhr = $.ajax({
                                            type: 'POST',
                                            url: '/kadmos_change_node_pos',
                                            data: {'graphID':graphID, 'nodeName':aNode.uID, 'newPos':newPos, 'currentOrder':nodeOrder, 'sessionId': sessionId},
                                            success: function(result)
                                            {
                                                if (result.includes("ERROR:"))
                                                {
                                                    bootboxContent.message = result
                                                    kadmosErrorMessage(bootboxContent);
                                                }
                                                else
                                                {
                                                    var updatedData = {};
                                                    updatedData = data;
                                                    var graphData = JSON.parse(result);             
                                                    for (var i = 0; i < updatedData.graphs.length; i++)
                                                    {
                                                        if (graphID == updatedData.graphs[i].id)
                                                        {
                                                            updatedData.graphs[i] = graphData.graphs[0];   
                                                        }
                                                    }
                                                    
                                                    clearView();
                                                    makeKadmosMenu(updatedData);
                                                    xdsm_script(updatedData,graphID);
                                                    
                                                    bootboxContent.message = "Success!"
                                                    kadmosSuccessMessage(bootboxContent)
                                                }
                                            },
                                            error: function(result)
                                            {
                                                bootboxContent.message = result
                                                kadmosErrorMessage(bootboxContent);
                                            }
                                        });
                                        kadmosHavePatience(xhr, bootboxContent)
                                    }
                                })
                            }
                        }
                    });
                }
                
                function deleteNode(aNode)
                {                  
                    bootbox.hideAll();
                    bootbox.confirm("Are you sure you want to do this?", function(sure)
                    {
                        if (sure)
                        {                                
                            var bootboxContent = {title: "Delete competence", message: '<p>Please be patient...</p>'};
                            var xhr = $.ajax({
                                type: 'POST',
                                url: '/kadmos_delete_node',
                                data: {'graphID':graphID, 'nodeName':aNode.uID, 'currentOrder':nodeOrder, 'sessionId': sessionId},
                                success: function(result)
                                {
                                    if (result.includes("ERROR:"))
                                    {
                                        bootboxContent.message = result
                                        kadmosErrorMessage(bootboxContent);
                                    }
                                    else
                                    {
                                        var updatedData = {};
                                        updatedData = data;
                                        var graphData = JSON.parse(result);             
                                        for (var i = 0; i < updatedData.graphs.length; i++)
                                        {
                                            if (graphID == updatedData.graphs[i].id)
                                            {
                                                updatedData.graphs[i] = graphData.graphs[0];   
                                            }
                                        }
                                        
                                        clearView();
                                        makeKadmosMenu(updatedData);
                                        xdsm_script(updatedData,graphID);
                                        
                                        bootboxContent.message = "Success!"
                                        kadmosSuccessMessage(bootboxContent)
                                    }
                                },
                                error: function(result)
                                {
                                    bootboxContent.message = result
                                    kadmosErrorMessage(bootboxContent);
                                }
                            });
                            kadmosHavePatience(xhr, bootboxContent)
                        }
                    })
                }
                
                
                function addMathematicalFunction()
                {               
                    bootbox.hideAll();
                    
                    var html = d3.select("html")
                    var form_content = html.append("div").attr("class","form-content").attr("style","display:none")
                    var form = form_content.append("form").attr("class","form").attr("role","form")
                    var form_group, input;
                        
                    //Tool information
                    form_group = form.append("div").attr("class","form-group")
                    form_group.append("text").text("Name")
                    input = form_group.append("input")
                        .attr("id","function_node")
                        .attr("class","form-control")
                        .attr("name","function_node")
                        .attr("placeholder","Add a tool name here...")
                    //Tool input nodes
                    form_group = form.append("div").attr("class","form-group")
                    form_group.append("text").text("Tool input nodes")
                    input = form_group.append("input")
                        .attr("id","input_nodes_xPath")
                        .attr("class","form-control")
                        .attr("name","input_nodes_xPath")
                        .attr("placeholder","xPaths of the input nodes (please seperate with comma)")
                    input = form_group.append("input")
                        .attr("id","input_nodes_name")
                        .attr("class","form-control")
                        .attr("name","input_nodes_name")
                        .attr("placeholder","Variable names of the input nodes (Please separate with comma)")
                    //Tool output nodes
                    form_group = form.append("div").attr("class","form-group")
                    form_group.append("text").text("Tool output nodes")
                    input = form_group.append("input")
                        .attr("id","output_node_xPath")
                        .attr("class","form-control")
                        .attr("name","output_node_xPath")
                        .attr("placeholder","xPath of the output node")
                    input = form_group.append("input")
                        .attr("id","equation")
                        .attr("class","form-control")
                        .attr("name","equation")
                        .attr("placeholder","Equation for the output node")
                    input = form_group.append("input")
                        .attr("id","language")
                        .attr("class","form-control")
                        .attr("name","language")
                        .attr("placeholder","Programming language (e.g. 'Python')")
                        
                    
                    var modal = bootbox.dialog({
                        closeButton: false,
                        message: $(".form-content").html(),
                        title: "Add mathematical function",
                        size: "large",
                        buttons: [
                          {
                            label: "Cancel",
                            className: "btn btn-default pull-left",
                            callback: function() {
                              modal.modal("hide");
                              d3.selectAll(".form-content").remove();
                            }
                          },
                          {
                            label: "OK",
                            className: "btn btn-primary pull-left",
                            callback: function() {
                                
                                //get form data
                                var form_data = {
                                    function_node: $('form #function_node').val(),
                                    input_nodes_xPath: $('form #input_nodes_xPath').val(),
                                    input_nodes_name: $('form #input_nodes_name').val(),
                                    output_node_xPath: $('form #output_node_xPath').val(),
                                    equation: $('form #equation').val(),
                                    language: $('form #language').val()
                                }
                                
                                
                                var bootboxContent = {title: "Add mathematical function", message: '<p>Please be patient...</p>'};
                                var xhr = $.ajax({
                                    type: 'POST',
                                    url: '/kadmos_add_mathematical_function',
                                    data: {'graphID':graphID, 'currentOrder':nodeOrder, 'form_data':JSON.stringify(form_data), 'sessionId': sessionId},
                                    success: function(result)
                                    {
                                        if (result.includes("ERROR:"))
                                        {
                                            bootboxContent.message = result
                                            kadmosErrorMessage(bootboxContent);
                                        }
                                        else
                                        {
                                            var updatedData = {};
                                            updatedData = data;
                                            var graphData = JSON.parse(result);             
                                            for (var i = 0; i < updatedData.graphs.length; i++)
                                            {
                                                if (graphID == updatedData.graphs[i].id)
                                                {
                                                    updatedData.graphs[i] = graphData.graphs[0];   
                                                }
                                            }
                                            
                                            clearView();
                                            makeKadmosMenu(updatedData);
                                            xdsm_script(updatedData,graphID);
                                            
                                            bootboxContent.message = "Success!"
                                            kadmosSuccessMessage(bootboxContent)
                                        }
                                    },
                                    error: function(result)
                                    {
                                        bootboxContent.message = result
                                        kadmosErrorMessage(bootboxContent);
                                    }
                                });
                                kadmosHavePatience(xhr, bootboxContent)
                                kadmosHavePatience(xhr, bootboxContent)
                                modal.modal("hide");
                                d3.selectAll(".form-content").remove();
                            }
                          }
                        ],
                        onEscape: function() {
                          modal.modal("hide");
                          d3.selectAll(".form-content").remove();
                        }
                    });
                    
                        
                    
                }
                
                
                 //aigner: children items for toolMenu
                //############################################################
                var inputChildrenitemsTree = [];
                var outputChildrenitemsTree = [];
                for (var j=0; j< varCategories.length; j++)
                {
                    inputChildrenitemsTree.push({title: 'according to ' + varCategories[j].description,
                                             varCategory: varCategories[j].name,
                                             description: varCategories[j].description,
                                             onMouseClick: function(elm, data, i) {showIOTree(data.varCategory,data.description,elm.__data__,"in")},
                                             onMouseOver: function(elm,data,i){}});
                    outputChildrenitemsTree.push({title: 'according to ' + varCategories[j].description,
                                             varCategory: varCategories[j].name,
                                             description: varCategories[j].description,
                                             onMouseClick: function(elm, data, i) {
                                                showIOTree(data.varCategory,data.description,elm.__data__,"out")},
                                             onMouseOver: function(elm,data,i){}});
                }
                
                var inputChildrenitemsList = [];
                var outputChildrenitemsList = [];
                for (var j=0; j< varCategories.length; j++)
                {
                    inputChildrenitemsList.push({title: 'according to ' + varCategories[j].description,
                                             varCategory: varCategories[j].name,
                                             description: varCategories[j].description,
                                             onMouseClick: function(elm, data, i) {showIOList(data.varCategory,data.description,elm.__data__,"in")},
                                             onMouseOver: function(elm,data,i){}});
                    outputChildrenitemsList.push({title: 'according to ' + varCategories[j].description,
                                             varCategory: varCategories[j].name,
                                             description: varCategories[j].description,
                                             onMouseClick: function(elm, data, i) {showIOList(data.varCategory,data.description,elm.__data__,"out")},
                                             onMouseOver: function(elm,data,i){}});
                }
                //############################################################
                
                
                //menu --> functions for right click options (coordinator)
                var toolMenuCoor = [
                {
                    title: 'Show input variable tree...',
                    onMouseDown: function(elm, k, i) {
                    },
                    onMouseUp: function(elm, k, i) {
                    },
                    onMouseOver: function(elm, d, i) {
                    },
                    childrenItems: inputChildrenitemsTree
                },
                {
                    title: 'Show output variable tree...',
                    onMouseDown: function(elm, k, i) {
                    },
                    onMouseUp: function(elm, k, i) {
                    },
                    onMouseOver: function(elm, d, i) {
                    },
                    childrenItems: outputChildrenitemsTree
                },
                {
                    title: 'Show input variable list...',
                    onMouseDown: function(elm, k, i) {
                    },
                    onMouseUp: function(elm, k, i) {
                    },
                    onMouseOver: function(elm, d, i) {
                    },
                    childrenItems: inputChildrenitemsList
                },
                {
                    title: 'Show output variable list...',
                    onMouseDown: function(elm, k, i) {
                    },
                    onMouseUp: function(elm, k, i) {
                    },
                    onMouseOver: function(elm, d, i) {
                    },
                    childrenItems: outputChildrenitemsList
                }
                ]
                
                
                //menu --> functions for right click options for other tools
                var toolMenuSpecial = [
                {
                    title: 'Competence information',
                    onMouseDown: function(elm, k, i) {
                        editDCMetadata(data,currentGraph,k)
                        //sendDCMetadata(dc_metadata);
                    },
                    onMouseUp: function(elm, k, i) {
                    },
                    onMouseOver: function(elm, d, i) {
                    },
                    childrenItems: []
                },
                {
                    title: 'Change competence position in the graph',
                    onMouseDown: function(elm, k, i) {
                        changeNodePosition(k);
                    },
                    onMouseUp: function(elm, k, i) {
                    },
                    onMouseOver: function(elm, d, i) {
                    },
                    childrenItems: []
                },
                {
                    title: 'Delete competence',
                    onMouseDown: function(elm, k, i) {
                        deleteNode(k);
                    },
                    onMouseUp: function(elm, k, i) {
                    },
                    onMouseOver: function(elm, d, i) {
                    },
                    childrenItems: []
                }]
                var toolMenu = toolMenuCoor.concat(toolMenuSpecial)
                //################################################################################################//
                
                
				
                
                //################################################################################################//
                //aigner: Get node order of current graph nodes
                var nodeOrder="";
                for (var j=1; j<currentGraph.xdsm.nodes.length;j++)
                {

                    if (j!=1)
                        nodeOrder += ",";
                    nodeOrder += currentGraph.xdsm.nodes[j].uID;
                }
                                
                //aigner: theInputOptions -> array of all nodes in the graph, which can then be selected in the fpg manipulations
                var theInputOptions = [];
                for (var j=1; j<currentGraph.xdsm.nodes.length;j++)
                {
                    //aigner: value: j-1 because in KADMOS the coordinator is not in the node list, therefore all other competences' indices are decreased by 1!
                    theInputOptions.push({text: currentGraph.xdsm.nodes[j].name, value: currentGraph.xdsm.nodes[j].uID});
                }
                
                //aigner MDAO architecture options
                var MDAO_architectures = [{text:'-',value:'-'},
                                          {text:'Unconverged MDA',value:'unconverged-MDA'},
                                          {text:'Converged MDA',value:'converged-MDA'},
                                          {text:'Multidisciplinary Feasible (MDF)',value:'MDF'},
                                          {text:'Individual Discipline Feasible (IDF)',value:'IDF'},
                                          {text:'Unconverged Optimization',value:'unconverged-OPT'},
                                          {text:'Unconverged Design Of Experiments (DOE)',value:'unconverged-DOE'},
                                          {text:'Converged Design Of Experiments (DOE)',value:'converged-DOE'},
                                          {text:'Collaborative Optimization',value:'CO'},
                                          {text:'BLISS-2000',value:'BLISS-2000'}]
                var coupling_decompositions = ['-','Gauss-Seidel','Jacobi']
                var DOE_methods = ['-',
                                   'Full factorial design',
                                   'Latin hypercube design',
                                   'Monte Carlo design',
                                   'Custom design table',
                                   'Uniform design',
                                   'Box-Behnken design']
                var DOE_settings = []
                var coupled_functions_groups = [];
                //################################################################################################//
                
                
                var quickMenuDiv = d3.select(".xdsmDiv").append("div").attr("class","quickMenuDiv")
                    .style("top","-10px")
                    .style("margin-left","10pt")
                    
                
                var changeViewButton = quickMenuDiv.append("button")
                    .attr("class","btn btn-info")
                    .attr("data-toggle","tooltip")
                    .attr("data-placement","top")
                    .attr("title","Change visualization type")
                    .style("margin-left","10px")
                    .style("margin-bottom","10px")
                changeViewButton.append("span")
                    .attr("class","glyphicon glyphicon-random")
                    .attr("aria-hidden","true")
                changeViewButton.on("mousedown", function()
                {
                    bootbox.hideAll();
                 
                    bootbox.hideAll();
                    bootbox.prompt({
                        closeButton: false,
                        title: "Change visualization type",
                        inputType: 'select',
                        value: '',
                        inputOptions: [
                            {
                                text: 'Please choose...',
                                value: '',
                            },
                            {
                                text: 'XDSM view',
                                value: 'xdsm',
                            },
                            {
                                text: 'Edge Bundles view',
                                value: 'edgeBundles',
                            },
                            {
                                text: 'Sankey Diagram',
                                value: 'sankeyDiagram',
                            }
                        ],
                        callback: function (theView) {                            
                            if (theView == "xdsm")
                            {
                                clearView();
                                xdsm_script(data,currentGraph.id)
                            }
                            else if (theView == "edgeBundles")
                            {
                                clearView();
                                edgeBundles_script(data,currentGraph.id)
                            }
                            else if (theView == "sankeyDiagram")
                            {
                                clearView();
                                sankeyDiagram_script(data,currentGraph.id)
                            }
                            else
                            {}
                        }
                    });
                })                
                
                
                //####################################################################################################################
                //aigner: KADMOS function Save graph
                //####################################################################################################################
                // Function to download data to a file
                function download(filename, text) {
                    var element = document.createElement('a');
                    element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
                    element.setAttribute('download', filename);

                    element.style.display = 'none';
                    document.body.appendChild(element);

                    element.click();

                    document.body.removeChild(element);
                }
                
                
                //aigner: Export changes --> Download as CMDOWS file
                function exportChangesToFile(fileType)
                {                
                    bootbox.hideAll();
                    var bootboxContent = {title: "Save graph", message: '<p>Please be patient...</p>'};
                    var xhr = $.ajax(
                    {
                        type: 'POST',
                        data: {'fileNames_str':JSON.stringify([currentGraph.name]), 'fileType': fileType, 'graphIDs_str': JSON.stringify([currentGraph.id]),
                            'sessionId': sessionId},
                        url: '/kadmos_export_graphs',
                        success: function(result)
                        {                
                            var files = result
                            files.forEach(function(file)
                            {
                                download_from_kadmos(file,"/kadmos_download/"+file+"/"+sessionId)
                            })
                            
                            if (result.includes("ERROR:"))
                            {
                                bootboxContent.message = result
                                kadmosErrorMessage(bootboxContent);
                            }
                            else
                            {
                                bootboxContent.message = "Successfully created the files. Ready for download."
                                bootbox.hideAll();
                                bootbox.confirm(
                                {
                                    closeButton: false,
                                    title: bootboxContent.title,
                                    message: bootboxContent.message,
                                    callback: function(){
                                        remove_obsolete_files()
                                    }
                                });
                            }
                        },
                        error: function(result)
                        {
                            bootboxContent.message = result
                            kadmosErrorMessage(bootboxContent);
                        }
                    });
                    kadmosHavePatience(xhr, bootboxContent)
                }
                    
              
                
				var saveButton = quickMenuDiv.append("button")
                    .attr("class","btn btn-primary")
                    .attr("data-toggle","tooltip")
                    .attr("data-placement","top")
                    .attr("title","Save graph")
                    .style("margin-left","10px")
                    .style("margin-bottom","10px")
                saveButton.append("span")
                    .attr("class","glyphicon glyphicon-floppy-disk")
                    .attr("aria-hidden","true")
                saveButton.on("mousedown", function()
                {	
                    bootbox.hideAll();
                    
                    bootbox.prompt(
                    {
                        closeButton: false,
                        title: "Save graph. What would you like to save the graph as?",
                        inputType: 'select',
                        inputOptions: [ {text:" Save as new graph",value:"VISTOMS"},
                                        {text:"Save as KDMS file (download)",value:"kdms"},
                                        {text:"Save as CMDOWS file (download)",value:"cmdows"}],
                        value: "VISTOMS",
                        callback: function (fileType)
                        {   
                            if (fileType)
                            {
                                if (fileType.includes("VISTOMS"))
                                {
                                    bootbox.hideAll();
                                    var html = d3.select("html")
                                    var form_content = html.append("div").attr("class","form-content").attr("style","display:none")
                                    var form = form_content.append("form").attr("class","form").attr("role","form")
                                    var form_group, input;
                                        
                                    //Tool information
                                    form_group = form.append("div").attr("class","form-group")
                                    form_group.append("text").text("Name")
                                    input = form_group.append("input")
                                        .attr("id","graph_name")
                                        .attr("class","form-control")
                                        .attr("name","graph_name")
                                        .attr("placeholder","Add a graph name here...")
                                    //Tool information
                                    form_group = form.append("div").attr("class","form-group")
                                    form_group.append("text").text("Description")
                                    input = form_group.append("input")
                                        .attr("id","graph_description")
                                        .attr("class","form-control")
                                        .attr("name","graph_description")
                                        .attr("placeholder","Add a graph description here...")
                                    
                                    bootbox.dialog({
                                        closeButton: false,
                                        message: $(".form-content").html(),
                                        title: "<p>The graph will be saved in the MDO System Interface"
                                              +"<p>Please type in a new name and description for the graph</p>",
                                        size: "large",
                                        buttons: [
                                          {
                                            label: "Cancel",
                                            className: "btn btn-default pull-left",
                                            callback: function() {
                                              d3.selectAll(".form-content").remove();
                                            }
                                          },
                                          {
                                            label: "OK",
                                            className: "btn btn-primary pull-left",
                                            callback: function() {
                                                
                                                //get form data
                                                var graph_name = $('form #graph_name').val();
                                                var graph_description = $('form #graph_description').val();
                                                
                                                
                                                var bootboxContent = {title: "Save as new graph", message: '<p>Please be patient...</p>'};
                                                var newGraphID = ""
                                                if (data.graphs.length<100){newGraphID="0"+String(data.graphs.length+1)}
                                                else {newGraphID=String(data.graphs.length+1)}
                                                
                                                var newGraphID = '01';
                                                data.graphs.forEach(function(graph) 
                                                {
                                                    id_int = parseInt(graph.id)
                                                    if (data.graphs.length < 100){newGraphID = "0" + String(id_int+1);}
                                                    else{newGraphID = String(id_int+1);}
                                                })
                                                
                                                var xhr = $.ajax(
                                                {
                                                    type: 'POST',
                                                    data: {'graphID': graphID, 'newGraphID': newGraphID, 'newGraphName': graph_name, 'newGraphDesc': graph_description, 'currentOrder':nodeOrder, 'sessionId': sessionId},
                                                    url: '/kadmos_save_vistoms_graph',
                                                    success: function(result)
                                                    {
                                                        if (result.includes("ERROR:"))
                                                        {
                                                            bootboxContent.message = result
                                                            kadmosErrorMessage(bootboxContent);
                                                        }
                                                        else
                                                        {
                                                            var updatedData = jQuery.extend(true, {}, data);
                                                            var graphData = JSON.parse(result);
                                                            
                                                            for (var i = 0; i < data.graphs.length; i++)
                                                            {
                                                                if (graphID == data.graphs[i].id)
                                                                {
                                                                    //Insert copied graph behind the original one
                                                                    updatedData.graphs.splice(i+1, 0, graphData.graphs[0])
                                                                }
                                                            }
                                                            
                                                            
                                                            
                                                            clearView();
                                                            makeKadmosMenu(updatedData);
                                                            xdsm_script(updatedData,newGraphID);
                                                            
                                                            bootboxContent.message = "Success!"
                                                            kadmosSuccessMessage(bootboxContent)
                                                        }
                                                    },
                                                    error: function(result)
                                                    {
                                                        bootboxContent.message = result
                                                        kadmosErrorMessage(bootboxContent);
                                                    }
                                                });
                                                kadmosHavePatience(xhr, bootboxContent)
                                                d3.selectAll(".form-content").remove();
                                            }
                                          }
                                        ]
                                    });
                                }
                                else
                                {
                                    exportChangesToFile(fileType)
                                }
                            }
                        }
                    })
                })
                //####################################################################################################################
                
                
                
                
                var deleteButton = quickMenuDiv.append("button")
                    .attr("class","btn btn-danger")
                    .attr("data-toggle","tooltip")
                    .attr("data-placement","top")
                    .attr("title","Delete graph")
                    .style("margin-left","10px")
                    .style("margin-bottom","10px")
                deleteButton.append("span")
                    .attr("class","glyphicon glyphicon-trash")
                    .attr("aria-hidden","true")
                deleteButton.on("mousedown", function()
                {
					delete_graphs([graphID]);
                })
                
                
                var backButton = quickMenuDiv.append("button")
                    .attr("class","btn btn-warning")
                    .attr("data-toggle","tooltip")
                    .attr("data-placement","top")
                    .attr("title","Revert graph manipulation step")
                    .style("margin-left","10px")
                    .style("margin-bottom","10px")
                backButton.append("span")
                    .attr("class","glyphicon glyphicon-arrow-left")
                    .attr("aria-hidden","true")
                backButton.on("mousedown", function()
                {
                    var bootboxContent = {title: "Revert graph manipulation step", message: '<p>Please be patient...</p>'};
                    
                    bootbox.hideAll();
                    bootbox.confirm("Are you sure you want to revert the last step?", function(sure)
                    {
                        if (sure)
                        {   
                            
                            var xhr = $.ajax(
                            {
                                type: 'POST',
                                url: '/kadmos_revert_step',
                                data: {'graphID':graphID, 'sessionId': sessionId},
                                success: function(result)
                                {
                                    if (result.includes("ERROR:"))
                                    {
                                        bootboxContent.message = result
                                        kadmosErrorMessage(bootboxContent);
                                    }
                                    else
                                    {
                                        var updatedData = {};
                                        updatedData = data;
                                        var graphData = JSON.parse(result);             
                                        for (var i = 0; i < updatedData.graphs.length; i++)
                                        {
                                            if (graphID == updatedData.graphs[i].id)
                                            {
                                                updatedData.graphs[i] = graphData.graphs[0];   
                                            }
                                        }
                                        
                                        clearView();
                                        makeKadmosMenu(updatedData);
                                        xdsm_script(updatedData,graphID);
                                        
                                        bootboxContent.message = "Success!"
                                        kadmosSuccessMessage(bootboxContent)
                                    }
                                },
                                error: function(result)
                                {
                                    bootboxContent.message = result
                                    kadmosErrorMessage(bootboxContent);
                                }
                            });
                            kadmosHavePatience(xhr, bootboxContent)
                        }
                    })
                })
                    
                 var fwdButton = quickMenuDiv.append("button")
                    .attr("class","btn btn-warning")
                    .attr("data-toggle","tooltip")
                    .attr("data-placement","top")
                    .attr("title","Restore graph manipulation step")
                    .style("margin-left","10px")
                    .style("margin-bottom","10px")
                fwdButton.append("span")
                    .attr("class","glyphicon glyphicon-arrow-right")
                    .attr("aria-hidden","true")
                fwdButton.on("mousedown", function()
                {
                    var bootboxContent = {title: "Restore graph manipulation step", message: '<p>Please be patient...</p>'};
                    
                    bootbox.hideAll();
                    bootbox.confirm("Are you sure you want to restore the last step?", function(sure)
                    {
                        if (sure)
                        {   
                            var xhr = $.ajax(
                            {
                                type: 'POST',
                                url: '/kadmos_revert_step',
                                data: {'graphID':graphID},
                                success: function(result)
                                {
                                    if (result.includes("ERROR:"))
                                    {
                                        bootboxContent.message = result
                                        kadmosErrorMessage(bootboxContent);
                                    }
                                    else
                                    {
                                        var updatedData = {};
                                        updatedData = data;
                                        var graphData = JSON.parse(result);             
                                        for (var i = 0; i < updatedData.graphs.length; i++)
                                        {
                                            if (graphID == updatedData.graphs[i].id)
                                            {
                                                updatedData.graphs[i] = graphData.graphs[0];   
                                            }
                                        }
                                        
                                        clearView();
                                        makeKadmosMenu(updatedData);
                                        xdsm_script(updatedData,graphID);
                                        
                                        bootboxContent.message = "Success!"
                                        kadmosSuccessMessage(bootboxContent)
                                    }
                                },
                                error: function(result)
                                {
                                    bootboxContent.message = result
                                    kadmosErrorMessage(bootboxContent);
                                }
                            });
                            kadmosHavePatience(xhr, bootboxContent)
                        }
                    })
                })
                
                
                function addDesignCompetence(theData, theCurrentGraph=null)
                {               
                    bootbox.hideAll();
                    bootbox.dialog({
                        closeButton: false,
                        title: "Add design competence",
                        message: "<form id='inp_outp_form' method='post' enctype='multipart/form-data'>" 
                                +"<label>"
                                +"<text>Competence name</text>"
                                +"<br>"
                                +"<input name='function_node' id='function_node' placeholder='Add a tool name here...'></input>"
                                +"<br>"
                                +"<br>"
                                +"<text>Upload input file:</text>"
                                +"<input type='file' name='input_xml' />"
                                +"<br>"
                                +"<text>Upload output file:</text>"
                                +"<input type='file' name='output_xml' />"
                                +"<br>"
                                +"<input type='submit' value='Submit'/>"
                                +"</label></form>",
                        buttons : 
                        { 
                            cancel: {
                                label: "Cancel",
                                className: 'btn-danger'
                            }
                        }                                
                    });
                    
                    $('#inp_outp_form').on('submit',function(event){
                        event.preventDefault();
                        var formData = new FormData($('#inp_outp_form')[0]);
                        formData.append('graphID', graphID);
                        formData.append('currentOrder',nodeOrder);
                        formData.append('function_node', $('form #function_node').val());
                        formData.append('sessionId', sessionId);
                       
                        var bootboxContent = {title: "Add design competence", message: '<p>Please be patient...</p>'};
                        var xhr = $.ajax({
                            type: 'POST',
                            url: '/kadmos_add_design_competence',
                            data: formData,
                            processData: false,
                            contentType: false,
                            success: function(result)
                            {
                                if (result.includes("ERROR:"))
                                {
                                    bootboxContent.message = result
                                    kadmosErrorMessage(bootboxContent);
                                }
                                else
                                {
                                    var updatedData = {};
                                    updatedData = theData;
                                    var graphData = JSON.parse(result);             
                                    for (var i = 0; i < updatedData.graphs.length; i++)
                                    {
                                        if (graphID == updatedData.graphs[i].id)
                                        {
                                            updatedData.graphs[i] = graphData.graphs[0];   
                                        }
                                    }
                                    
                                    clearView();
                                    makeKadmosMenu(updatedData);
                                    xdsm_script(updatedData,graphID);
                                    
                                    bootboxContent.message = "Success!"
                                    kadmosSuccessMessage(bootboxContent)
                                }
                            },
                            error: function(result)
                            {
                                bootboxContent.message = result
                                kadmosErrorMessage(bootboxContent);
                            }
                        });
                        kadmosHavePatience(xhr, bootboxContent)
                        d3.selectAll(".form-content").remove();
                    });
                }
                
                //aigner: Function adds or edits a competence's metadata
                function editDCMetadata(theData, theCurrentGraph, aNode=null)
                {               
                    bootbox.hideAll();
                    
                    //aigner: Initialize the metadata element and it's sub components
                    var metadata;
                    if (aNode != null)
                    {
                        metadata = JSON.parse(JSON.stringify(aNode.metadata));
                    }
                    
                    //aigner: Initialize sub component "general_info"
                    if (typeof metadata.general_info == "undefined"
                        || metadata.general_info == null)
                    {
                        metadata.general_info = {owner:{},creator:{},operator:{}};
                        if (typeof metadata.general_info.description == "undefined"){metadata.general_info.description = ""}
                        if (typeof metadata.general_info.status == "undefined"){metadata.general_info.status = ""}
                        if (typeof metadata.general_info.owner.contactUID == "undefined"){metadata.general_info.owner.contactUID = ""}
                        if (typeof metadata.general_info.creator.contactUID == "undefined"){metadata.general_info.creator.contactUID = ""}
                        if (typeof metadata.general_info.operator.contactUID == "undefined"){metadata.general_info.operator.contactUID = ""}
                        
                    }
                    //aigner: Initialize sub component "performance_info"
                    if (typeof metadata.performance_info == "undefined"
                        || metadata.performance_info == null)
                    {
                        metadata.performance_info = {};
                        if (typeof metadata.performance_info.precision == "undefined"){metadata.performance_info.precision = ""}
                        if (typeof metadata.performance_info.fidelity_level == "undefined"){metadata.performance_info.fidelity_level = "-"}
                        if (typeof metadata.performance_info.run_time == "undefined"){metadata.performance_info.run_time = ""}
                        if (typeof metadata.performance_info.verification == "undefined"){metadata.performance_info.verification = ""}                        
                    }
                    //aigner: Initialize sub component "execution_info"
                    //This is somewhat more complicated, since it can be either a "local_component" or a "remote_component"
                    var local_component_info = [[[],[]]]
                    local_component_info[0][1] = {
                        command: "",
                        description: "",
                        hardware_requirements: "",
                        software_requirements: "",
                        integration_platform: ""}
                    var remote_component_info = {
                        data_exchange_settings: {folder:"", urlsite:""},
                        job_settings: {
                            job_name:"",
                            notification_message:"",
                            remote_engineer:{contact_u_i_d:""},
                            single_or_multi_execution:""
                        }
                    }
                    if (typeof metadata.execution_info == "undefined"
                        || metadata.execution_info == null)
                    {
                        metadata.execution_info = {}
                    }
                    //aigner: Set type of component to "none" initially
                    metadata.execution_info.component_type = "none";
                    if (typeof metadata.execution_info.remote_component_info == "undefined")
                    {
                        metadata.execution_info.remote_component_info = remote_component_info
                    }
                    else
                    {
                        metadata.execution_info.component_type = "remote";
                    }
                    if (typeof metadata.execution_info.local_component_info == "undefined")
                    {
                        metadata.execution_info.local_component_info = local_component_info
                    }
                    else
                    {
                        metadata.execution_info.component_type = "local";
                    }
                    //aigner: Initialize sub component "licensing"
                    if (typeof metadata.general_info.licensing == "undefined"
                        || metadata.general_info.licensing == null)
                    {
                        metadata.general_info.licensing = {};
                        if (typeof metadata.general_info.licensing.license_type == "undefined"){metadata.general_info.licensing.license_type = ""}
                        if (typeof metadata.general_info.licensing.license_specification == "undefined"){metadata.general_info.licensing.license_specification = ""}
                        if (typeof metadata.general_info.licensing.license_info == "undefined"){metadata.general_info.licensing.license_info = ""}
                    }
                    //aigner: Initialize sub component "sources"
                    if (typeof metadata.general_info.sources == "undefined"
                        || metadata.general_info.sources == null)
                    {
                        metadata.general_info.sources = {};
                        if (typeof metadata.general_info.sources.repository_link == "undefined"){metadata.general_info.sources.repository_link = ""}
                        if (typeof metadata.general_info.sources.download_link == "undefined"){metadata.general_info.sources.download_link = ""}
                        if (typeof metadata.general_info.sources.references == "undefined"){metadata.general_info.sources.references = ""}
                    }
                                       
                    //aigner. Now make the previously initialized infos visible in the html page with the help of a bootbox pop-up
                    var html = d3.select("html")
                    var form_content = html.append("div").attr("class","form-content").attr("style","display:none")
                    var form = form_content.append("form").attr("class","form").attr("role","form")
                    var form_group, input;
                        
                    //aigner: Visualize sub component "general_info"
                    form_group = form.append("div").attr("class","form-group")
                    form_group.append("label").text("General information")
                    form_group = form.append("div").attr("class","form-group")
                    form_group.append("text").text("Description")
                    var description = form_group.append("textarea")
                        .attr("id","description")
                        .attr("class","form-control")
                        .attr("name","description")
                        .attr("placeholder","Add a description for the DC here")
                    description.text(metadata.general_info.description)
                    <!-- form_group = form.append("div").attr("class","form-group") -->
                    <!-- form_group.append("text").text("Mode") -->
                    <!-- var mode = form_group.append("input") -->
                        <!-- .attr("id","mode") -->
                        <!-- .attr("class","form-control") -->
                        <!-- .attr("name","mode") -->
                        <!-- .attr("placeholder","Mode (e.g. main)") -->
                    <!-- if (typeof metadata.general_info != "undefined"  -->
                        <!-- && metadata.general_info !=null  -->
                        <!-- && typeof metadata.general_info.mode!="undefined") -->
                    <!-- { -->
                        <!-- mode.attr("value",metadata.general_info.mode) -->
                    <!-- } -->
                    <!-- form_group = form.append("div").attr("class","form-group") -->
                    <!-- form_group.append("text").text("Version") -->
                    <!-- var version = form_group.append("input") -->
                        <!-- .attr("id","version") -->
                        <!-- .attr("class","form-control") -->
                        <!-- .attr("name","version") -->
                        <!-- .attr("placeholder","Version (e.g. 1.0)") -->
                    <!-- if (typeof metadata.general_info != "undefined"  -->
                        <!-- && metadata.general_info !=null  -->
                        <!-- && typeof metadata.general_info.version!="undefined") -->
                    <!-- { -->
                        <!-- version.attr("value",metadata.general_info.version) -->
                    <!-- } -->
                    form_group = form.append("div").attr("class","form-group")
                    form_group.append("text").text("Status")
                    var input = form_group.append("select").attr("id","status").style("margin-left","5px")
                    var status = input.append("option")
                    status.attr("type","select").attr("value","-").text("Please select...")
                    status = input.append("option")
                    status.attr("type","select").attr("value","Available").text("Available")
                    if(metadata.general_info.status=="Available"){status.attr("selected","true")}
                    status = input.append("option")
                    status.attr("type","select").attr("value","N/A").text("N/A")
                    if(metadata.general_info.status=="N/A"){status.attr("selected","true")}
                    var contacts = []
                    if (theCurrentGraph.organization.contacts){contacts=theCurrentGraph.organization.contacts};
                    form_group = form.append("div").attr("class","form-group")
                    form_group.append("text").text("Owner")
                    input = form_group.append("select").attr("id","owner_uid").style("margin-left","5px")
                    input.append("option").attr("type","select").attr("value","-").text("Please select...")
                    contacts.forEach(function(contact){
                        var owner = input.append("option")
                        owner.attr("type","select").attr("value",contact.attrib.uID).text(contact.name + " (" + contact.company + ")")
                        if (contact.attrib.uID==metadata.general_info.owner.contactUID){owner.attr("selected","true")}
                    })
                    form_group = form.append("div").attr("class","form-group")
                    form_group.append("text").text("Creator")
                    input = form_group.append("select").attr("id","creator_uid").style("margin-left","5px")
                    input.append("option").attr("type","select").attr("value","-").text("Please select...")
                    contacts.forEach(function(contact){
                        var creator = input.append("option")
                        creator.attr("type","select").attr("value",contact.attrib.uID).text(contact.name + " (" + contact.company + ")")
                        if (contact.attrib.uID==metadata.general_info.creator.contactUID){creator.attr("selected","true")}
                    })
                    form_group = form.append("div").attr("class","form-group")
                    form_group.append("text").text("Operator")
                    input = form_group.append("select").attr("id","operator_uid").style("margin-left","5px")
                    input.append("option").attr("type","select").attr("value","-").text("Please select...")
                    contacts.forEach(function(contact){
                        var operator = input.append("option")
                        operator.attr("type","select").attr("value",contact.attrib.uID).text(contact.name + " (" + contact.company + ")")
                        if (contact.attrib.uID==metadata.general_info.operator.contactUID){operator.attr("selected","true")}
                    })
                    
                    //aigner: Visualize sub component "performance_info"
                    form_group = form.append("div").attr("class","form-group").style("margin-top","40px")
                    form_group.append("label").text("Performance information")
                    form_group = form.append("div").attr("class","form-group")
                    form_group.append("text").text("Precision")
                    input = form_group.append("input")
                        .attr("id","precision")
                        .attr("class","form-control")
                        .attr("name","precision")
                        .attr("placeholder","Precision (range 0 - 1)")
                        .attr("value",metadata.performance_info.precision)
                    form_group = form.append("div").attr("class","form-group")
                    form_group.append("text").text("Fidelity level")
                    input = form_group.append("select").attr("id","fidelity_level").style("margin-left","5px")
                    input.append("option").attr("type","select").attr("value","-").text("Please select...").attr("selected","true")
                    for (var count=0; count <= 3; count ++)
                    {
                        var fidelity = input.append("option")
                        fidelity.attr("type","select").attr("value",count).text(String(count))
                        if (count==metadata.performance_info.fidelity_level){fidelity.attr("selected","true")}
                    }
                    form_group = form.append("div").attr("class","form-group")
                    form_group.append("text").text("Run time")
                    input = form_group.append("input")
                        .attr("id","run_time")
                        .attr("class","form-control")
                        .attr("name","run_time")
                        .attr("placeholder","Run time in sec")
                        .attr("value",metadata.performance_info.run_time)
                    form_group = form.append("div").attr("class","form-group")
                    form_group.append("text").text("Verification")
                    input = form_group.append("input")
                        .attr("id","verification")
                        .attr("class","form-control")
                        .attr("name","verification")
                        .attr("placeholder","Verification (optional)")
                        .attr("value",metadata.performance_info.verification)
                        
                    //Information on tool licensing
                    form_group = form.append("div").attr("class","form-group").style("margin-top","40px")
                    form_group.append("label").text("License information (optional)")
                    form_group = form.append("div").attr("class","form-group")
                    form_group.append("text").text("License type")
                    input = form_group.append("input")
                        .attr("id","license_type")
                        .attr("class","form-control")
                        .attr("name","license_type")
                        .attr("placeholder","License type (e.g. open-source, commercial)")
                        .attr("value",metadata.general_info.licensing.license_type)
                    form_group = form.append("div").attr("class","form-group")
                    form_group.append("text").text("License specification")
                    input = form_group.append("input")
                        .attr("id","license_specification")
                        .attr("class","form-control")
                        .attr("name","license_specification")
                        .attr("placeholder","License specification (e.g. Apache License 2.0)")
                        .attr("value",metadata.general_info.licensing.license_specification)
                    form_group = form.append("div").attr("class","form-group")
                    form_group.append("text").text("License information")
                    input = form_group.append("input")
                        .attr("id","license_info")
                        .attr("class","form-control")
                        .attr("name","license_info")
                        .attr("placeholder","License information (e.g. https://www.apache.org/licenses/LICENSE-2.0)")
                        .attr("value",metadata.general_info.licensing.license_info)
                        
                    //Information on tool sources
                    form_group = form.append("div").attr("class","form-group").style("margin-top","40px")
                    form_group.append("label").text("Source information (optional)")
                    form_group = form.append("div").attr("class","form-group")
                    form_group.append("text").text("Repository link")
                    input = form_group.append("input")
                        .attr("id","repository_link")
                        .attr("class","form-control")
                        .attr("name","repository_link")
                        .attr("placeholder","Repository link")
                        .attr("value",metadata.general_info.sources.repository_link)
                    form_group = form.append("div").attr("class","form-group")
                    form_group.append("text").text("Download link")
                    input = form_group.append("input")
                        .attr("id","download_link")
                        .attr("class","form-control")
                        .attr("name","download_link")
                        .attr("placeholder","Download link")
                        .attr("value",metadata.general_info.sources.download_link)
                    form_group = form.append("div").attr("class","form-group")
                    form_group.append("text").text("References")
                    input = form_group.append("input")
                        .attr("id","references")
                        .attr("class","form-control")
                        .attr("name","references")
                        .attr("placeholder","References")
                        .attr("value",metadata.general_info.sources.references)
                    
                    //aigner: Visualize sub component "execution_info"
                    //Again, this is more complicated...
                    form_group = form.append("div").attr("class","form-group").style("margin-top","40px")
                    form_group.append("label").text("Execution information")
                    form_group = form.append("div").attr("class","form-group")
                    form_group.append("text").text("Execution type")
                    input = form_group.append("select")
                        .attr("id","execution_type")
                        .attr("class","execution_type")
                        .style("margin-left","5px")
                    var option = input.append("option").attr("value","-").text("Please select...").attr("selected","true")
                    var option_loc = input.append("option").attr("value","local_component").text("Local component")
                    var option_rem = input.append("option").attr("value","remote_component").text("Remote component")
                    if(metadata.execution_info.component_type=="local"){option_loc.attr("selected","true")}
                    else if(metadata.execution_info.component_type=="remote"){option_rem.attr("selected","true")}
                    
                    //Use a jquery function to toggle between local_component, remote_component, and none 
                    //depending on the user's dropdown selection of the "execution_type" element
                    $(function() {
                        var form = d3.selectAll(".form");
                        
                        //Information on remote component
                        var local_component_info = metadata.execution_info.local_component_info[metadata.execution_info.local_component_info.length-1][1];
                        form_group = form.append("div").attr("class","form-group_lc")
                        form_group.append("text").text("Operating System")
                        input = form_group.append("select").attr("id","operating_system").style("margin-left","5px")
                        var input_list = [{text:"Windows",value:"windows"},
                                          {text:"Linux",value:"linux"},
                                          {text:"Mac OS",value:"mac"},
                                          {text:"Other",value:"other"}];
                        input.append("option").attr("type","select").attr("value","-").text("Please select...")
                        input_list.forEach(function(theInput){
                            var operatingSystem = input.append("option")
                            operatingSystem.attr("type","select").attr("value",theInput.value).text(theInput.text)
                            if (theInput.value==local_component_info.operating_system)
                            {
                                operatingSystem.attr("selected","true")
                            }
                        })
                        form_group = form.append("div").attr("class","form-group_lc")
                        form_group.append("text").text("Integration platform")
                        input = form_group.append("input")
                            .attr("id","integration_platform")
                            .attr("class","form-control")
                            .attr("name","integration_platform")
                            .attr("placeholder","Integration platform (e.g. RCE, Optimus)")
                            .attr("value",local_component_info.integration_platform)
                        form_group = form.append("div").attr("class","form-group_lc")
                        form_group.append("text").text("Execution command")
                        input = form_group.append("textarea")
                            .attr("id","command")
                            .attr("class","form-control")
                            .attr("cols","40")
                            .attr("rows","5")
                            .attr("name","command")
                            .attr("placeholder","Execution command")
                            .text(local_component_info.command)
                        form_group = form.append("div").attr("class","form-group_lc")
                        form_group.append("text").text("Details")
                        input = form_group.append("textarea")
                            .attr("id","description_cmd")
                            .attr("class","form-control")
                            .attr("cols","40")
                            .attr("rows","1")
                            .attr("name","description_cmd")
                            .attr("placeholder","Details on the exectuion command (optional)")
                            .text(local_component_info.description)
                        form_group = form.append("div").attr("class","form-group_lc")
                        form_group.append("text").text("Software requirements")
                        input = form_group.append("textarea")
                            .attr("id","software_requirements")
                            .attr("class","form-control")
                            .attr("cols","40")
                            .attr("rows","1")
                            .attr("name","software_requirements")
                            .attr("placeholder","Software requirements")
                            .text(local_component_info.software_requirements)
                        form_group = form.append("div").attr("class","form-group_lc")
                        form_group.append("text").text("Hardware requirements")
                        input = form_group.append("textarea")
                            .attr("id","hardware_requirements")
                            .attr("class","form-control")
                            .attr("cols","40")
                            .attr("rows","1")
                            .attr("name","hardware_requirements")
                            .attr("placeholder","Hardware requirements")
                            .text(local_component_info.hardware_requirements)
                            
                        //Information on remote component
                        var remote_component_info = metadata.execution_info.remote_component_info;
                        form_group = form.append("div").attr("class","form-group_rc").style("margin-top","40px")
                        form_group.append("label").text("Remote component information")
                        form_group = form.append("div").attr("class","form-group_rc")
                        form_group.append("text").text("Remote engineer")
                        input = form_group.append("select").attr("id","remote_engineer_uid").style("margin-left","5px")
                        input.append("option").attr("type","select").attr("value","-").text("Please select...")
                        contacts.forEach(function(contact){
                            var owner = input.append("option")
                            owner.attr("type","select").attr("value",contact.attrib.uID).text(contact.name + " (" + contact.company + ")")
                            if (contact.attrib.uID==remote_component_info.job_settings.remote_engineer.contact_u_i_d){owner.attr("selected","true")}
                        })
                        form_group = form.append("div").attr("class","form-group_rc")
                        form_group.append("text").text("URL site")
                        input = form_group.append("input")
                            .attr("id","urlsite")
                            .attr("class","form-control")
                            .attr("name","urlsite")
                            .attr("placeholder","URL site")
                            .attr("value",remote_component_info.data_exchange_settings.urlsite)
                        form_group = form.append("div").attr("class","form-group_rc")
                        form_group.append("text").text("Folder")
                        input = form_group.append("input")
                            .attr("id","folder")
                            .attr("class","form-control")
                            .attr("name","folder")
                            .attr("placeholder","Folder")
                            .attr("value",remote_component_info.data_exchange_settings.folder)
                        form_group = form.append("div").attr("class","form-group_rc")
                        form_group.append("text").text("Job name")
                        input = form_group.append("input")
                            .attr("id","job_name")
                            .attr("class","form-control")
                            .attr("name","job_name")
                            .attr("placeholder","Job name")
                            .attr("value",remote_component_info.job_settings.job_name)
                        form_group = form.append("div").attr("class","form-group_rc")
                        form_group.append("text").text("Single or multi execution")
                        input = form_group.append("select").attr("id","single_or_multi_execution").style("margin-left","5px")
                        input.append("option").attr("type","select").attr("value","-").text("Please select...").attr("selected","true")
                        var single_type = input.append("option").attr("type","select").attr("value","single").text("Single")
                        var multi_type = input.append("option").attr("type","select").attr("value","multi").text("Multi")
                        if (metadata.execution_info.remote_component_info.job_settings.single_or_multi_execution=="single")
                        {
                            single_type.attr("selected","true")
                        }
                        else if (metadata.execution_info.remote_component_info.job_settings.single_or_multi_execution=="multi")
                        {
                            multi_type.attr("selected","true")
                        }
                        form_group = form.append("div").attr("class","form-group_rc")
                        form_group.append("text").text("Notification message")
                        input = form_group.append("textarea")
                            .attr("id","notification_message")
                            .attr("class","form-control")
                            .attr("cols","40")
                            .attr("rows","1")
                            .attr("name","notification_message")
                            .attr("placeholder","Notification message")
                            .text(remote_component_info.job_settings.notification_message)
                            
                        //Which of the above visualized block should actually be shown?
                        //--> The one that the component actually is, namely either a "local_component" or a "remote"
                        d3.selectAll(".form-group_rc").attr("style","display:none")
                        d3.selectAll(".form-group_lc").attr("style","display:none")
                        if      (metadata.execution_info.component_type=="local"){d3.selectAll(".form-group_lc").attr("style","display:block")}
                        else if (metadata.execution_info.component_type=="remote"){d3.selectAll(".form-group_rc").attr("style","display:block")}
                        
                        //aigner: Function switches between remote and local component depending on user input
                        $('.execution_type').on('change', function(e)
                        {
                            var execution_type = $('form #execution_type').val();
                            if (execution_type == "local_component")
                            {
                                metadata.execution_info.component_type = "local";
                                d3.selectAll(".form-group_rc").attr("style","display:none")
                                d3.selectAll(".form-group_lc").attr("style","display:block")
                            }
                            else if (execution_type == "remote_component")
                            {
                                metadata.execution_info.component_type = "remote";
                                d3.selectAll(".form-group_rc").attr("style","display:block")
                                d3.selectAll(".form-group_lc").attr("style","display:none")
                            }
                            else
                            {
                                metadata.execution_info.component_type = "none";
                                d3.selectAll(".form-group_rc").attr("style","display:none")
                                d3.selectAll(".form-group_lc").attr("style","display:none")
                            }
                        });
                    });
                    
                    

                    
                    var modal = bootbox.dialog({
                        closeButton: false,
                        message: $(".form-content").html(),
                        title: "Competence Metadata: " + aNode.name,
                        size: "large",
                        buttons: [
                          {
                            label: "Cancel",
                            className: "btn btn-default",
                            callback: function() {
                              modal.modal("hide");
                              d3.selectAll(".form-content").remove();
                            }
                          },
                          {
                            label: "OK",
                            className: "btn btn-primary",
                            callback: function() {               
                                
                                var metadata_to_kadmos = {
                                    general_info: {
                                        description: $('form #description').val(),
                                        mode: $('form #mode').val(),
                                        version: $('form #version').val(),
                                        status: $('form #status').val(),
                                        owner_uid: $('form #owner_uid').val(),
                                        creator_uid: $('form #creator_uid').val(),
                                        operator_uid: $('form #operator_uid').val()
                                    },
                                    performance_info: {
                                        precision: $('form #precision').val(),
                                        fidelity_level: $('form #fidelity_level').val(),
                                        run_time: $('form #run_time').val(),
                                        verification: $('form #verification').val()
                                    },
                                    execution_details: {
                                        local_component:false,
                                        operating_system:"",
                                        integration_platform: "",
                                        command: "",
                                        description: "",
                                        software_requirements: "",
                                        hardware_requirements: ""
                                    },
                                    remote_component_info: {
                                        remote_component: false,
                                        remote_engineer_uid: "",
                                        single_or_multi_execution: "",
                                        job_name: "",
                                        urlsite: "",
                                        folder: ""
                                    },
                                    licensing: {
                                        license_type: $('form #license_type').val(),
                                        license_specification: $('form #license_specification').val(),
                                        license_info: $('form #license_info').val()
                                    },
                                    sources: {
                                        repository_link: $('form #repository_link').val(),
                                        download_link: $('form #download_link').val(),
                                        references: $('form #references').val()
                                    }
                                }
                                
                                if (metadata.execution_info.component_type=="local")
                                {
                                    metadata_to_kadmos.execution_details = {
                                        local_component: true,
                                        operating_system: $('form #operating_system').val(),
                                        integration_platform: $('form #integration_platform').val(),
                                        command: $('form #command').val(),
                                        description: $('form #description_cmd').val(),
                                        software_requirements: $('form #software_requirements').val(),
                                        hardware_requirements: $('form #hardware_requirements').val()
                                    }
                                }
                                else if (metadata.execution_info.component_type=="remote")
                                {
                                    metadata_to_kadmos.remote_component_info = {
                                        remote_component: true,
                                        remote_engineer_uid: $('form #remote_engineer_uid').val(),
                                        single_or_multi_execution: $('form #single_or_multi_execution').val(),
                                        job_name: $('form #job_name').val(),
                                        urlsite: $('form #urlsite').val(),
                                        folder: $('form #folder').val(),
                                        notification_message: $('form #notification_message').val()
                                    }
                                }
                                                                
                                var metadata_str = JSON.stringify(metadata_to_kadmos)
                                var bootboxContent = {title: "Get graph elements", message: '<p>Please be patient...</p>'};
                                var xhr = $.ajax(
                                {
                                    type: 'POST',
                                    url: '/kadmos_add_DC_metadata',
                                    data: {'graphID':graphID, 'currentOrder':nodeOrder, 'nodeName':aNode.uID, 'metadata_str':metadata_str, 'sessionId': sessionId},
                                    success: function(result)
                                    {
                                        if (result.includes("ERROR:"))
                                        {
                                            bootboxContent.message = result
                                            kadmosErrorMessage(bootboxContent);
                                        }
                                        else
                                        {   
                                            var updatedData = {};
                                            updatedData = theData;
                                            var graphData = JSON.parse(result);             
                                            for (var i = 0; i < updatedData.graphs.length; i++)
                                            {
                                                if (graphID == updatedData.graphs[i].id)
                                                {
                                                    updatedData.graphs[i] = graphData.graphs[0];   
                                                }
                                            }
                                            
                                            clearView();
                                            makeKadmosMenu(updatedData);
                                            xdsm_script(updatedData,graphID);
                                            
                                            bootboxContent.message = "Success!"
                                            kadmosSuccessMessage(bootboxContent)
                                        }
                                    },
                                    error: function(result)
                                    {
                                        bootboxContent.message = result
                                        kadmosErrorMessage(bootboxContent);
                                    }
                                });
                                kadmosHavePatience(xhr, bootboxContent)
                                modal.modal("hide");
                                d3.selectAll(".form-content").remove();
                            }
                          }
                        ],
                        onEscape: function() {
                          modal.modal("hide");
                          d3.selectAll(".form-content").remove();
                        }
                    });
                }      
                
                
                var addCBButton = quickMenuDiv.append("button")
                    .attr("class","btn btn-success")
                    .attr("data-toggle","tooltip")
                    .attr("data-placement","top")
                    .attr("title","Add competence block")
                    .style("margin-left","10px")
                    .style("margin-bottom","10px")
                addCBButton.append("span")
                    .attr("class","glyphicon glyphicon-plus")
                    .attr("aria-hidden","true")
                addCBButton.on("mousedown", function()
                {
                    var bootboxContent = {title: "Add competence block", message: '<p>Please be patient...</p>'};
                    
                    bootbox.hideAll();
                    bootbox.prompt({
                        closeButton: false,
                        title: "<b>Add competence block</b>"
                              +"<p>Which type of competence block do you want to add?</p>",
                        inputType: 'select',
                        inputOptions: [
                            {
                                text: 'Please choose...',
                                value: '',
                            },
                            {
                                text: 'Design competence',
                                value: 'dc',
                            },
                            {
                                text: 'Mathematical function',
                                value: 'mf',
                            }
                        ],
                        callback: function (result) {
                            if (result == 'mf')
                            {
                                addMathematicalFunction();
                            }
                            else if(result == "dc")
                            {
                                addDesignCompetence(data, currentGraph);
                            }
                            else;
                        }
                    });
                })
                
                //aigner: View and edit contact information
                //#####################################################################################################
                function contact_info(contact_uid, theContacts)
                {
                    if (contact_uid=="")
                    {
                        var empty_contact = {name:"",function:"",country:"",company:"",telephone:"",address:"",department:"",email:"",attrib:{uID:""}}
                        theContacts.push(empty_contact)
                    }
                    theContacts.forEach(function(contact, index, contacts)
                    {
                        if (contact.attrib.uID == contact_uid)
                        {
                            if (typeof currentGraph.organization.organigram=="undefined")
                            {
                                currentGraph.organization.organigram = {architects: [], integrators: [], toolSpecialists: []};
                            }
                            if (typeof currentGraph.organization.contacts=="undefined")
                            {
                                currentGraph.organization.contacts = [];
                            }
							if (typeof currentGraph.organization.organigram.architects=="undefined")
                            {
                                currentGraph.organization.organigram.architects = [];
                            }
                            if (typeof currentGraph.organization.organigram.integrators=="undefined")
                            {
                                currentGraph.organization.organigram.integrators = [];
                            }
                            if (typeof currentGraph.organization.organigram.toolSpecialists=="undefined")
                            {
                                currentGraph.organization.organigram.toolSpecialists = [];
                            }
                            
                            //Show contact info
                            //############################################################
                            bootbox.hideAll();
                            var html = d3.select("html")
                            var form_content = html.append("div").attr("class","form-content").attr("style","display:none")
                            var form = form_content.append("form").attr("class","form").attr("role","form")
                            var form_group, input;
                                
                            form_group = form.append("div").attr("class","form-group")        
                            function form_control(text,value)
                            {
                                form_group.append("label").text(text)
                                input = form_group.append("input")
                                    .attr("id","contact_"+text)
                                    .attr("class","form-control")
                                    .attr("name","contact_"+text)
                                    .attr("placeholder","...")
                                    .attr("value",value)
                            }
                            form_control("Name",contact.name)
                            form_control("uID",contact.attrib.uID)
                            form_control("Function",contact.function)
                            form_control("Country",contact.country)
                            form_control("Company",contact.company)
                            form_control("Telephone",contact.telephone)
                            form_control("Address",contact.address)
                            form_control("Department",contact.department)
                            form_control("E-Mail",contact.email)
                            
                            form_group.append("label").text("Roles")
                            var input = form_group.append("div").attr("id","contact_roles")
                            var label1 = input.append("div").append("text").style("margin-top","5pt").attr("for","architect").text("Architect ")
                            label1.append("input").attr("type","checkbox").attr("id","architect")
                            var label2 = input.append("div").append("text").style("margin-top","5pt").attr("for","integrator").text("Integrator ")
                            label2.append("input").attr("type","checkbox").attr("id","integrator")
                            var label3 = input.append("div").append("text").style("margin-top","5pt").attr("for","toolSpecialist").text("Tool Specialist ")
                            label3.append("input").attr("type","checkbox").attr("id","toolSpecialist")                       
                            currentGraph.organization.organigram.architects.forEach(function(architect)
                            {
                                if (contact.attrib.uID==architect.contactUID)
                                {
                                    $('#architect').attr('checked', true); 
                                }                                
                            });
                            currentGraph.organization.organigram.integrators.forEach(function(integrator)
                            {
                                if (contact.attrib.uID==integrator.contactUID)
                                {
                                    $('#integrator').attr('checked', true); 
                                }                                
                            });
                            currentGraph.organization.organigram.toolSpecialists.forEach(function(toolSpecialist)
                            {
                                if (contact.attrib.uID==toolSpecialist.contactUID)
                                {
                                    $('#toolSpecialist').attr('checked', true); 
                                }                                
                            });
                            //############################################################
                            
                            //Function to save contact information in global organization object
                            //############################################################
                            function update_contact_info()
                            {
                                //Save contact information in global contacts object
                                currentGraph.organization.contacts = theContacts
                                //Send changes to kadmos and update the graph
                                var bootboxContent = {title: "Update contact information", message: '<p>Please be patient...</p>'};
                                var xhr = $.ajax({
                                    type: 'POST',
                                    data: {'graphID': graphID, 'currentOrder': nodeOrder, 'organization': JSON.stringify(currentGraph.organization), 'sessionId': sessionId},
                                    url: '/kadmos_edit_contact_infos',
                                    success: function(result)
                                    {
                                        if (result.includes("ERROR:"))
                                        {
                                            bootboxContent.message = result
                                            kadmosErrorMessage(bootboxContent);
                                        }
                                        else
                                        {
                                            var updatedData = {};
                                            updatedData = data;
                                            var graphData = JSON.parse(result);             
                                            for (var i = 0; i < updatedData.graphs.length; i++)
                                            {
                                                if (graphID == updatedData.graphs[i].id)
                                                {
                                                    updatedData.graphs[i] = graphData.graphs[0];   
                                                }
                                            }
                                            
                                            clearView();
                                            makeKadmosMenu(updatedData);
                                                                                            
                                            xdsm_script(updatedData,graphID);
                                            
                                            bootboxContent.message = "Success!"
                                            kadmosSuccessMessage(bootboxContent)
                                        }
                                    },
                                    error: function(result)
                                    {
                                        bootboxContent.message = result
                                        kadmosErrorMessage(bootboxContent);
                                    }
                                });
                                kadmosHavePatience(xhr, bootboxContent)
                            }
                            //############################################################
                            
                            
                            //############################################################
                            var modal = bootbox.dialog({
                            closeButton: false,
                            message: $(".form-content").html(),
                            title: "Contact information",
                            size: "large",
                            buttons: [
                              {
                                label: "Delete contact",
                                className: "btn btn-danger pull-left",
                                callback: function() {
                                    modal.modal("hide");
                                    d3.selectAll(".form-content").remove();
                                    bootbox.hideAll();
                                    bootbox.confirm("Are you sure you want to delete the contact?", function(sure)
                                    {
                                        if (sure)
                                        {                                
                                            //remove contact from roles in organigram
                                            currentGraph.organization.organigram.architects.forEach(function(architect, index, architects)
                                            {
                                                if (contact.attrib.uID==architect.contactUID){architects.splice(index,1)}                                
                                            });
                                            currentGraph.organization.organigram.integrators.forEach(function(integrator, index, integrators)
                                            {
                                                if (contact.attrib.uID==integrator.contactUID){integrators.splice(index,1)}                                
                                            });
                                            currentGraph.organization.organigram.toolSpecialists.forEach(function(toolSpecialist, index, toolSpecialists)
                                            {
                                                if (contact.attrib.uID==toolSpecialist.contactUID){toolSpecialists.splice(index,1)}                                
                                            });
                                            
                                            //Delete contact from organization
                                            contacts.splice(index,1);
                                            
                                            //Call function update_contact_info
                                            update_contact_info();
                                        }
                                    })
                                }
                              },
                              {
                                label: "Apply",
                                className: "btn btn-primary pull-right",
                                callback: function() {
                                    contact.name = $('form #contact_Name').val()
                                    contact.attrib.uID = $('form #contact_uID').val()
                                    contact.function = $('form #contact_Function').val()
                                    contact.country = $('form #contact_Country').val()
                                    contact.company = $('form #contact_Company').val()
                                    contact.telephone = $('form #contact_Telephone').val()
                                    contact.address = $('form #contact_Address').val()
                                    contact.department = $('form #contact_Department').val()
                                    contact.email = $('form #contact_E-Mail').val()
                                    
                                    
                                    //aigner: toggle roles of the contact
                                    //############################################################
                                    var roles = {
                                        architect: document.getElementById("architect").checked,
                                        integrator: document.getElementById("integrator").checked,
                                        toolSpecialist: document.getElementById("toolSpecialist").checked
                                    }                                    
                                    var contact_is_arc = false;
                                    var contact_is_int = false;
                                    var contact_is_ts = false;
                                    currentGraph.organization.organigram.architects.forEach(function(architect, index, architects)
                                    {
                                        if (contact.attrib.uID==architect.contactUID)
                                        {
                                            contact_is_arc = true;
                                            if (!roles.architect){
                                                architects.splice(index,1)
                                            }
                                           
                                        }                                
                                    });
                                    currentGraph.organization.organigram.integrators.forEach(function(integrator, index, integrators)
                                    {
                                        if (contact.attrib.uID==integrator.contactUID)
                                        {
                                            contact_is_int = true;
                                            if (!roles.integrator){
                                                integrators.splice(index,1)
                                            }
                                        }                                
                                    });
                                    currentGraph.organization.organigram.toolSpecialists.forEach(function(toolSpecialist, index, toolSpecialists)
                                    {
                                        if (contact.attrib.uID==toolSpecialist.contactUID)
                                        {
                                            contact_is_ts = true;
                                            if (!roles.toolSpecialist){
                                                toolSpecialists.splice(index,1)
                                            }
                                        }                                
                                    });
                                    if (!contact_is_arc && roles.architect){currentGraph.organization.organigram.architects.push({contactUID: contact.attrib.uID})}
                                    if (!contact_is_int && roles.integrator){currentGraph.organization.organigram.integrators.push({contactUID: contact.attrib.uID})}
                                    if (!contact_is_ts && roles.toolSpecialist){currentGraph.organization.organigram.toolSpecialists.push({contactUID: contact.attrib.uID})}
                                    //############################################################
                                    
                                    //Call function update_contact_info
                                    update_contact_info();
                                    
                                    modal.modal("hide");
                                    d3.selectAll(".form-content").remove();
                                }
                              },
                              {
                                label: "Cancel",
                                className: "btn btn-default pull-right",
                                callback: function() {
                                  modal.modal("hide");
                                  d3.selectAll(".form-content").remove();
                                }
                              }
                            ],
                            onEscape: function() {
                              modal.modal("hide");
                              d3.selectAll(".form-content").remove();
                            }
                        });
                        //############################################################
                        }
                    })
                }
                
                var orgaButton = quickMenuDiv.append("button")
                    .attr("class","btn btn-info")
                    .attr("data-toggle","tooltip")
                    .attr("data-placement","top")
                    .attr("title","Contact options")
                    .style("margin-left","10px")
                    .style("margin-bottom","10px")
                orgaButton.append("span")
                    .attr("class","glyphicon glyphicon-user")
                    .attr("aria-hidden","true")
                orgaButton.on("mousedown", function()
                {
                    bootbox.hideAll();
                    
                    var contacts = []
                    if (currentGraph.organization.contacts){contacts=JSON.parse(JSON.stringify(currentGraph.organization.contacts))};
                    var options = [{text:"Select...",value:"select"},{text:"Add new contact",value:""}];
                    contacts.forEach(function(contact){
                        options.push({text:contact.name + " (" + contact.company + ")", value:contact.attrib.uID});
                    })     
                 
                    bootbox.hideAll();
                    bootbox.prompt({
                        closeButton: false,
                        title: "<b>Contact information</b>"
                              +"<p>Which contact do you want to show or edit?<br>"
                              +"You can also add a new contact. Just select 'Add new contact'</p>",
                        inputType: 'select',
                        value: 'select',
                        inputOptions: options,
                        callback: function (result) {
                            if (result==null || result=="select"){}
                            else
                            {
                                contact_info(result,contacts);
                            }
                        }
                    });
                    
                    
                })
                
                //#####################################################################################################
                    
                
                //####################################################################################################################               
                
				
				
				
				var graph_menu = "      <nav>"
                          +"          <ul>"
                          +"             <li><a href='#'>Graph inspection <span>&#x25BE;</span></a>"
                          +"                  <ul>"                        
                          +"                      <li class='search_element'><a href='#'>Find graph elements</a></li>"
                          +"                      <li><a href='#'>Inspect data model<span>&#x25B8;</span></a>"
                          +"                          <ul class='data_model_view'>"
                          +"                              <li class='tree_view'><a href='#'>Tree view<span>&#x25B8;</span></a></li>"
                          +"                              <li class='list_view'><a href='#'>List view<span>&#x25B8;</span></a></li>"
                          +"                          </ul>"
                          +"                      </li>"
                          +"                      <li><a href='#'>Check<span>&#x25B8;</span></a>"
                          +"                          <ul class='check_graph'>"
                          +"                              <li class='l1_check'><a href='#'>L1 check: graph</a></li>"
                          +"                              <li class='l2_check'><a href='#'>L2 check: problem formulation</a></li>"
                          +"                              <li class='l3_check'><a href='#'>L3 check: graph & problem formulation</a></li>"
                          +"                          </ul>"
                          +"                      </li>"
                          +"                  </ul>"
                          +"              </li>"
                          +"              <li><a href='#'>Graph manipulation <span>&#x25BE;</span></a>"
                          +"                  <ul>"
                          +"                      <li><a href='#'>FPG manipulation <span>&#x25B8;</span></a>"
                          +"                          <ul class='fpg_manipulations'>"
                          +"                              <li class='start_MDO_problem'><a href='#'>Copy graph for MDAO problem definition</a></li>"
                          +"                              <li class='get_function_order'><a href='#'>Get possible function order</a></li>"
                          +"                              <li class='make_variables_valid'><a href='#'>Make all variables valid</a></li>"
                          +"                              <li><a href='#'>Remove...<span>&#x25B8;</span></a>"
                          +"                                  <ul class='remove'>"
                          +"                                      <li class='remove_dcs'><a href='#'>design competences</a></li>"
                          +"                                      <li class='remove_collision_parameters'><a href='#'>collision parameters</a></li>"
                          +"                                      <li class='remove_unused_outputs'><a href='#'>unused outputs</a></li>"
                          +"                                  </ul>"
                          +"                              </li>"
                          +"                              <li><a href='#'>Merge...<span>&#x25B8;</span></a>"
                          +"                                  <ul class='merge'>"
                          +"                                      <li class='merge_seq_competences'><a href='#'>sequential competences</a></li>"
                          +"                                      <li class='merge_par_competences'><a href='#'>parallel competences</a></li>"
                          +"                                      <li class='merge_funcMod_competences'><a href='#'>function modes competences</a></li>"
                          +"                                  </ul>"
                          +"                             </li>"
                          +"                              <li><a href='#'>Assign...<span>&#x25B8;</span></a>"
                          +"                                  <ul class='assign'>"
                          +"                                      <li class='assign_parameter_roles'><a href='#'>parameter roles</a></li>"
                          +"                                      <li class='assign_probFunc_roles'><a href='#'>problem function roles</a></li>"
                          +"                                  </ul>"
                          +"                              </li>"
                          +"                          </ul>"
                          +"                      </li>"
                          +"                      <li><a href='#'>MDPG manipulation <span>&#x25B8;</span></a>"
                          +"                          <ul class='mdpg_manipulations'>"
                          +"                              <li class='start_MDO_architecture'><a href='#'>Copy graph for MDAO architecture definition</a></li>"
                          +"                              <li><a href='#'>Set...<span>&#x25B8;</span></a>"
                          +"                                  <ul class='set'>"
                          +"                                      <li class='set_MDAO_architecture'><a href='#'>MDAO architecture</a></li>"
                          +"                                      <li class='set_coupling_decomposition'><a href='#'>Coupling decomposition</a></li>"
                          +"                                  </ul>"
                          +"                              </li>"
                          +"                              <li class='impose_MDAO_architecture'><a href='#'>Impose MDAO architecture</a></li>"
                          +"                          </ul>"
                          +"                      </li>"
                          +"                      <li class='upload_script'><a href='#'>Upload custom script</a></li>"
                          +"                  </ul>"
                          +"              </li>"
                          +"          </ul>"
                          +"      </nav>"
                          
				d3.select(".xdsmDiv").append('div').attr("class","graph_menu").style("margin-left","20px").html(graph_menu);
				
					
                

                var inspectionsDiv = d3.select(".xdsmDiv").append("div").attr("class","quickMenuDiv")
                    .style("top","-10px")
                    .style("margin-left","10pt")
                //aigner: Search for graph elements
                //####################################################################################################################               
                //CATEGORIES
                 var categories = [
                    {text:"all",value:"all"},
                    {text:"variable",value:"variable"},
                    {text:"function",value:"function"}]
                
                
                var subCategories = [
                    {text:"all",value:"all"},
                    //VARIABLES:
                    {text:"hole",value:"hole"},
                    {text:"supplied input ",value:"supplied input "},
                    {text:"supplied shared input",value:"supplied shared input"},
                    {text:"output",value:"output"},
                    {text:"collision",value:"collision"},
                    {text:"coupling",value:"coupling"},
                    {text:"pure circular coupling",value:"pure circular coupling"},
                    {text:"shared coupling",value:"shared coupling"},
                    {text:"shared circular coupling",value:"shared circular coupling"},
                    {text:"collided coupling",value:"collided coupling"},
                    {text:"collided circular coupling",value:"collided circular coupling"},
                    {text:"collided shared coupling",value:"collided shared coupling"},
                    {text:"collided shared circular coupling",value:"collided shared circular coupling"},
                    {text:"all inputs",value:"all inputs"},
                    {text:"all outputs",value:"all outputs"},
                    {text:"all couplings",value:"all couplings"},
                    {text:"all circular variables",value:"all circular variables"},
                    {text:"all collisions",value:"all collisions"},
                    {text:"all splittable variables",value:"all splittable variables"},
                    {text:"all problematic variables",value:"all problematic variables"},
                    //FUNCTIONS:
                    {text:"hole",value:"hole"},
                    {text:"inputless",value:"inputless"},
                    {text:"outputless",value:"outputless"},
                    {text:"complete",value:"complete"},
                    {text:"all problematic functions",value:"all problematic functions"}]
                    
                var operators = [
                    {text:"Operator (please select)",value:"-"},
                    {text:"<",value:"<"},
                    {text:"<=",value:"<="},
                    {text:"==",value:"=="},
                    {text:">=",value:">="},
                    {text:">",value:">"}]
               
				var search_element_dom = d3.select(".search_element")
				search_element_dom.on("mousedown", function()
                {
                    bootbox.hideAll();
                    
                    var html = d3.select("html")
                    var form_content = html.append("div").attr("class","form-content").attr("style","display:none")
                    var form = form_content.append("form").attr("class","form").attr("role","form")
                    
                    var form_group1 = form.append("div").attr("class","form-group")
                    form_group1.append("label").attr("for","category").text("Category")
                    var input1 = form_group1.append("select").attr("id","category")
                    categories.forEach(function(category)
                        {
                            input1.append("option").attr("type","select").attr("value",category.value).text(category.text)
                        }
                    )
                    
                    var form_group2 = form.append("div").attr("class","form-group")
                    form_group2.append("label").attr("for","subcategory").text("Sub-category")
                    var input2 = form_group2.append("select").attr("id","sub-category")
                    subCategories.forEach(function(subcategory)
                        {
                            input2.append("option").attr("type","select").attr("value",subcategory.value).text(subcategory.text)
                        }
                    )
                    
                    
                    var maxNumOfAttributes = 3;
                    
                    for (var i=0; i<maxNumOfAttributes; i++)
                    {
                        var form_group3 = form.append("div").attr("class","form-group")
                        var input = form_group3.append("div").attr("class","attribute")
                        if (i==0){input.append("label").attr("for","attribute"+String(i)).text("Attribute conditions (max. "+String(maxNumOfAttributes)+")")}
                        input.append("input")
                            .attr("id","attribute"+String(i))
                            .attr("class","form-control")
                            .attr("name","attribute")
                            .attr("placeholder","attribute "+String(i+1))
                        input = form_group3.append("select").attr("id","operator"+String(i))
                        operators.forEach(function(operator)
                        {
                            input.append("option").attr("type","select").attr("value",operator.value).text(operator.text)
                        })

                        input = form_group3.append("div").attr("class","attribute")
                        input.append("input")
                            .attr("id","value"+String(i))
                            .attr("class","form-control")
                            .attr("name","value")
                            .attr("placeholder","value "+String(i+1))
                    }
                    
                    var form_group4 = form.append("div").attr("class","form-group")
                    for (var i=0; i<maxNumOfAttributes; i++)
                    {
                        var input = form_group4.append("div").attr("class","attribute")
                        if (i==0){input.append("label").attr("for","attribute"+String(i)).text("Included attributes (max. "+String(maxNumOfAttributes)+")")}
                        input.append("input")
                            .attr("id","attr_include"+String(i))
                            .attr("class","form-control")
                            .attr("name","attr_include")
                            .attr("placeholder","attribute "+String(i+1))
                        input.append("input")
                            .attr("id","attr_include_val"+String(i))
                            .attr("class","form-control")
                            .attr("name","attr_include_val "+String(i+1))
                            .attr("placeholder","value "+String(i+1))
                    }
                    
                    var form_group5 = form.append("div").attr("class","form-group")
                    for (var i=0; i<maxNumOfAttributes; i++)
                    {
                        var input = form_group5.append("div").attr("class","attribute")
                        if (i==0){input.append("label").attr("for","attribute"+String(i)).text("Excluded attributes (max. "+String(maxNumOfAttributes)+")")}
                        input.append("input")
                            .attr("id","attr_exclude"+String(i))
                            .attr("class","form-control")
                            .attr("name","attr_exclude")
                            .attr("placeholder","attribute "+String(i+1))
                        input.append("input")
                            .attr("id","attr_exclude_val"+String(i))
                            .attr("class","form-control")
                            .attr("name","attr_exclude_val")
                            .attr("placeholder","value "+String(i+1))
                    }
                        
                    var form_group8 = form.append("div").attr("class","form-group")
                    form_group8.append("label").attr("for","xPath_include").text("Included xPaths")
                    var input8 = form_group8.append("input")
                        .attr("id","xPath_include")
                        .attr("class","form-control")
                        .attr("name","xPath_include")
                        .attr("placeholder","/schema/parent/child1, /schema/parent/child2, ...")
                        
                    var form_group9 = form.append("div").attr("class","form-group")
                    form_group9.append("label").attr("for","xPath_exclude").text("Excluded xPaths")
                    var input9 = form_group9.append("input")
                        .attr("id","xPath_exclude")
                        .attr("class","form-control")
                        .attr("name","xPath_exclude")
                        .attr("placeholder","/schema/parent/child1, /schema/parent/child2, ...")
                        
                        
                    
                    
                    var modal = bootbox.dialog({
                        closeButton: false,
                        message: $(".form-content").html(),
                        title: "Search for graph elements.",
                        size: "large",
                        buttons: [
                          {
                            label: "Cancel",
                            className: "btn btn-default pull-left",
                            callback: function() {
                              modal.modal("hide");
                              d3.selectAll(".form-content").remove();
                            }
                          },
                          {
                            label: "OK",
                            className: "btn btn-primary pull-left",
                            callback: function() {
                                
                                var category = $('form #category').val()
                                var sub_category = $('form #sub-category').val()
                                var attr_cond = []
                                var attr_include = [] 
                                var attr_exclude = []
                                for (var i=0; i<maxNumOfAttributes; i++)
                                {   
                                    if ($('form #attribute'+String(i)).val()!=""&&$('form #operator'+String(i)).val()!=""&&$('form #value'+String(i)).val()!="")
                                    {attr_cond.push([$('form #attribute'+String(i)).val(),$('form #operator'+String(i)).val(),$('form #value'+String(i)).val()])}
                                    if ($('form #attr_include'+String(i)).val()!=""&&$('form #attr_include_val'+String(i)).val()!="")
                                    {attr_include.push([$('form #attr_include'+String(i)).val(),$('form #attr_include_val'+String(i)).val()])}
                                    if ($('form #attr_exclude'+String(i)).val()!=""&&$('form #attr_exclude_val'+String(i)).val()!="")
                                    {attr_exclude.push([$('form #attr_exclude'+String(i)).val(),$('form #attr_exclude_val'+String(i)).val()])}
                                }
                                var xPath_include = $('form #xPath_include').val()
                                var xPath_exclude = $('form #xPath_exclude').val()
                                
                                var bootboxContent = {title: "Get graph elements", message: '<p>Please be patient...</p>'};
                                var xhr = $.ajax(
                                {
                                    type: 'POST',
                                    url: '/kadmos_find_all_nodes',
                                    data: {'graphID':graphID, 
                                           'category':category,
                                           'sub_category':sub_category,
                                           'attr_cond':JSON.stringify(attr_cond),
                                           'attr_include':JSON.stringify(attr_include),
                                           'attr_exclude':JSON.stringify(attr_exclude),
                                           'xPath_include':xPath_include,
                                           'xPath_exclude':xPath_exclude,
                                           'sessionId': sessionId,},
                                    success: function(result)
                                    {
                                        if (result.includes("ERROR:"))
                                        {
                                            bootboxContent.message = result
                                            kadmosErrorMessage(bootboxContent);
                                        }
                                        else if (result == "[]")
                                        {   
                                            bootboxContent.title = "<b>Get graph elements</b>"
                                            bootboxContent.message = "<b>No matching graph elements!</b>"
                                            kadmosSuccessMessage(bootboxContent);
                                        }
                                        else
                                        {   
                                            bootbox.hideAll();
											var treeData = (JSON.parse(JSON.stringify(currentGraph.variableSchemes[varCategories[0].description])));
											var firstEl = treeData[0].xPath.split("/")[1]		

											var graphElements = JSON.parse(result)
                                            
                                            var title = "List view of graph elements."
                                                    +" category: \"" + category + "\""
                                                    +", sub-category: \"" + sub_category + "\""
                                            var message = "";
											
											var variables = [];
											var functions = [];
											
                                            //get all potential functions first. these will be 
                                            var functions_all = [];
                                            var alld3Nodes = d3.selectAll(".node")
                                            alld3Nodes.each(
                                                function(){
                                                   functions_all.push(this.__data__)
                                                }
                                            )
                                            
											if (result.includes("/"+firstEl))
                                            {
                                                //There are variables in the result
												
												
                                                //push back functions in list
                                                functions_all.forEach(function(aFunction){
                                                    if (graphElements.includes(aFunction.uID)){functions.push(aFunction);}
                                                })
                                                
                                                //push back variables in list
                                                var pipeData = [];
												graphElements.forEach(function(graphElement)
												{
													if (graphElement.includes("/"+firstEl))
													{ 
														pipeData += ","+graphElement;
													}
												})
												variables = JSON.parse(JSON.stringify(treeData))
												prune_tree(pipeData,variables)
												variables.forEach(function(variable)
												{
													variable.name = variable.xPath
													//work around because nodeMenu expexts the data, to have a "data" object inside
                                                    variable.data = variable
                                                    variable.data.name = variable.xPath.split("/")[variable.xPath.split("/").length-1]
												})
												
                                                //message with option: How does the user want to show the variables, as a list or as a tree view?
												bootbox.prompt(
                                                {
                                                    closeButton: false,
                                                    title: "The graph elements you requested contain variables. How do you want them displayed?",
                                                    inputType: 'checkbox',
                                                    inputOptions: [{text:" Show variables as tree view",value:"Tree View"},{text:" Show variables as list",value:"List"}],
                                                    callback: function (result)
                                                    {
                                                        if (result)
                                                        {
                                                            var message1 = "", message2 = "";
                                                            var array
                                                            bootbox.hideAll();
                                                            if(result.includes("Tree View"))
                                                            {
                                                                var headLine = "Tree Layout for category:\" " + category + "\", subcategory:\" " + sub_category + "\"";  
                                                                var d3_body = d3.select("body");
                                                                var lobiID = String(getRandomInt(0,1000))
                                                                var divClassName = "treeDiv" + lobiID;
                                                                var treeLayoutdiv = d3_body.append("div").attr("class",divClassName+ " panel-default")
                                                                                                .style("left","200px")
                                                                                                .style("top","200px")
                                                                                                .style("position", "absolute")
                                                                treeLayoutdiv.append("div").attr("class","panel-heading")
                                                                    .append("div").attr("class","panel_title").append("h4").text(headLine)
                                                                $('.' + divClassName).lobiPanel({
                                                                    reload: false,
                                                                    editTitle: false,
                                                                    unpin: false,
                                                                    minWidth: 200,
                                                                    maxWidth: 100000,
                                                                    minHeight: 200,
                                                                    maxHeight: 100000,
                                                                });
                                                                $('.'+divClassName).lobiPanel('unpin');
                                                                var treeLayoutSVG = treeLayoutdiv.append("svg").attr("class","treeLayoutSVG")
                                                                var treeLayout = treeLayoutSVG.append("g").attr("class","treeLayout");
                                                                maketreeLayout(array, treeLayout, treeLayoutSVG, treeLayoutdiv, divClassName, headLine, varCategories[0].description);
                                                            }
															if (result.includes("List"))
															{
																showList(title,variables,nodeMenu);
															}
                                                            
                                                        }
                                                    }
                                                })
											}
                                            else
                                            {
                                                //push back functions in list
                                                functions_all.forEach(function(aFunction){
                                                    if (graphElements.includes(aFunction.uID)){functions.push(aFunction);}
                                                })
                                            }
                                            showList(title,functions,toolMenu);
                                        }
                                    },
                                    error: function(result)
                                    {
                                        bootboxContent.message = result
                                        kadmosErrorMessage(bootboxContent);
                                    }
                                });
                                kadmosHavePatience(xhr, bootboxContent)
                                modal.modal("hide");
                                d3.selectAll(".form-content").remove();
                            }
                          }
                        ],
                        onEscape: function() {
                          modal.modal("hide");
                          d3.selectAll(".form-content").remove();
                        }
                    });
                    
                    
               
                })
                //####################################################################################################################
                
                
                
                //aigner: Kadmos functions --> Check graph
                //####################################################################################################################				
                var l1_check_dom = d3.select(".l1_check")
				l1_check_dom.on("mousedown", function()
				{
					var bootboxContent = {title: "L1 check: graph", message: '<p>Please be patient...</p>'};
					var xhr = $.ajax({
						type: 'POST',
						data: {'graphID': graphID, 'sessionId': sessionId},
						url: '/kadmos_L1_check',
						success: function(result)
						{
							if (result.includes("ERROR:"))
							{
								bootboxContent.message = result
								kadmosErrorMessage(bootboxContent);
							}
							else
							{
								bootboxContent.message = result
								kadmosSuccessMessage(bootboxContent);
							}
						},
						error: function(result)
						{
							bootboxContent.message = result
							kadmosErrorMessage(bootboxContent);
						}
					});
					kadmosHavePatience(xhr, bootboxContent)
				})
                    
                var l2_check_dom = d3.select(".l2_check")
				l2_check_dom.on("mousedown", function()
				{
					var bootboxContent = {title: "L2 check: problem formulation", message: '<p>Please be patient...</p>'};
					var xhr = $.ajax({
						type: 'POST',
						data: {'graphID': graphID, 'sessionId': sessionId},
						url: '/kadmos_L2_check',
						success: function(result)
						{
							if (result.includes("ERROR:"))
							{
								bootboxContent.message = result
								kadmosErrorMessage(bootboxContent);
							}
							else
							{
								bootboxContent.message = result
								kadmosSuccessMessage(bootboxContent);
							}
						},
						error: function(result)
						{
							bootboxContent.message = result
							kadmosErrorMessage(bootboxContent);
						}
					});
					kadmosHavePatience(xhr, bootboxContent)
				})
                    
                var l3_check_dom = d3.select(".l3_check")
				l3_check_dom.on("mousedown", function()
				{
					var bootboxContent = {title: "L3 check: graph & problem formulation", message: '<p>Please be patient...</p>'};
					var xhr = $.ajax({
						type: 'POST',
						data: {'graphID': graphID, 'sessionId': sessionId},
						url: '/kadmos_L3_check',
						success: function(result)
						{
							if (result.includes("ERROR:"))
							{
								bootboxContent.message = result
								kadmosErrorMessage(bootboxContent);
							}
							else
							{
								bootboxContent.message = result
								kadmosSuccessMessage(bootboxContent);
							}
						},
						error: function(result)
						{
							bootboxContent.message = result
							kadmosErrorMessage(bootboxContent);
						}
					});
					kadmosHavePatience(xhr, bootboxContent)
				})
                
                //####################################################################################################################

                
                //aigner: Kadmos functions --> Graph manipulation
                //####################################################################################################################				
				var start_MDO_problem_DOM = d3.select(".start_MDO_problem")
				start_MDO_problem_DOM.on("mousedown", function()
				{
					bootbox.hideAll();
					var html = d3.select("html")
					var form_content = html.append("div").attr("class","form-content").attr("style","display:none")
					var form = form_content.append("form").attr("class","form").attr("role","form")
					var form_group, input;
						
					//Tool information
					form_group = form.append("div").attr("class","form-group")
					form_group.append("text").text("Name")
					input = form_group.append("input")
						.attr("id","graph_name")
						.attr("class","form-control")
						.attr("name","graph_name")
						.attr("placeholder","Add a graph name here...")
					//Tool information
					form_group = form.append("div").attr("class","form-group")
					form_group.append("text").text("Description")
					input = form_group.append("input")
						.attr("id","graph_description")
						.attr("class","form-control")
						.attr("name","graph_description")
						.attr("placeholder","Add a graph description here...")
					
				   bootbox.dialog({
                        closeButton: false,
						message: $(".form-content").html(),
						title: "<p>This will create an initial FPG for your MDO problem. After clicking this button, you can go through the \"<b>FPG manipulation</b>\" steps."
							  +"<p>Please type in a new name and description for the graph</p>",
						size: "large",
						buttons: [
						  {
							label: "Cancel",
							className: "btn btn-default pull-left",
							callback: function() {
							  d3.selectAll(".form-content").remove();
							}
						  },
						  {
							label: "OK",
							className: "btn btn-primary pull-left",
							callback: function() {
								
								//get form data
								var graph_name = $('form #graph_name').val();
								var graph_description = $('form #graph_description').val();
								
								
								var newGraphID = '01';
								data.graphs.forEach(function(graph) 
								{
									id_int = parseInt(graph.id)
									if (data.graphs.length < 100){newGraphID = "0" + String(id_int+1);}
									else{newGraphID = String(id_int+1);}
								})
								var bootboxContent = {title: "Copy graph for defining MDO problem", message: '<p>Please be patient...</p>'};
								var xhr = $.ajax({
									type: 'POST',
									url: '/kadmos_start_defining_MDO_problem',
									data: {'graphID':graphID, 'currentOrder': nodeOrder, 'newGraphName':graph_name, 'newGraphDesc':graph_description, 'newGraphID':newGraphID, 'sessionId': sessionId},
									success: function(result)
									{
										if (result.includes("ERROR:"))
										{
											bootboxContent.message = result
											kadmosErrorMessage(bootboxContent);
										}
										else
										{
											var updatedData = jQuery.extend(true, {}, data);
											var graphData = JSON.parse(result);
											
											for (var i = 0; i < data.graphs.length; i++)
											{
												if (graphID == data.graphs[i].id)
												{
													
													//Insert copied graph behind the original one
													updatedData.graphs.splice(i+1, 0, graphData.graphs[0])
												}
											}
											
											clearView();
											makeKadmosMenu(updatedData);
											xdsm_script(updatedData,newGraphID);
											
											bootboxContent.message = "Success!"
											kadmosSuccessMessage(bootboxContent)
										}
									},
									error: function(result)
									{
										bootboxContent.message = result
										kadmosErrorMessage(bootboxContent);
									}
								});
								kadmosHavePatience(xhr, bootboxContent)
								d3.selectAll(".form-content").remove();
							}
						  }
						]
					});
				})
                
                
                var remove_dcs_DOM = d3.select(".remove_dcs")
                remove_dcs_DOM.on("mousedown", function()
                    {                        
                        bootbox.hideAll();
                        bootbox.prompt(
                        {
                            closeButton: false,
                            title: "Please select the design competences you would like to exclude.",
                            inputType: 'checkbox',
                            inputOptions: theInputOptions,
                            callback: function (nodeList) 
                            {
                                if (nodeList)
                                {
                                    var nodeStr = '';
                                    for (var i=0; i< nodeList.length; i++)
                                    {
                                        if (i==0){nodeStr += nodeList[i]}
                                        else{nodeStr += ',' + nodeList[i]}
                                    }
                                    
                                    bootbox.hideAll();
                                    bootbox.confirm("Are you sure you want to do this?", function(sure)
                                    {
                                        if (sure)
                                        {                                
                                            var bootboxContent = {title: "Exclude design competences", message: '<p>Please be patient...</p>'};
                                            var xhr = $.ajax({
                                                type: 'POST',
                                                url: '/kadmos_exclude_DCs',
                                                data: {'graphID':graphID, 'nodeList':nodeStr, 'currentOrder':nodeOrder, 'sessionId': sessionId},
                                                success: function(result)
                                                {
                                                    if (result.includes("ERROR:"))
                                                    {
                                                        bootboxContent.message = result
                                                        kadmosErrorMessage(bootboxContent);
                                                    }
                                                    else
                                                    {
                                                        var updatedData = {};
                                                        updatedData = data;
                                                        var graphData = JSON.parse(result);             
                                                        for (var i = 0; i < updatedData.graphs.length; i++)
                                                        {
                                                            if (graphID == updatedData.graphs[i].id)
                                                            {
                                                                updatedData.graphs[i] = graphData.graphs[0];   
                                                            }
                                                        }
                                                        
                                                        clearView();
                                                        makeKadmosMenu(updatedData);
                                                        xdsm_script(updatedData,graphID);
                                                        
                                                        bootboxContent.message = "Success!"
                                                        kadmosSuccessMessage(bootboxContent)
                                                    }
                                                },
                                                error: function(result)
                                                {
                                                    bootboxContent.message = result
                                                    kadmosErrorMessage(bootboxContent);
                                                }
                                            });
                                            kadmosHavePatience(xhr, bootboxContent)
                                        }
                                    })
                                }
                            }
                        });
                    })
                
				var merge_seq_competences_DOM = d3.select(".merge_seq_competences")
                merge_seq_competences_DOM.on("mousedown", function()
				{                        
					bootbox.hideAll();
					bootbox.prompt(
					{
                        closeButton: false,
						title: "Please select the sequential design competences you would like to merge.",
						inputType: 'checkbox',
						inputOptions: theInputOptions,
						callback: function (nodeList) 
						{
							if (nodeList)
							{
								var nodeStr = '';
								for (var i=0; i< nodeList.length; i++)
								{
									if (i==0){nodeStr += nodeList[i]}
									else{nodeStr += ',' + nodeList[i]}
								}
								
								bootbox.hideAll();
								bootbox.confirm("Are you sure you want to do this", function(sure)
								{
									if (sure)
									{                       
										var bootboxContent = {title: "Merge sequential design competences", message: '<p>Please be patient...</p>'};
										var xhr = $.ajax({
											type: 'POST',
											url: '/kadmos_merge_seq_DCs',
											data: {'graphID':graphID, 'nodeList':nodeStr, 'currentOrder':nodeOrder, 'sessionId': sessionId},
											success: function(result)
											{
												if (result.includes("ERROR:"))
												{
													bootboxContent.message = result
													kadmosErrorMessage(bootboxContent);
												}
												else
												{
													var updatedData = {};
													updatedData = data;
													var graphData = JSON.parse(result);             
													for (var i = 0; i < updatedData.graphs.length; i++)
													{
														if (graphID == updatedData.graphs[i].id)
														{
															updatedData.graphs[i] = graphData.graphs[0];   
														}
													}
													
													clearView();
													makeKadmosMenu(updatedData);
													xdsm_script(updatedData,graphID);
													
													bootboxContent.message = "Success!"
													kadmosSuccessMessage(bootboxContent)
												}
											},
											error: function(result)
											{
												bootboxContent.message = result
												kadmosErrorMessage(bootboxContent);
											}
										});
										kadmosHavePatience(xhr, bootboxContent)
									}
								})
							
							}
						}
					});
				})
                
                var merge_par_competences_DOM = d3.select(".merge_par_competences")
                merge_par_competences_DOM.on("mousedown", function()
				{
					bootbox.hideAll();
					bootbox.prompt(
					{
                        closeButton: false,
						title: "Please select the parallel design competences you would like to merge.",
						inputType: 'checkbox',
						inputOptions: theInputOptions,
						callback: function (nodeList) 
						{
							if (nodeList)
							{
								var nodeStr = '';
								for (var i=0; i< nodeList.length; i++)
								{
									if (i==0){nodeStr += nodeList[i]}
									else{nodeStr += ',' + nodeList[i]}
								}
								
								bootbox.hideAll();
								bootbox.confirm("Are you sure you want to do this", function(sure)
								{
									if (sure)
									{                       
										var bootboxContent = {title: "Merge parallel design competences", message: '<p>Please be patient...</p>'};
										var xhr = $.ajax({
											type: 'POST',
											url: '/kadmos_merge_parallel_DCs',
											data: {'graphID':graphID, 'nodeList':nodeStr, 'currentOrder':nodeOrder, 'sessionId': sessionId},
											success: function(result)
											{
												if (result.includes("ERROR:"))
												{
													bootboxContent.message = result
													kadmosErrorMessage(bootboxContent);
												}
												else
												{
													var updatedData = {};
													updatedData = data;
													var graphData = JSON.parse(result);             
													for (var i = 0; i < updatedData.graphs.length; i++)
													{
														if (graphID == updatedData.graphs[i].id)
														{
															updatedData.graphs[i] = graphData.graphs[0];   
														}
													}
													
													clearView();
													makeKadmosMenu(updatedData);
													xdsm_script(updatedData,graphID);
													
													bootboxContent.message = "Success!"
													kadmosSuccessMessage(bootboxContent)
												}
											},
											error: function(result)
											{
												bootboxContent.message = result
												kadmosErrorMessage(bootboxContent);
											}
										});
										kadmosHavePatience(xhr, bootboxContent)
									}
								})
							
							}
						}
					});
				})
                    
                var merge_funcMod_competences_DOM = d3.select(".merge_funcMod_competences")
                merge_funcMod_competences_DOM.on("mousedown", function()
				{
					bootbox.hideAll();
					bootbox.prompt(
					{
                        closeButton: false,
						title: "Please select the design competences you would like to merge.",
						inputType: 'checkbox',
						inputOptions: theInputOptions,
						callback: function (nodeList) 
						{
							if (nodeList)
							{
								var nodeStr = '';
								for (var i=0; i< nodeList.length; i++)
								{
									if (i==0){nodeStr += nodeList[i]}
									else{nodeStr += ',' + nodeList[i]}
								}
								
								bootbox.hideAll();
								bootbox.confirm("Are you sure you want to do this", function(sure)
								{
									if (sure)
									{                       
										var bootboxContent = {title: "Merge function mode design competences", message: '<p>Please be patient...</p>'};
										var xhr = $.ajax({
											type: 'POST',
											url: '/kadmos_merge_func_mod_DCs',
											data: {'graphID':graphID, 'nodeList':nodeStr, 'currentOrder':nodeOrder, 'sessionId': sessionId},
											success: function(result)
											{
												if (result.includes("ERROR:"))
												{
													bootboxContent.message = result
													kadmosErrorMessage(bootboxContent);
												}
												else
												{
													var updatedData = {};
													updatedData = data;
													var graphData = JSON.parse(result);             
													for (var i = 0; i < updatedData.graphs.length; i++)
													{
														if (graphID == updatedData.graphs[i].id)
														{
															updatedData.graphs[i] = graphData.graphs[0];   
														}
													}
													
													clearView();
													makeKadmosMenu(updatedData);
													xdsm_script(updatedData,graphID);
													
													bootboxContent.message = "Success!"
													kadmosSuccessMessage(bootboxContent)
												}
											},
											error: function(result)
											{
												bootboxContent.message = result
												kadmosErrorMessage(bootboxContent);
											}
										});
										kadmosHavePatience(xhr, bootboxContent)
									}
								})
							
							}
						}
					});
				})
                    
                var remove_collision_parameters_DOM = d3.select(".remove_collision_parameters")
                remove_collision_parameters_DOM.on("mousedown", function()
				{
					bootbox.hideAll();
					bootbox.prompt(
					{
                        closeButton: false,
						title: "Please select the design competences for which you would like to remove collisions.",
						inputType: 'checkbox',
						inputOptions: theInputOptions,
						callback: function (nodeList) 
						{
							if (nodeList)
							{
								var nodeStr = '';
								for (var i=0; i< nodeList.length; i++)
								{
									if (i==0){nodeStr += nodeList[i]}
									else{nodeStr += ',' + nodeList[i]}
								}
								
								bootbox.hideAll();
								bootbox.confirm("Are you sure you want to do this", function(sure)
								{
									if (sure)
									{                       
										var bootboxContent = {title: "Remove collisions", message: '<p>Please be patient...</p>'};
										var xhr = $.ajax({
											type: 'POST',
											url: '/kadmos_remove_collision',
											data: {'graphID':graphID, 'nodeList':nodeStr, 'currentOrder':nodeOrder, 'sessionId': sessionId},
											success: function(result)
											{
												if (result.includes("ERROR:"))
												{
													bootboxContent.message = result
													kadmosErrorMessage(bootboxContent);
												}
												else
												{
													var updatedData = {};
													updatedData = data;
													var graphData = JSON.parse(result);             
													for (var i = 0; i < updatedData.graphs.length; i++)
													{
														if (graphID == updatedData.graphs[i].id)
														{
															updatedData.graphs[i] = graphData.graphs[0];   
														}
													}
													
													clearView();
													makeKadmosMenu(updatedData);
													xdsm_script(updatedData,graphID);
													
													bootboxContent.message = "Success!"
													kadmosSuccessMessage(bootboxContent)
												}
											},
											error: function(result)
											{
												bootboxContent.message = result
												kadmosErrorMessage(bootboxContent);
											}
										});
										kadmosHavePatience(xhr, bootboxContent)
									}
								})
							
							}
						}
					});
				})
                    
                var make_variables_valid_DOM = d3.select(".make_variables_valid")
                make_variables_valid_DOM.on("mousedown", function()
				{
					bootbox.hideAll();
					bootbox.confirm("<p>This will automatically remove all collisions from the graph by creating multiple variable instances.</p>" 
									+"<p>Are you sure you want to do this?</p>", function(sure)
					{
						if (sure)
						{
							var bootboxContent = {title: "Create FPG", message: '<p>Please be patient...</p>'};
							var xhr = $.ajax({
								type: 'POST',
								url: '/kadmos_make_all_variables_valid',
								data: {'graphID':graphID, 'currentOrder':nodeOrder, 'sessionId': sessionId},
								success: function(result)
								{
									if (result.includes("ERROR:"))
									{
										bootboxContent.message = result
										kadmosErrorMessage(bootboxContent);
									}
									else
									{
										var updatedData = {};
										updatedData = data;
										var graphData = JSON.parse(result);             
										for (var i = 0; i < updatedData.graphs.length; i++)
										{
											if (graphID == updatedData.graphs[i].id)
											{
												updatedData.graphs[i] = graphData.graphs[0];   
											}
										}
										
										clearView();
										makeKadmosMenu(updatedData);
										xdsm_script(updatedData,graphID);
										
										bootboxContent.message = "Success!"
										kadmosSuccessMessage(bootboxContent)
									}
								},
								error: function(result)
								{
									bootboxContent.message = result
									kadmosErrorMessage(bootboxContent);
								}
							});
							kadmosHavePatience(xhr, bootboxContent)
						}
					});
				})
                    
                var assign_probFunc_roles_DOM = d3.select(".assign_probFunc_roles")
                assign_probFunc_roles_DOM.on("mousedown", function()
				{
					bootbox.hideAll();
					bootbox.confirm("Are you sure you want to do this?", function(sure)
					{
						if (sure)
						{
							var bootboxContent = {title: "Create FPG", message: '<p>Please be patient...</p>'};
							var xhr = $.ajax({
								type: 'POST',
								url: '/kadmos_add_function_problem_roles',
								data: {'graphID':graphID, 'currentOrder':nodeOrder, 'sessionId': sessionId},
								success: function(result)
								{
									if (result.includes("ERROR:"))
									{
										bootboxContent.message = result
										kadmosErrorMessage(bootboxContent);
									}
									else
									{
										var updatedData = {};
										updatedData = data;
										var graphData = JSON.parse(result);             
										for (var i = 0; i < updatedData.graphs.length; i++)
										{
											if (graphID == updatedData.graphs[i].id)
											{
												updatedData.graphs[i] = graphData.graphs[0];   
											}
										}
										
										clearView();
										makeKadmosMenu(updatedData);
										xdsm_script(updatedData,graphID);
										
										bootboxContent.message = "Success!"
										kadmosSuccessMessage(bootboxContent)
									}
								},
								error: function(result)
								{
									bootboxContent.message = result
									kadmosErrorMessage(bootboxContent);
								}
							});
							kadmosHavePatience(xhr, bootboxContent)
						}
					});
				})
                    
                    
                var assign_parameter_roles_DOM = d3.select(".assign_parameter_roles")
				assign_parameter_roles_DOM.on("mousedown", function()
				{
					if (markedVariables.length<1)
					{
						bootbox.hideAll();
						bootbox.alert({
                            closeButton: false,
							title: "Assign parameter roles",
							message: "<p>Please mark all parameters, which are of special interest. The options are:</p>"
									 +"<ul><li><b>Design Variable</b></li>"
									 +"<li><b>Objective</b></li>"
									 +"<li><b>Constraint</b></li>"
									 +"<li><b>State Variable</b> (also \"quantity of interest\")</li></ul><br/>"
									 +"<p>To \"pre-mark\" special parameters open the <b>Data Model Tree</b> or <b>Data Model List</b>, select the parameters via right-mouse click and use one  of the \"Pre-assign parameter\" options.</p>"
									 +"<p><b>IMPORTANT: After all parameters are \"pre-assigned\", klick this button again to assign the parameter roles!</b></p>"
						})
					}
					else
					{
						markVariable(markedVariables)
					}
					
				})
                
                var get_function_order_DOM = d3.select(".get_function_order")
				get_function_order_DOM.on("mousedown", function()
				{
					bootbox.hideAll();
					bootbox.prompt(
					{
                        closeButton: false,
						title: "Select a sorting method",
						inputType: 'select',
						value: 'single-swap',
						inputOptions: [
									{
								text: 'single-swap',
								value: 'single-swap',
							},
							{
								text: 'two-swap',
								value: 'two-swap',
							},
							{
								text: 'hybrid-swap',
								value: 'hybrid-swap',
							},
							{
								text: 'brute-force',
								value: 'brute-force',
							},
							{
								text: 'branch-and-bound',
								value: 'branch-and-bound',
							}
						],
						callback: function (result) {
							if (result)
							{            
								var method = result;
								var bootboxContent = {title: "Get Possible Function Order", message: '<p>Please be patient...</p>'};
								var xhr = $.ajax({
									type: 'POST',
									url: '/kadmos_get_possible_function_order',
									data: {'graphID':graphID, 'sortingMethod':method, 'currentOrder':nodeOrder, 'sessionId': sessionId},
									success: function(result)
									{
										if (result.includes("ERROR:"))
										{
											bootboxContent.message = result
											kadmosErrorMessage(bootboxContent);
										}
										else
										{
											var updatedData = {};
											updatedData = data;
											var graphData = JSON.parse(result);             
											for (var i = 0; i < updatedData.graphs.length; i++)
											{
												if (graphID == updatedData.graphs[i].id)
												{
													updatedData.graphs[i] = graphData.graphs[0];   
												}
											}
											
											clearView();
											makeKadmosMenu(updatedData);
											xdsm_script(updatedData,graphID);
											
											bootboxContent.message = "Success!"
											kadmosSuccessMessage(bootboxContent)
										}
									},
									error: function(result)
									{
										bootboxContent.message = result
										kadmosErrorMessage(bootboxContent);
									}
								});
								kadmosHavePatience(xhr, bootboxContent)
							}
						}
					});
				})
                
                var remove_unused_outputs_DOM = d3.select(".remove_unused_outputs")
				remove_unused_outputs_DOM.on("mousedown", function()
				{
					bootbox.hideAll();
					bootbox.prompt(
					{
                        closeButton: false,
						title: "<p><b>Remove unused outputs</b></p>" 
							  +"<p>Do you want to clean up the graph afterwards?</p>" 
							  +"<b>CAUTION:</b> This will remove all unused competences automatically as well!",
						inputType: 'select',
						value: "True",
						inputOptions: [{text:'yes', value:'True'}, {text:'no', value:'False'}],
						callback: function (selection) 
						{
							if (selection)
							{
								var bootboxContent = {title: "Enrich FPG", message: '<p>Please be patient...</p>'};
								var xhr = $.ajax({
									type: 'POST',
									url: '/kadmos_remove_unused_outputs',
									data: {'graphID':graphID, 'currentOrder':nodeOrder, 'cleanUp':selection,
										'sessionId': sessionId},
									success: function(result)
									{
										if (result.includes("ERROR:"))
										{
											bootboxContent.message = result
											kadmosErrorMessage(bootboxContent);
										}
										else
										{
											var updatedData = {};
											updatedData = data;
											var graphData = JSON.parse(result);             
											for (var i = 0; i < updatedData.graphs.length; i++)
											{
												if (graphID == updatedData.graphs[i].id)
												{
													updatedData.graphs[i] = graphData.graphs[0];   
												}
											}
											
											clearView();
											makeKadmosMenu(updatedData);
											xdsm_script(updatedData,graphID);
											
											bootboxContent.message = "Success!"
											kadmosSuccessMessage(bootboxContent)
										}
									},
									error: function(result)
									{
										bootboxContent.message = result
										kadmosErrorMessage(bootboxContent);
									}
								});
								kadmosHavePatience(xhr, bootboxContent)
							}
						
						}
					})
				})
                //####################################################################################################################
                
                
                //aigner: MDPG manipulations
                //####################################################################################################################				
                //aigner: Kadmos function Start defining MDO problem
                var start_MDO_architecture_DOM = d3.select(".start_MDO_architecture")
				start_MDO_architecture_DOM.on("mousedown", function()
				{
					bootbox.hideAll();
					bootbox.hideAll();
					var html = d3.select("html")
					var form_content = html.append("div").attr("class","form-content").attr("style","display:none")
					var form = form_content.append("form").attr("class","form").attr("role","form")
					var form_group, input;
						
					//Tool information
					form_group = form.append("div").attr("class","form-group")
					form_group.append("text").text("Name")
					input = form_group.append("input")
						.attr("id","graph_name")
						.attr("class","form-control")
						.attr("name","graph_name")
						.attr("placeholder","Add a graph name here...")
					//Tool information
					form_group = form.append("div").attr("class","form-group")
					form_group.append("text").text("Description")
					input = form_group.append("input")
						.attr("id","graph_description")
						.attr("class","form-control")
						.attr("name","graph_description")
						.attr("placeholder","Add a graph description here...")
					
					bootbox.dialog({
                        closeButton: false,
						message: $(".form-content").html(),
						title: "<p>This will create an initial MDPG for your MDO problem. After clicking this button, you can go through the \"<b>MDPG manipulation</b>\" steps."
							  +"<p>Please type in a new name and description for the graph</p>",
						size: "large",
						buttons: [
						  {
							label: "Cancel",
							className: "btn btn-default pull-left",
							callback: function() {
							  d3.selectAll(".form-content").remove();
							}
						  },
						  {
							label: "OK",
							className: "btn btn-primary pull-left",
							callback: function() {
								
								//get form data
								var graph_name = $('form #graph_name').val();
								var graph_description = $('form #graph_description').val();
								
								
								var newGraphID = '01';
								data.graphs.forEach(function(graph) 
								{
									id_int = parseInt(graph.id)
									if (data.graphs.length < 100){newGraphID = "0" + String(id_int+1);}
									else{newGraphID = String(id_int+1);}
								})
								var bootboxContent = {title: "Copy graph for defining MDAO architecture", message: '<p>Please be patient...</p>'};
								var xhr = $.ajax({
									type: 'POST',
									url: '/kadmos_start_defining_MDAO_architecture',
									data: {'graphID':graphID, 'currentOrder': nodeOrder, 'newGraphName':graph_name, 'newGraphDesc':graph_description, 'newGraphID':newGraphID, 'sessionId': sessionId},
									success: function(result)
									{
										if (result.includes("ERROR:"))
										{
											bootboxContent.message = result
											kadmosErrorMessage(bootboxContent);
										}
										else
										{
											var updatedData = jQuery.extend(true, {}, data);
											var graphData = JSON.parse(result);
											
											for (var i = 0; i < data.graphs.length; i++)
											{
												if (graphID == data.graphs[i].id)
												{
													
													//Insert copied graph behind the original one
													updatedData.graphs.splice(i+1, 0, graphData.graphs[0])
												}
											}
											
											clearView();
											makeKadmosMenu(updatedData);
											xdsm_script(updatedData,newGraphID);
											
											bootboxContent.message = "Success!"
											kadmosSuccessMessage(bootboxContent)
										}
									},
									error: function(result)
									{
										bootboxContent.message = result
										kadmosErrorMessage(bootboxContent);
									}
								});
								kadmosHavePatience(xhr, bootboxContent)
								d3.selectAll(".form-content").remove();
							}
						  }
						]
					});
				})
                
				
				var MDAO_architecture = '-';
                var coupling_decomposition = '-';
                var DOE_method = '-';
                var allow_unconverged_couplings = 'True';
                var set_MDAO_architecture_DOM = d3.select(".set_MDAO_architecture")
                set_MDAO_architecture_DOM.on("mousedown", function()
				{
					var theOptions = []
					for (var i = 0; i<MDAO_architectures.length; i++){theOptions.push({text:MDAO_architectures[i].text, value:MDAO_architectures[i].value})}
					bootbox.hideAll();
					bootbox.prompt(
					{
                        closeButton: false,
						title: "<p><b>Set MDAO Architecture</b></p><br>"
                               +"<p>You can specify the settings for your MDO architecture here.</p>"
                               +"<p>To actually impose the architecture on your graph, you need to press the button <b>\"Impose MDAO architecture\"</b> afterwards.",
						inputType: 'select',
						value: MDAO_architecture,
						size: "large",
						inputOptions: theOptions,
						callback: function (result) {
                        if (result)
                        {
                            MDAO_architecture = result;
                            if (!MDAO_architecture.includes("unconverged"))
                            {
                                var theOptions = [{text:"yes",value:"True"},{text:"no",value:"False"}]
                                bootbox.hideAll();
                                bootbox.prompt(
                                {
                                    closeButton: false,
                                    title: "Allow Unconverged Couplings?",
                                    value: "True",
                                    inputType: 'select',
                                    inputOptions: theOptions,
                                    callback: function (result) 
                                    {
                                        if (result)
                                        {   
                                            allow_unconverged_couplings = result;                                    
                                        }
                                    }
                                })
                            }
                            if (MDAO_architecture.includes("DOE") || MDAO_architecture == "BLISS-2000")
                            {
                                var theOptions = []
                                for (var i = 0; i<DOE_methods.length; i++){theOptions.push({text:DOE_methods[i], value:DOE_methods[i]})}
                                bootbox.prompt(
                                {
                                    closeButton: false,
                                    title: "Select DOE Method",
                                    value: DOE_method,
                                    inputType: 'select',
                                    inputOptions: theOptions,
                                    callback: function (result) 
                                    {
                                        if (result)
                                        {
                                            DOE_method = result;
                                            
                                            
                                            function form_control(text, type, id, form_group)
                                            {
                                                form_group.append("label").text(text)
                                                var input = form_group.append("input")
                                                    .attr("id",id)
                                                    .attr("class","form-control")
                                                    .attr("type",type)
                                                    .attr("name",text)
                                            }
                                            function set_doe_settings(ids)
                                            {
                                                var modal = bootbox.dialog(
                                                {
                                                    closeButton: false,
                                                    message: $(".form-content").html(),
                                                    title: "DOE settings",
                                                    size: "large",
                                                    buttons: [
                                                      {
                                                        label: "OK",
                                                        className: "btn btn-primary pull-right",
                                                        callback: function() {
                                                            ids.forEach(function(id)
                                                            {
                                                                DOE_settings.push({'name': id, 'value': $('form #' + id.split('/').join('_')).val()})
                                                            })
                                                            d3.selectAll(".form-content").remove();
                                                        }
                                                      },
                                                      {
                                                        label: "Cancel",
                                                        className: "btn btn-default pull-right",
                                                        callback: function() {
                                                            d3.selectAll(".form-content").remove();
                                                            bootbox.hideAll();
                                                        }
                                                      }
                                                    ]
                                                });   
                                            }
                                            
                                            var html = d3.select("html")
                                            var form_content = html.append("div").attr("class","form-content").attr("style","display:none")
                                            var form = form_content.append("form").attr("class","form").attr("role","form")
                                            var form_group = form.append("div").attr("class","form-group")        
                                            if (DOE_method=='Full factorial design')
                                            {
                                                bootbox.hideAll();
                                                form_control("Number of levels", "number", "levels", form_group)
                                                form_control("Number of runs", "number", "runs", form_group)
                                                set_doe_settings(["levels","runs"]);                                                
                                            }
                                            else if (DOE_method=='Latin hypercube design')
                                            {
                                                bootbox.hideAll();
                                                form_control("Seed", "number", "seed", form_group)
                                                form_control("Number of runs", "number", "runs", form_group)
                                                set_doe_settings(["seed","runs"]);       
                                            }
                                            else if (DOE_method=='Monte Carlo design')
                                            {
                                                bootbox.hideAll();
                                                form_control("Seed", "number", "seed", form_group)
                                                form_control("Number of runs", "number", "runs", form_group)
                                                set_doe_settings(["seed","runs"]);  
                                            }
                                            else if (DOE_method=='Box-Behnken design')
                                            {
                                                bootbox.hideAll();
                                                form_control("Number of center runs", "number", "center_runs", form_group)
                                                set_doe_settings(["center_runs"]);  
                                            }
                                            else if (DOE_method=='Uniform design')
                                            {
                                                bootbox.hideAll();
                                                form_control("Seed", "number", "seed", form_group)
                                                form_control("Number of runs", "number", "runs", form_group)
                                                set_doe_settings(["seed","runs"]);
                                            }
                                            else if (DOE_method=='Custom design table')
                                            {
                                            }
                                            else;
                                            
                                        }
                                        else
                                        {
                                            bootbox.hideAll();
                                        }
                                    }
                                })
                            }
                            if (MDAO_architecture == "converged-MDA"
                                || MDAO_architecture == "MDF"
                                || MDAO_architecture == "converged-DOE")
                            {
                                var theOptions = []
                                for (var i = 0; i<coupling_decompositions.length; i++){theOptions.push({text:coupling_decompositions[i], value:coupling_decompositions[i]})}
                                bootbox.prompt(
                                {
                                    closeButton: false,
                                    title: "Select Coupling Decomposition",
                                    value: coupling_decomposition,
                                    inputType: 'select',
                                    inputOptions: theOptions,
                                    callback: function (result) {
                                        if (result){coupling_decomposition = result;}
                                        else {bootbox.hideAll()}
                                    }
                                })       
                            }
                            if (MDAO_architecture == "CO" || MDAO_architecture == "BLISS-2000")
                            {
                                bootbox.prompt(
                                {
                                    closeButton: false,
                                    title: "Select number of coupled function groups",
                                    inputType: 'number',
                                    callback: function (result) {
                                        if (result)
                                        {
                                            var number_of_coupled_groups = result;
                                            
                                            var theOptions = [];
                                            for (var j=1; j<currentGraph.xdsm.nodes.length;j++)
                                            {
                                                //aigner: value: j-1 because in KADMOS the coordinator is not in the node list, therefore all other competences' indices are decreased by 1!
                                                theOptions.push({text: currentGraph.xdsm.nodes[j].name, value: currentGraph.xdsm.nodes[j].uID});
                                            }
                                            
                                            coupled_functions_groups = [];
                                            function select_coupled_functions_group(num, counter)
                                            {
                                                counter ++;
                                                if (num >= counter)
                                                {
                                                    bootbox.prompt(
                                                    {
                                                        closeButton: false,
                                                        title: "Select functions for group " +String(counter),
                                                        inputType: 'checkbox',
                                                        inputOptions: theOptions,
                                                        callback: function (result) 
                                                        {
                                                            if (result)
                                                            {
                                                                coupled_functions_groups.push(result)
                                                                select_coupled_functions_group(num, counter);
                                                            }
                                                            else {bootbox.hideAll()}
                                                        }
                                                    })    
                                                }
                                            }
                                            select_coupled_functions_group(number_of_coupled_groups, 0)
                                        }
                                        else {bootbox.hideAll()}
                                    }
                                })       
                            }
                        }}
					})
				})
                    
				var set_coupling_decomposition_DOM = d3.select(".set_coupling_decomposition")
                set_coupling_decomposition_DOM.on("mousedown", function()
				{
					var theOptions = []
					for (var i = 0; i<coupling_decompositions.length; i++){theOptions.push({text:coupling_decompositions[i], value:coupling_decompositions[i]})}
					bootbox.hideAll();
					bootbox.prompt(
					{
                        closeButton: false,
						title: "Select Coupling Decomposition",
						value: coupling_decomposition,
						inputType: 'select',
						inputOptions: theOptions,
						callback: function (result) 
                        {
                            if (result)
                            {
                                coupling_decomposition = result;
                                var theOptions = [{text:"yes",value:"True"},{text:"no",value:"False"}]
                                bootbox.hideAll();
                                bootbox.prompt(
                                {
                                    closeButton: false,
                                    title: "Allow Unconverged Couplings?",
                                    value: "True",
                                    inputType: 'select',
                                    inputOptions: theOptions,
                                    callback: function (result) 
                                    {
                                        if (result)
                                        {   
                                            allow_unconverged_couplings = result;                                    
                                        }
                                    }
                                })
                            }
                        }
					})                    
				})
                    
                var impose_MDAO_architecture_DOM = d3.select(".impose_MDAO_architecture")
				impose_MDAO_architecture_DOM.on("mousedown", function()
				{                  
                    var bootboxContent = {title: "Impose MDAO Architecture", message: '<p>Please be patient...</p>'};
                    DOE_settings.push({'name': 'method','value':DOE_method})
                    var xhr = $.ajax(
                    {
                        type: 'POST',
                        url: '/kadmos_impose_MDAO_architecture',
                        data: {'graphID':graphID, 
                               'currentOrder':nodeOrder,
                               'mdao_architecture':MDAO_architecture,
                               'doe_settings': JSON.stringify(DOE_settings),
                               'coupled_functions_groups': JSON.stringify(coupled_functions_groups),
                               'coupling_decomposition':coupling_decomposition,
                               'allow_unconverged_couplings':allow_unconverged_couplings,
                               'sessionId':sessionId},
                        success: function(result)
                        {
                            if (result.includes("ERROR:"))
                            {
                                bootboxContent.message = result
                                kadmosErrorMessage(bootboxContent);
                            }
                            else
                            {
                                var updatedData = {};
                                updatedData = data;
                                var graphData = JSON.parse(result);             
                                for (var i = 0; i < updatedData.graphs.length; i++)
                                {
                                    if (graphID == updatedData.graphs[i].id)
                                    {
                                        updatedData.graphs[i] = graphData.graphs[0];   
                                    }
                                }
                                
                                clearView();
                                makeKadmosMenu(updatedData);
                                xdsm_script(updatedData,graphID);
                                
                                bootboxContent.message = "Success!"
                                kadmosSuccessMessage(bootboxContent)
                            }
                        },
                        error: function(result)
                        {
                            bootboxContent.message = result
                            kadmosErrorMessage(bootboxContent);
                        }
                    });
                    kadmosHavePatience(xhr, bootboxContent)
				})
                //####################################################################################################################
                
                
                //aigner: Upload custom KADMOS script
                //####################################################################################################################
				var upload_script_DOM = d3.select(".upload_script")
				upload_script_DOM.on("mousedown", function(){
						bootbox.dialog({
                            closeButton: false,
							title: "Upload custom script",
							message: "<button id='downloadBtn' class='btn btn-primary'><i class='glyphicon glyphicon-download-alt '></i> Download template script</button>"
									+"<form style='margin-top:20px;' id='scriptForm' method='post' enctype='multipart/form-data'>" 
									+"<label>"
									+"<text>Upload script file:</text>"
									+"<input type='file' name='file[]' />"
									+"<input type='submit' value='Submit'/>"
									+"</label></form>",
							buttons : 
							{ 
								cancel: {
									label: "Cancel",
									className: 'btn-danger'
								}
							}                                
						});
						
						//aigner: template for custom script that user can download
						$('#downloadBtn').on("mousedown", function(){
								var script_template = "import sys\n\n"
								+"from kadmos.graph import *\n\n\n"
								+"def script(graph, mpg):\n"
								+"    \"\"\"\n"
								+"    Script to include manual operations in the KADMOS integration in VISTOMS\n\n"
								+"    :param graph: Any kadmos graph\n"
								+"    :param mpg: A corresponding MDO process graph\n"
								+"    :return: graph, mpg\n"
								+"    \"\"\"\n"
								+"    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #\n"
								+"    #                                       ADD YOUR SCRIPT BELOW THIS LINE                                           #\n"
								+"    # Example script:\n"
								+"    # graph.remove_node('node_ID')\n"
								+"    # graph.remove_edge('node_ID1', 'node_ID2')\n"
								+"    #                                          END SCRIPT ABOVE THIS LINE                                             #\n"
								+"    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #\n"
								+"    return graph, mpg\n"

								download('script_template.py', script_template);
						})
						//upload of custom script
						$('#scriptForm').on('submit',function(event){
							event.preventDefault();
							var formData = new FormData($('#scriptForm')[0]);
							formData.append('graphID', graphID);
							formData.append('sessionId', sessionId);
						   
							if (typeof currentGraph.xdsm.workflow !== 'undefined' && currentGraph.xdsm.workflow.length > 0) 
							{
								// the array is defined and has at least one element
								upload_custom_script()
							}
							else
							{
								upload_custom_script()
							}
							
							function upload_custom_script()
							{
								var bootboxContent = {title: "Upload custom KADMOS script", message: '<p>Please be patient...</p>'};
								var xhr = $.ajax({
									type: 'POST',
									url: '/kadmos_run_custom_script',
									data: formData,
									processData: false,
									contentType: false,
									success: function(result)
									{
										if (result.includes("ERROR:"))
										{
											bootboxContent.message = result
											kadmosErrorMessage(bootboxContent);
										}
										else
										{
											var updatedData = {};
											updatedData = data;
											var graphData = JSON.parse(result);             
											for (var i = 0; i < updatedData.graphs.length; i++)
											{
												if (graphID == updatedData.graphs[i].id)
												{
													updatedData.graphs[i] = graphData.graphs[0];   
												}
											}
																											
											clearView();
											makeKadmosMenu(updatedData);
											xdsm_script(updatedData,graphID);
											
											bootboxContent.message = "Success!"
											kadmosSuccessMessage(bootboxContent)
										}
									},
									error: function(result)
									{
										bootboxContent.message = result
										kadmosErrorMessage(bootboxContent);
									}
								});
								kadmosHavePatience(xhr, bootboxContent)
							}
						});
				})
				//####################################################################################################################
                
				
				//aigner: Data Model Tree View Button
				//####################################################################################################################
				var dropdown1 = d3.select(".tree_view")
				var links = dropdown1.append("ul").attr("class","view_options");
				for (var j=0; j< varCategories.length; j++)
				{
					//console.log(varCategories[j])
					var linkLi = links.append("li");
					var linkA = linkLi.append("a")
						.attr("id",j)
						.text(varCategories[j].description)
						.on("mouseover", function(){d3.select(this).style("cursor", "pointer")})
						.on("click", function()
						{		
							showFullTree(varCategories[this.id].name,varCategories[this.id].description)
						})
				}
				//####################################################################################################################
                
                //aigner: Data Model List View Button
				//####################################################################################################################
				//aigner: Function, that shows a list of elements
                function showList(aTitle,aList,aMenu)
                {
                    if (aList.length != 0)
                    {
                        var d3_body = d3.select("body");
                        
                        var panel_div = d3_body.append("div").attr("class", "myPanel panel-default")
                        panel_div.append("div").attr("class","panel-heading")
                            .append("div").attr("class","panel_title").append("h4").text(aTitle)
                        panel_div.append("input")
                            .attr("id","myInput")
                            .attr("placeholder","Filter search...")
                            .attr("title","Type in a name")
                            .attr("onkeyup","filterSearch()")
                        var listGroup = panel_div.append("div").attr("class","panel-body")
                            .append("table").attr("id","myTable")
                            .append("tbody")
                            
                        var tr = listGroup
                            .selectAll('tr')
                            .data(aList).enter()
                                .append('tr')
                        var td = tr.append("td").html(function(d) { 
                            if (d.xPath){return d.xPath;} 
                            else {return d.name;} 
                        })
                        tr.on('contextmenu', d3.contextMenu(aMenu));
                        $('.myPanel').lobiPanel({
                            reload: false,
                            editTitle: false,
                            expand: false,
                            unpin: false,
                            resize: "none",
                            minWidth: 200,
                            minHeight: 200,
                            maxWidth: 1100,
                            maxHeight: 1200,
                        });
                        $('.myPanel').lobiPanel('unpin');
                    }
                }
                
				var dropdownList = d3.select(".list_view")
				var linksList = dropdownList.append("ul").attr("class","view_options");
				for (var j=0; j< varCategories.length; j++)
				{
					//console.log(varCategories[j])
					var linkLi = linksList.append("li");
					var linkA = linkLi.append("a")
						.attr("id",j)
						.text(varCategories[j].description)
						.on("mouseover", function(){d3.select(this).style("cursor", "pointer")})
						.on("click", function()
						{	
                            var variables = JSON.parse(JSON.stringify(currentGraph.variableSchemes[varCategories[this.id].name]));
                            variables.forEach(function(variable)
                            {
                                variable.name = variable.xPath
                                //work around because nodeMenu expexts the data, to have a "data" object inside
                                variable.data = variable
                                variable.data.name = variable.xPath.split("/")[variable.xPath.split("/").length-1]
                            })
                            var title = "List view: full variable set categorized according to " + varCategories[this.id].description
                            showList(title,variables,nodeMenu)
						})
				}
				//aigner: Set width of the div, so the VISTOMS dropdown (in the top of the page) still works
				//####################################################################################################################
				
                
				//aigner: Tree option menu to select which kind of tree view the user wants to see
				//#####################################################################//
				var emptyArray;
				function showFullTree(aCategory,categoryDescr)
				{
                    var headLine = "Full data model tree view; Categorization: " + categoryDescr;
                    var d3_body = d3.select("body");
                    var lobiID = String(getRandomInt(0,1000))
                    var divClassName = 'treeDiv' + lobiID;
                    var treeLayoutdiv = d3_body.append("div").attr("class",divClassName+" panel-default")
													.style("left",(d3.event.pageX) + "px")
													.style("top",(d3.event.pageY - 28) + "px")
													.style("position", "absolute")
                    treeLayoutdiv.append("div").attr("class","panel-heading")
                        .append("div").attr("class","panel_title").append("h4").text(headLine)
                    $('.' + divClassName).lobiPanel({
                        reload: false,
                        editTitle: false,
                        unpin: false,
                        minWidth: 200,
                        maxWidth: 100000,
                        minHeight: 200,
                        maxHeight: 100000,
                    });
                    $('.'+divClassName).lobiPanel('unpin');
					var treeLayoutSVG = treeLayoutdiv.append("svg").attr("class","treeLayoutSVG")
					var treeLayout = treeLayoutSVG.append("g").attr("class","treeLayout");
					maketreeLayout(emptyArray, treeLayout, treeLayoutSVG, treeLayoutdiv, divClassName, headLine, aCategory);
				}
				
				var childrenItems = [];
				for (var j=0; j< varCategories.length; j++)
				{
					childrenItems.push({title: 'according to ' + varCategories[j].description,
								   varCategory: varCategories[j].name,
								   description: varCategories[j].description,
								   onMouseClick: function(elm, d, i) {showFullTree(d.varCategory,d.description)},
								   onMouseOver: function(elm,d,i){}})
				}
				var treeOptionMenu = [
				{
					title: 'Show variable tree...',
					onMouseDown: function(elm, d, i) {
					},
					onMouseUp: function(elm, d, i) {
					},
					onMouseOver: function(elm, d, i) {
					},
					childrenItems: childrenItems
				}
				]
				//#####################################################################//
				

				//create the xdsm div
				d3.select(".xdsmDiv").append("div").attr("class","xdsm");
				//create tooltip
				var tooltip = d3.select(".xdsmDiv").selectAll(".tooltip").data(['tooltip'])
							  .enter().append("div")
							.attr("class", "tooltip")
							.style("opacity", 0);

				var scenarioKeys = Object.keys(mdo).sort();
				var xdsms = {};
				
				//If there is only one MDO and it is not named root, then it is created
				if (scenarioKeys.indexOf('root') === -1) 
				{
					var scenarioKeys_tmp = [];
					scenarioKeys_tmp.push('root');
					scenarioKeys = scenarioKeys_tmp;
					var mdo_tmp={};
					mdo_tmp['root']=mdo;
					mdo = mdo_tmp;
				} 		
				// new format managing several XDSM
				var index = scenarioKeys.indexOf('root');
				if (index > -1) 
				{
					scenarioKeys.splice(index, 1);
				}
				scenarioKeys.unshift('root');

				var xdsm;
				scenarioKeys.forEach(function(k) 
				{
					if (mdo.hasOwnProperty(k) && k =="root") 
					{
						//aigner: Here the graphs are created
						var graph = new Graph(mdo[k], k);
						//aigner: Here the xdsms are drawn
						xdsms[k] = new Xdsm(graph, k, tooltip);
						xdsms[k].draw(currentGraph.name);
						xdsm = xdsms[k];
						update_subXdsm(xdsms, k);
						prepareTreeLayout(xdsms, k);
					}
				}, this)
				
					
				xdsm.svg.selectAll(".node")
				.each(function(node) 
				{		
					var gNode = this;
					var d3gNode = d3.select(gNode);
                    
					scenarioKeys.forEach(function(k)
					{
						if (k == node.xdsm)
						{
							var cx, cy;
							for (var j=0; j < gNode.childNodes.length; j++)
							{
								if(gNode.childNodes[j].nodeName != "text")
								{
									cx = gNode.childNodes[j].getAttribute("width")/2-13;
									cy = -gNode.childNodes[j].getAttribute("height")/2+8;
								}
							}
							//Expand Symbol is created
							var expandSymbol = d3.select(gNode).append("g").attr("class", "expandSymbol");
							var circle = expandSymbol.append("circle")
								.attr("cx", cx)
								.attr("cy", cy);
							var line1 = expandSymbol.append("line");
							var line2 = expandSymbol.append("line");
							drawExpandSymbol( circle, line1,line2);
							expandSymbol = expandSymbol
							.on("mouseover", function(){circle.style("fill-opacity", .8);})
							.on("mouseout", function(){circle.style("fill-opacity", .6)})
							
							.on("click", function()
							{
								//On mousedown, additional sub-workflows will be visualized
								var createNew = true;
								if(xdsms[k]){createNew=false;}                    
								if (createNew == true)
								{
									var graph = new Graph(mdo[k], k);
									xdsms[k] = new Xdsm(graph, k, tooltip);
									xdsms[k].draw(currentGraph.name);
									update_subXdsm(xdsms, k);
									prepareTreeLayout(xdsms, k);
									//Remove Symbol is created
									var removeSymbol = xdsms[k].svg.append("g")
										.attr("class", "removeSymbol");
									var circle = removeSymbol.append("circle");
									var minus = removeSymbol.append("line");
									drawRemoveSymbol(xdsms[k], circle, minus);
									//Clicking on remove symbol will remove sub-workflow
									removeSymbol.append("svg:title").text("Click right to inspect");
									removeSymbol = removeSymbol
									.on("mouseover", function(){
										circle.style("fill", "red").style("fill-opacity", .8);})
									.on("mouseout", function(){
										circle.style("fill", "red").style("fill-opacity", .6)})
									.on("mousedown", function(){circle.style("fill", "darkred").style("fill-opacity",1);})
									.on("mouseup", function(d)
									{
										xdsms[k].svg.remove();
										d3.select(".treeDiv"+k).remove();
										delete xdsms[k];
									})
									
									xdsms[k].svg.selectAll(".node")
										.each(function(d) 
										{		
											var gNode = this;
											d3.select(gNode).on('contextmenu', d3.contextMenu(toolMenu));
										})
								}
							})
						}
					})
					d3gNode.append("svg:title").text("Click right to inspect");
                    d3gNode = d3gNode.on('mouseover', function(){d3.select(this).style("cursor", "pointer")});
                    if (this.__data__.name == "Coordinator")
                    {
                        d3gNode = d3gNode.on('contextmenu', d3.contextMenu(toolMenuCoor))
                    }
                    else
                    {
                        d3gNode = d3gNode.on('contextmenu', d3.contextMenu(toolMenu))
                    }
				})	
				
				function prepareTreeLayout(xdsms, refName)
				{			
					function showEdgeTree(data,aVarCategory,categoryDescr)
					{
                        var headLine = "Tree view: " + data.from + "  →  " + data.to + "; Categorization: " + categoryDescr;
                        var d3_body = d3.select("body");
                        var lobiID = String(getRandomInt(0,1000))
                        var divClassName = "treeDiv" + lobiID;
                        var treeLayoutdiv = d3_body.append("div").attr("class",divClassName + " panel-default")
                                                        .style("left",(d3.event.pageX) + "px")
                                                        .style("top",(d3.event.pageY - 28) + "px")
                                                        .style("position", "absolute")
                        treeLayoutdiv.append("div").attr("class","panel-heading")
                            .append("div").attr("class","panel_title").append("h4").text(headLine)
                        $('.'+divClassName).lobiPanel({
                            reload: false,
                            editTitle: false,
                            unpin: false,
                            minWidth: 1000,
                            maxWidth: 100000,
                            minHeight: 500,
                            maxHeight: 100000,
                        });
                        $('.'+divClassName).lobiPanel('unpin');
                        var treeLayoutSVG = treeLayoutdiv.append("svg").attr("class","treeLayoutSVG")
                        var treeLayout = treeLayoutSVG.append("g").attr("class","treeLayout");
                        maketreeLayout(data.name, treeLayout, treeLayoutSVG, treeLayoutdiv, divClassName, headLine, aVarCategory);
					}
					
					function showEdgeTable(anEdge)
					{						                        
                        var headLine = "Edge Information (" + anEdge.from + " - " + anEdge.to + ")";
						var anEdgeNameSplit = anEdge.name.split(',')
						var numberOfconnections = anEdgeNameSplit.length;
						var dimension=0;
						var nullDim=false;
						var undefinedLeafs;
						var theLeafNodes = (JSON.parse(JSON.stringify(currentGraph.variableSchemes[varCategories[0].name])));
						var pipeData = anEdge.name;
						prune_tree(pipeData,theLeafNodes)
						var undefLeafsNumber=0;
						var defLeafsNumber=0;
						for (var k=0;k<theLeafNodes.length;k++)
						{
							if (theLeafNodes[k].dimension!=null){dimension = dimension+theLeafNodes[k].dimension}
							else{nullDim=true}
							if (theLeafNodes[k].value.includes("could not be found")||theLeafNodes[k].value.includes("unknown"))
							{
								if (undefLeafsNumber==0){undefinedLeafs += theLeafNodes[k].xPath;}
								else {undefinedLeafs += "," + theLeafNodes[k].xPath;}
								undefLeafsNumber ++;
							}
							else
							{
								defLeafsNumber++;
							}
						}
						
						//Render data for table
						var data = [];
						data.push({ "name" : "Total number of connections", "value" : numberOfconnections })
						data.push({ "name" : "Number of referenced connections", "value" : defLeafsNumber })
						data.push({ "name" : "Dimension of referenced connections", "value" : String(dimension) })
						if (undefinedLeafs)
						{
							data.push({ "name" : "Number of unreferenced connections", "value" :  undefLeafsNumber})					
						}
						
                        var d3_body = d3.select("body");
                        
                        var panel_div = d3_body.append("div").attr("class", "myPanel panel-default")
                        panel_div.append("div").attr("class","panel-heading")
                            .append("div").attr("class","panel_title").append("h4").text(headLine)
                        var listGroup = panel_div.append("div").attr("class","panel-body")
                            .append("table").attr("id","myTable")
                            .append("tbody")
                            
                        data.forEach(function(listElement)
                        {
                            var row = listGroup.append("tr")
                            row.append("td").text(listElement.name)
                            row.append("td").text(listElement.value)
                            
                        })
                        $('.myPanel').lobiPanel({
                            reload: false,
                            editTitle: false,
                            expand: false,
                            unpin: false,
                            resize: "none",
                            minWidth: 200,
                            minHeight: 200,
                            maxWidth: 1100,
                            maxHeight: 1200,
                        });
                        $('.myPanel').lobiPanel('unpin');
					}
					
					xdsms[refName].svg.selectAll(".edge")
					.each(function(edge) 
					{
						//edgeMenu --> functions for right click options
						var edgeChildrenItemsTree = [];
						for (var j=0; j< varCategories.length; j++)
						{
							edgeChildrenItemsTree.push({title: 'according to ' + varCategories[j].description,
													varCategory: varCategories[j].name,
													description: varCategories[j].description,
													onMouseClick: function(elm, data, i) {showEdgeTree(edge,data.varCategory,data.description)},
													onMouseOver: function(elm,data,i){}});
						}
                        var edgeChildrenItemsList = [];
						for (var j=0; j< varCategories.length; j++)
						{
							edgeChildrenItemsList.push({title: 'according to ' + varCategories[j].description,
                                varCategory: varCategories[j].name,
                                description: varCategories[j].description,
                                onMouseClick: function(elm, data, i) {
                                    var variables = [];
                                    var pipeData = edge.name;
                                    var title = "List view: " + edge.from + "  →  " + edge.to + "; Categorization: " + data.description;
                                    variables = JSON.parse(JSON.stringify(currentGraph.variableSchemes[data.varCategory]))
                                    prune_tree(pipeData,variables)
                                    variables.forEach(function(variable)
                                    {
                                        variable.name = variable.xPath
                                        //work around because nodeMenu expexts the data, to have a "data" object inside
                                        variable.data = variable
                                        variable.data.name = variable.xPath.split("/")[variable.xPath.split("/").length-1]
                                    })
                                    
                                    showList(title,variables,nodeMenu);
                                },
                                onMouseOver: function(elm,data,i){}});
						}
						var theEdge = this;
						var edgeMenu = [
						{
							title: 'Show edge info',
							onMouseDown: function(elm, k, i) {
								showEdgeTable(edge)
							},
							onMouseUp: function(elm, k, i) {
							},
							onMouseOver: function(elm, d, i) {
							},
							childrenItems: []
						},
						{
							title: 'Show variable tree...',
							onMouseDown: function(elm, k, i) {
							},
							onMouseUp: function(elm, k, i) {
							},
							onMouseOver: function(elm, d, i) {
							},
							childrenItems: edgeChildrenItemsTree
						},
                        {
							title: 'Show variable list...',
							onMouseDown: function(elm, k, i) {
							},
							onMouseUp: function(elm, k, i) {
							},
							onMouseOver: function(elm, d, i) {
							},
							childrenItems: edgeChildrenItemsList
						},
						{
							title: 'Delete a variable connection here...',
							onMouseDown: function(elm, k, i) {
                                deleteEdge(k)
							},
							onMouseUp: function(elm, k, i) {
							},
							onMouseOver: function(elm, d, i) {
							},
							childrenItems: {}
						}
						]
                        
                        function deleteEdge(anEdge)
                        {         
                            bootbox.hideAll();
                            bootbox.confirm({
                                closeButton: false,
								title: "Delete output from competence " + anEdge.from_uID,
								message: "<form id='infos' action=''>\Full xPath of the variable: <input type='text' name='edgeName' />\</form>",
								callback: function(result) {
                                        if(result)
                                        {
                                            var formData = $('#infos').submit()[0];
                                            bootbox.confirm("Are you sure you want to do this?", function(sure)
                                            {
                                                if (sure)
                                                {
                                                    var bootboxContent = {title: "Delete variable connection", message: '<p>Please be patient...</p>'};
                                                    var xhr = $.ajax({
                                                        type: 'POST',
                                                        url: '/kadmos_delete_edge',
                                                        data: {graphID: graphID, nodeName: anEdge.from_uID, edgeName: formData[0].value, 'currentOrder':nodeOrder, 'sessionId': sessionId},
                                                        success: function(result)
                                                        {
                                                            if (result.includes("ERROR:"))
                                                            {
                                                                bootboxContent.message = result
                                                                kadmosErrorMessage(bootboxContent);
                                                            }
                                                            else
                                                            {
                                                                var updatedData = {};
                                                                updatedData = data;
                                                                var graphData = JSON.parse(result);             
                                                                for (var i = 0; i < updatedData.graphs.length; i++)
                                                                {
                                                                    if (graphID == updatedData.graphs[i].id)
                                                                    {
                                                                        updatedData.graphs[i] = graphData.graphs[0];   
                                                                    }
                                                                }
                                                                                                                                
                                                                clearView();
                                                                makeKadmosMenu(updatedData);
                                                                xdsm_script(updatedData,graphID);
                                                                
                                                                bootboxContent.message = "Success!"
                                                                kadmosSuccessMessage(bootboxContent)
                                                            }
                                                        },
                                                        error: function(result)
                                                        {
                                                            bootboxContent.message = result
                                                            kadmosErrorMessage(bootboxContent);
                                                        }
                                                    });
                                                    kadmosHavePatience(xhr, bootboxContent)
                                                }
                                            })
                                        }
									}     
                            });
                            
                            $('form').on('submit',function(event){
                                event.preventDefault();
                            });
                        
                        
                        }
						
						var thisEdge = d3.select(this);
						//thisEdge.append("svg:title").text("Click right to inspect");
						thisEdge.on('contextmenu', d3.contextMenu(edgeMenu))
					})
				}
				
               
				function maketreeLayout(pipeData, layout, svg, div, aLobiID, aName, aVarCategory)
				{			
                    var treeData = (JSON.parse(JSON.stringify(currentGraph.variableSchemes[aVarCategory])));				
                    //aigner: Build the tree layout                    
                    //######################################################################
                    //Function to prune the tree according to list of xPaths that are actually there
                    //The tree will only be pruned if there is pipeData, such as in an edge or for the input of a tool
                    if (pipeData)
                    {
                        prune_tree(pipeData, treeData);
                    }    
                    if (pipeData == ""){treeData = {}}
                    //build tree layout for vistoms
                    var newTree = {};
                    buildTree(newTree, treeData)                    
                    treeData = newTree
                    //######################################################################
                    
                    // Set the dimensions and margins of the diagram
                    var margin = {top: 20, right: 90, bottom: 20, left: 90},
                        width = 960 - margin.left - margin.right,
                        height = 500 - margin.top - margin.bottom;

                    // append the svg object to the body of the page
                    // appends a 'group' element to 'svg'
                    // moves the 'group' element to the top left margin
                    var offset_tmp = 60;
                    svg = svg.attr("width", width + margin.right + margin.left+offset_tmp)
                             .attr("height", height + margin.top + margin.bottom+offset_tmp);
                    div = div.attr("width", width + margin.right + margin.left+offset_tmp)
                             .attr("height", height + margin.top + margin.bottom+offset_tmp);
                    layout = layout
                        .attr("width", width + margin.right + margin.left)
                        .attr("height", height + margin.top + margin.bottom);
                    var name;
                    var rect = layout.append("rect")
                            .attr("class","treeFrame")
                            .attr("width", 0)
                            .attr("height", 70)
                            .attr("transform", "translate("
                              + String(10) + "," + String(10) + ")")
                            .attr("fill", "white")
                            .attr("stroke", "darkgrey")
                            .attr("stroke-width", 0)
                            .attr("fill-opacity", 0.8)
                    //aigner: Put remove button on top of the rect
                    d3.selectAll(".treeRemoveSymbol").moveToFront();
                    
                    //aigner: Exit, if no tree data is available!
                    if (!treeData.children || treeData.children.length==0)
                    {
                        var newText = layout.append('text')
                        .attr("dx", 40)
                        .attr("dy", 55)
                        .style("font-family", "Arial")
                        .style("font-size", "32pt")
                        .style("font-weight", "bold")
                        .html(function (d) {
                            return 'No tree view available! &#8594; Please check data for consistency!';
                        });
                        
                        var newWidth;
                        rect.attr("width",  function(d) {
                            newWidth = this.parentNode.getBBox().width+20;
                            return newWidth;}) 
                        div = div.attr("width", newWidth+30)
                        svg = svg.attr("width", newWidth+30)
                        return;
                    }
                    
                    // Calculate total nodes, max label length
                    var totalNodes = 0;
                    var maxLabelLength = 0;
                    
                    var i = 0,
                        duration = 500,
                        root;

                    // declares a tree layout and assigns the size
                    var treemap = d3.tree().size([height, width]);

                    // Assigns parent, children, height, depth
                    root = d3.hierarchy(treeData, function(d) { return d.children; });
                    root.x0 = height / 2;
                    root.y0 = 0;
                    
                    // Append a group which holds all nodes and which the zoom Listener can act upon.
                    var svgGroup_xOff = root.data.name.length*10
                    var svgGroup = layout.append("g")
                        .attr("transform", "translate("+ svgGroup_xOff + "," + String(margin.top+10) + ")");
                    
                        
                    // Collapse root initially
                    collapse(root);
                    update(root);

                    // Collapse the node and all it's children
                    function collapse(d) {
                      if(d.children) {
                        d._children = d.children
                        d._children.forEach(collapse)
                        d.children = null
                      }
                    }
                    // Collapse the node and all it's children
                    function expand(d) {
                      if(d._children) {
                        d.children = d._children
                        d.children.forEach(expand)
                        d._children = null
                      }
                    }
                    // Toggle children on click.
                    function click(d) 
                    {
                        if (d.children) {
                            d._children = d.children;
                            d.children = null;
                        } else 
                        {
                            d.children = d._children;
                            d._children = null;
                        }
                        update(d);
                    }
                    // Collapse/expand entire tree on double-click
                    function dblclick(d) 
                    {
                        if(d.children) 
                        {
                            collapse(d);
                        }
                        else if(d._children)
                        {
                            expand(d);
                        }
                        update(d);
                    }	
                    
                    function update(source) 
                    {
                        var levelWidth = [1];
                        var getLevelWidth = function(level, n) 
                        {
                            if (n.children && n.children.length > 0) {
                                if (levelWidth.length <= level + 1) levelWidth.push(0);
                                
                                //establish maxLabelLength
                                maxLabelLength = Math.max(n.data.name.length, maxLabelLength);
                                
                                levelWidth[level + 1] += n.children.length;
                                n.children.forEach(function(d) {
                                    getLevelWidth(level + 1, d);
                                });
                            }
                        };
                        getLevelWidth(0, root);
                        var newHeight = d3.max(levelWidth) * 60; // 20 pixels per line
                        //BENNI: fill in viewerHeight instead of newHeight for other expanding/collapsing beaviour 
                        
                        layout = layout.attr("height", newHeight + margin.top + margin.bottom)
                        svgGroup = svgGroup.attr("height", newHeight + margin.top + margin.bottom)
                        treemap = treemap.size([newHeight, width]);
                        
                        
                        // Assigns the x and y position for the nodes
                        var treeData = treemap(root);

                        // Compute the new tree layout.
                        var nodes = treeData.descendants(),
                            links = treeData.descendants().slice(1);
                        
                        //aigner: count all descendants of a node
                        function countDescendants(node, counter) 
                        {
                            if (node._children)
                            {
                                node._children.forEach(function(n)
                                {
                                    counter = countDescendants(n, counter);
                                });
                            }
                            else if (node.children)
                            {
                                node.children.forEach(function(n)
                                {
                                    counter = countDescendants(n, counter);
                                });
                            }
                            else
                            {
                                counter ++;
                            }
                            return counter;
                        }
                        
                        
                        var depth_tmp = 0;
                        var labelLength = [];
                        labelLength.push(0);
                        nodes.forEach(function(d) 
                        {				
                            //aigner: If node is collapsed show number of ancestors
                            if (d._children)
                            {
                                d._childrenNum = countDescendants(d, 0);
                                d.data.text = d.data.name + " (" + d._childrenNum + ")";
                            }
                            else 
                            {
                                d.data.text = d.data.name
                            }
                            
                            //aigner: Find maximum labelLength for each level
                            if (d.depth>depth_tmp)
                            {
                                depth_tmp = d.depth;
                                labelLength.push(d.data.text.length);
                            }
                            else
                            {
                                if (d.depth!=0){labelLength[labelLength.length-1] = Math.max(d.data.text.length, labelLength[labelLength.length-1]);}
                            }
                        });
                        // Set widths between levels based on labelLength of each level.
                        var y_tmp = 100;		
                        var depth_tmp = 0;							
                        nodes.forEach(function(d) 
                        {
                            if (d.depth>depth_tmp)
                            {
                                depth_tmp = d.depth;
                                d.y = y_tmp+(labelLength[depth_tmp])*12+50;
                            }
                            else
                            {
                                d.y = y_tmp;
                            }
                            newWidth=d.y;
                            y_tmp = d.y;
                        });
                        
                        newWidth=Math.max(newWidth,getTextWidth(aName,"Arial 12pt"));
                        //aigner: Adjust height and width of the frame
                        $('.' + aLobiID).lobiPanel('setWidth', newWidth + margin.top + margin.bottom +400+maxLabelLength*25+offset_tmp);
                        $('.' + aLobiID).lobiPanel('setHeight', newHeight + margin.top + 2*margin.bottom+offset_tmp);
                        div = div.attr("height", newHeight + margin.top + margin.bottom+offset_tmp)
                        div = div.attr("width", newWidth + margin.top + margin.bottom +400+maxLabelLength*25+offset_tmp)
                            .on("mousedown", function(d) {
                                //d3.select(this).moveToFront();
                            })
                        svg = svg.attr("height", newHeight + margin.top + margin.bottom+offset_tmp)
                        svg = svg.attr("width", newWidth + margin.top + margin.bottom +400+maxLabelLength*25+offset_tmp)
                        rect = rect.attr("height", newHeight + margin.top + margin.bottom)
                        rect = rect.attr("width", newWidth + margin.top + margin.bottom+400+maxLabelLength*15)
                        
                        
                        
                        // ****************** Nodes section ***************************
                        // Update the nodes...
                        var node = svgGroup.selectAll('g.treeNode')
                          .data(nodes, function(d) {return d.id || (d.id = ++i); });
                        
                        var dblclick_timer = false;
                        // Enter any new modes at the parent's previous position.
                        var nodeEnter = node.enter().append('g')
                          .attr('class', 'treeNode')
                          .attr("transform", function(d) {
                            return "translate(" + source.y0 + "," + source.x0 + ")";
                        })
                        .on("mousedown", function(d) {
                            //prevent tree from expanding on right click!
                            if (d3.event.which != 3)
                            {
                                // if double click timer is active, this click is the double click
                                if ( dblclick_timer )
                                {
                                    clearTimeout(dblclick_timer)
                                    dblclick_timer = false
                                    // double click code code comes here
                                    //console.log("DOUBLE CLICK")
                                    dblclick(d);
                                }
                                // otherwise, what to do after single click (double click has timed out)
                                else dblclick_timer = setTimeout( function(){
                                    dblclick_timer = false
                                    // single click code code comes here
                                    //console.log("SINGLE CLICK")
                                    click(d);
                                }, 250)
                            }
                            
                        })
                        
                        // Add Circle for the nodes
                        nodeEnter.append('circle')
                          .attr('class', 'treeNode')
                          .attr('r', 4.5)
                          .style("fill", function(d) {
                                if (d._children)
                                {
                                    if(aName.includes("Input")){return '#ea9999'}
                                    else if(aName.includes("Output")){return '#d6ea99'}
                                    else {return "lightsteelblue"}
                                }
                                else {return "#fff"}
                          })
                          .style("stroke", function(d) {
                                if(aName.includes("Input")){
                                    //console.log(d);
                                    return '#CC0000'}
                                else if(aName.includes("Output")){
                                    //console.log(d);
                                    return '#99CC00'}
                          });

                        // Add labels for the nodes
                        nodeEnter.append('text')
                          .attr('class', 'nodeText')
                          .attr("dy", ".35em")
                          .attr("x", function(d) {
                              return d.children || d._children ? -13 : 13;
                          })
                          .attr("text-anchor", function(d) {
                              return d.children || d._children ? "end" : "start";
                          })
                          //.text(function(d) { return d.data.text; });
                        
                        
                        //Function writeTreeToXML goes through tree nodes and puts the into an xml document
                        function writeTreeToXML(aNode,anXMLDoc,anXPath)
                        {
                            //Variable "children" 
                            //--> One children variable, no matter whether a node has "_children" (collapsed) or "children" (expanded)
                            var children;
                            if (aNode._children){children = aNode._children;}
                            else if (aNode.children){children = aNode.children;}
                            
                            //Get current xml element with its xPath
                            var element = anXMLDoc.evaluate(anXPath,anXMLDoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ).singleNodeValue;
                            if (element != null) {element.value = '...';}
                            
                            //If a node has children (collapsed or expanded), loop through them
                            if (children)
                            {
                                for (var i=0; i < children.length;i++)
                                {
                                    //Name of the new XML element --> childName
                                    var child = children[i];
                                    var childName = child.data.name.split(/[\[\]]+/);//Split childName at "[]" which is the uID 
                                    var cleanChildName = childName[0].split(/[\+\*\^\-\ \#]+/);//Split childName all special characters
                                    var newNode = anXMLDoc.createElement(String(cleanChildName[0]));
                                    
                                    //The children are appended to the xPath --> newXPath
                                    var newXPath = anXPath+"/"+cleanChildName[0];
                                    
                                    //If childName contains a uID, make the uID an attribute
                                    if (childName[1])
                                    {
                                        if (parseInt(childName[1]))
                                        {
                                            var dummyID = childName[1];
                                            newNode.setAttribute("dummyID", dummyID)
                                            newXPath = newXPath+"[@dummyID='"+dummyID+"']";
                                        }
                                        else
                                        {
                                            var uID = childName[1];
                                            newNode.setAttribute("uID", uID)
                                            newXPath = newXPath+"[@uID='"+uID+"']";
                                        }
                                    }
                                    if (cleanChildName.length>1) {newNode.setAttribute("elementName", childName[0])};
                                    
                                    //Append the newNode to the xml structure
                                    element.appendChild(newNode);
                                    
                                    
                                    
                                    ////aigner: Sorting of XML elements according to "uID"
                                    // var items = element.children;
                                    // var itemsArr = [];
                                    // for (var j in items) {
                                        // if (items[j].nodeType == 1) { // get rid of the whitespace text nodes
                                            // itemsArr.push(items[j]);
                                        // }
                                    // }
                                    // itemsArr.sort(function(a,b){
                                        // if (a.getAttribute("uID") < b.getAttribute("uID"))
                                            // return -1;
                                        // if (a.getAttribute("uID") > b.getAttribute("uID"))
                                            // return 1;
                                        // return 0;
                                        // });

                                    // for (j = 0; j < itemsArr.length; ++j) {
                                      // element.appendChild(itemsArr[j]);
                                    // }
                                    
                                    //call function writeTreeToXML recursively for all children
                                    writeTreeToXML(child,anXMLDoc,newXPath)
                                }
                            }
                            else
                            {
                                if (aNode.data.value){element.innerHTML = String(aNode.data.value);}
                                else{element.innerHTML = " ";}
                            }
                            //return the xml document
                            return anXMLDoc;
                        }
                        
                        function removeAttributeInAllElements(aDocument,attribute)
                        {
                          var matchingElements = [];
                          var allElements = aDocument.getElementsByTagName('*');
                          for (var i = 0, n = allElements.length; i < n; i++)
                          {
                            if (allElements[i].getAttribute(attribute) !== null)
                            {
                                allElements[i].removeAttribute(attribute);
                            }
                          }
                          return matchingElements;
                        }
                        
                        function putAncestorsInXMLString(strWrapper, aNode)
                        {
                            //Name of the new XML element
                            var nodeName = aNode.data.name.split(/[\[\]]+/);//Split name at "[]" which is the uID 
                            var cleanNodeName = nodeName[0].split(/[\+\*\^\-\ \#]+/);//Split nodeName all special characters
                            //If nodeName contains a uID, make the uID an attribute
                            if (nodeName[1])
                            {
                                var uID = nodeName[1];
                                strWrapper.val = "<"+cleanNodeName[0]+" uID='"+ uID +"'>"+strWrapper.val+"</"+cleanNodeName[0]+">";;
                            }
                            else
                            {
                                strWrapper.val = "<"+cleanNodeName[0]+">"+strWrapper.val+"</"+cleanNodeName[0]+">";;
                            }
                            var aParent = aNode.parent;
                            if (aParent)
                            {
                                return putAncestorsInXMLString(strWrapper, aParent);
                            }
                            else 
                            {
                                return strWrapper;
                            }
                        }
                        
                        function putAncestorsInXPath(strWrapper, aNode)
                        {
                            //Name of the new XML element
                            var nodeName = aNode.data.name.split(/[\[\]]+/);//Split name at "[]" which is the uID 
                            var cleanNodeName = nodeName[0].split(/[\+\*\^\-\ \#]+/);//Split nodeName all special characters
                            //If nodeName contains a uID, make the uID an attribute
                            if (nodeName[1])
                            {
                                var uID = nodeName[1];
                                strWrapper.val = "/"+cleanNodeName[0]+"[@uID='"+uID+"']"+strWrapper.val;
                            }
                            else
                            {
                                strWrapper.val = "/"+cleanNodeName[0]+strWrapper.val;
                            }
                            var aParent = aNode.parent;
                            if (aParent)
                            {
                                return putAncestorsInXPath(strWrapper, aParent);
                            }
                            else 
                            {
                                return strWrapper;
                            }
                        }
                        
                        var nodeTreeMenu = nodeMenu;
                        var treeMenu = [
                            {
                                title: 'Download full tree as XML-file',
                                onMouseDown: function(elm, d, i) {
                                    //Begin xml structure with the first element
                                    var xmlString = "<"+nodes[0].data.name+">"+"</"+nodes[0].data.name+">";
                                    //Create a new xml document
                                    var parser = new DOMParser();
                                    var xmlDocument = parser.parseFromString(xmlString, "text/xml"); //important to use "text/xml"
                                    //Get initial xPath of the tree and pass it to the function "writeTreeToXML"
                                    var initialXPath = "/"+nodes[0].data.name;								
                                    writeTreeToXML(nodes[0],xmlDocument,initialXPath);
                                    //remove all attributes dummyID
                                    removeAttributeInAllElements(xmlDocument,'dummyID');
                                    //Make the xml document a string
                                    var serializer = new XMLSerializer();
                                    var xmlString = serializer.serializeToString(xmlDocument);
                                    xmlString = vkbeautify.xml(xmlString);														
                                    //Download a document with the xml-schema
                                    download(aName+'_full.xml', xmlString);						
                                },
                                onMouseUp: function(elm, d, i) {
                                },
                                onMouseOver: function(elm, d, i) {
                                },
                                childrenItems: []
                            },
                            {
                                title: 'Download tree as XML-file from current node',
                                onMouseDown: function(elm, d, i) {
                                    var xmlString = putAncestorsInXMLString({ val : '' }, d).val;
                                    var initialXPath = putAncestorsInXPath({ val : '' }, d).val;												
                                    //Create a new xml document
                                    var parser = new DOMParser();
                                    var xmlDocument = parser.parseFromString(xmlString, "text/xml"); //important to use "text/xml"
                                    //Get initial xPath of the tree and pass it to the function "writeCurrentTreeToXML"
                                    writeTreeToXML(d,xmlDocument,initialXPath);
                                    //remove all attributes dummyID
                                    removeAttributeInAllElements(xmlDocument,'dummyID');
                                    //Make the xml document a string
                                    var serializer = new XMLSerializer();
                                    xmlString = serializer.serializeToString(xmlDocument);
                                    xmlString = vkbeautify.xml(xmlString);														
                                    //Download a document with the xml-schema
                                    download(aName+"_"+d.data.name+'.xml', xmlString);						
                                },
                                onMouseUp: function(elm, d, i) {
                                },
                                onMouseOver: function(elm, d, i) {
                                },
                                childrenItems: []
                            }
                        ]
                        var nodeTreeMenu = nodeMenu.concat(treeMenu);
                        
                        
                        
                        nodeEnter = nodeEnter
                        .on('contextmenu', d3.contextMenu(nodeTreeMenu));                          
                        
                        
                        nodeEnter.append("svg:title").text("Click left to expand, click right to inspect")
                        
                        

                        // UPDATE
                        var nodeUpdate = nodeEnter.merge(node);

                        // Transition to the proper position for the node
                        nodeUpdate.transition()
                        .duration(duration)
                        .attr("transform", function(d) { 
                            return "translate(" + d.y + "," + d.x + ")";
                         });

                        // Update the node attributes and style
                        nodeUpdate.select('circle.treeNode')
                        .attr('r', 4.5)
                        .style("fill", function(d) {
                                if (d._children)
                                {
                                    if(aName.includes("Input")){return '#ea9999'}
                                    else if(aName.includes("Output")){return '#d6ea99'}
                                    else {return "lightsteelblue"}
                                }
                                else {return "#fff"}
                          })
                        .attr('cursor', 'pointer');
                        
                        nodeUpdate.select('text')
                        .text(function(d) { return d.data.text; });


                        // Remove any exiting nodes
                        var nodeExit = node.exit().transition()
                          .duration(duration)
                          .attr("transform", function(d) {
                              return "translate(" + source.y + "," + source.x + ")";
                          })
                          .remove();

                        // ****************** links section ***************************

                        // Update the links...
                        var link = svgGroup.selectAll('path.treeLink')
                          .data(links, function(d) { return d.id; });

                        // Enter any new links at the parent's previous position.
                        var linkEnter = link.enter().insert('path', "g")
                          .attr("class", "treeLink")
                          .attr('d', function(d){
                            var o = {x: source.x0, y: source.y0}
                            return diagonal(o, o)
                          });

                        // UPDATE
                        var linkUpdate = linkEnter.merge(link);

                        // Transition back to the parent element position
                        linkUpdate.transition()
                          .duration(duration)
                          .attr('d', function(d){ return diagonal(d, d.parent) });

                        // Remove any exiting links
                        var linkExit = link.exit().transition()
                          .duration(duration)
                          .attr('d', function(d) {
                            var o = {x: source.x, y: source.y}
                            return diagonal(o, o)
                          })
                          .remove();

                        // Store the old positions for transition.
                        nodes.forEach(function(d){
                        d.x0 = d.x;
                        d.y0 = d.y;
                        });

                        // Creates a curved (diagonal) path from parent to the child nodes
                        function diagonal(s, d) {
                        return "M" + s.y + "," + s.x
                              + "C" + (s.y + d.y) / 2 + "," + s.x
                              + " " + (s.y + d.y) / 2 + "," + d.x
                              + " " + d.y + "," + d.x;
                        }
                    }
				}
			}
			startXDSM(data,graphID);

			////aigner: Unused functions --> Could be helpful at some point
			////aigner: clone function, so that original object is not overwritten but deep copied
			// function clone(obj) {
				// if (null == obj || "object" != typeof obj) return obj;
				// var copy = obj.constructor();
				// for (var attr in obj) {
					// if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
				// }
				// return copy;
			// }
			////aigner: cleanArray Function
			// function cleanArray(actual) 
			// {
				// var newArray = new Array();
				// for (var i = 0; i < actual.length; i++) 
				// {
					// if (actual[i]) 
					// {
						// newArray.push(actual[i]);
					// }
				// }
				// return newArray;
			// }
			//#####################################################################//


			},{"./src/animation":2,"./src/graph":3,"./src/xdsm":5,"d3":1}]},{},[6]);
		}
		
		function edgeBundles_script(data,graphID)
		{
			
			(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
			!function(){function n(n){return n&&(n.ownerDocument||n.document||n).documentElement}function t(n){return n&&(n.ownerDocument&&n.ownerDocument.defaultView||n.document&&n||n.defaultView)}function e(n,t){return t>n?-1:n>t?1:n>=t?0:NaN}function r(n){return null===n?NaN:+n}function i(n){return!isNaN(n)}function u(n){return{left:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)<0?r=u+1:i=u}return r},right:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)>0?i=u:r=u+1}return r}}}function o(n){return n.length}function a(n){for(var t=1;n*t%1;)t*=10;return t}function l(n,t){for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}function c(){this._=Object.create(null)}function f(n){return(n+="")===bo||n[0]===_o?_o+n:n}function s(n){return(n+="")[0]===_o?n.slice(1):n}function h(n){return f(n)in this._}function p(n){return(n=f(n))in this._&&delete this._[n]}function g(){var n=[];for(var t in this._)n.push(s(t));return n}function v(){var n=0;for(var t in this._)++n;return n}function d(){for(var n in this._)return!1;return!0}function y(){this._=Object.create(null)}function m(n){return n}function M(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function x(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(var e=0,r=wo.length;r>e;++e){var i=wo[e]+t;if(i in n)return i}}function b(){}function _(){}function w(n){function t(){for(var t,r=e,i=-1,u=r.length;++i<u;)(t=r[i].on)&&t.apply(this,arguments);return n}var e=[],r=new c;return t.on=function(t,i){var u,o=r.get(t);return arguments.length<2?o&&o.on:(o&&(o.on=null,e=e.slice(0,u=e.indexOf(o)).concat(e.slice(u+1)),r.remove(t)),i&&e.push(r.set(t,{on:i})),n)},t}function S(){ao.event.preventDefault()}function k(){for(var n,t=ao.event;n=t.sourceEvent;)t=n;return t}function N(n){for(var t=new _,e=0,r=arguments.length;++e<r;)t[arguments[e]]=w(t);return t.of=function(e,r){return function(i){try{var u=i.sourceEvent=ao.event;i.target=n,ao.event=i,t[i.type].apply(e,r)}finally{ao.event=u}}},t}function E(n){return ko(n,Co),n}function A(n){return"function"==typeof n?n:function(){return No(n,this)}}function C(n){return"function"==typeof n?n:function(){return Eo(n,this)}}function z(n,t){function e(){this.removeAttribute(n)}function r(){this.removeAttributeNS(n.space,n.local)}function i(){this.setAttribute(n,t)}function u(){this.setAttributeNS(n.space,n.local,t)}function o(){var e=t.apply(this,arguments);null==e?this.removeAttribute(n):this.setAttribute(n,e)}function a(){var e=t.apply(this,arguments);null==e?this.removeAttributeNS(n.space,n.local):this.setAttributeNS(n.space,n.local,e)}return n=ao.ns.qualify(n),null==t?n.local?r:e:"function"==typeof t?n.local?a:o:n.local?u:i}function L(n){return n.trim().replace(/\s+/g," ")}function q(n){return new RegExp("(?:^|\\s+)"+ao.requote(n)+"(?:\\s+|$)","g")}function T(n){return(n+"").trim().split(/^|\s+/)}function R(n,t){function e(){for(var e=-1;++e<i;)n[e](this,t)}function r(){for(var e=-1,r=t.apply(this,arguments);++e<i;)n[e](this,r)}n=T(n).map(D);var i=n.length;return"function"==typeof t?r:e}function D(n){var t=q(n);return function(e,r){if(i=e.classList)return r?i.add(n):i.remove(n);var i=e.getAttribute("class")||"";r?(t.lastIndex=0,t.test(i)||e.setAttribute("class",L(i+" "+n))):e.setAttribute("class",L(i.replace(t," ")))}}function P(n,t,e){function r(){this.style.removeProperty(n)}function i(){this.style.setProperty(n,t,e)}function u(){var r=t.apply(this,arguments);null==r?this.style.removeProperty(n):this.style.setProperty(n,r,e)}return null==t?r:"function"==typeof t?u:i}function U(n,t){function e(){delete this[n]}function r(){this[n]=t}function i(){var e=t.apply(this,arguments);null==e?delete this[n]:this[n]=e}return null==t?e:"function"==typeof t?i:r}function j(n){function t(){var t=this.ownerDocument,e=this.namespaceURI;return e===zo&&t.documentElement.namespaceURI===zo?t.createElement(n):t.createElementNS(e,n)}function e(){return this.ownerDocument.createElementNS(n.space,n.local)}return"function"==typeof n?n:(n=ao.ns.qualify(n)).local?e:t}function F(){var n=this.parentNode;n&&n.removeChild(this)}function H(n){return{__data__:n}}function O(n){return function(){return Ao(this,n)}}function I(n){return arguments.length||(n=e),function(t,e){return t&&e?n(t.__data__,e.__data__):!t-!e}}function Y(n,t){for(var e=0,r=n.length;r>e;e++)for(var i,u=n[e],o=0,a=u.length;a>o;o++)(i=u[o])&&t(i,o,e);return n}function Z(n){return ko(n,qo),n}function V(n){var t,e;return function(r,i,u){var o,a=n[u].update,l=a.length;for(u!=e&&(e=u,t=0),i>=t&&(t=i+1);!(o=a[t])&&++t<l;);return o}}function X(n,t,e){function r(){var t=this[o];t&&(this.removeEventListener(n,t,t.$),delete this[o])}function i(){var i=l(t,co(arguments));r.call(this),this.addEventListener(n,this[o]=i,i.$=e),i._=t}function u(){var t,e=new RegExp("^__on([^.]+)"+ao.requote(n)+"$");for(var r in this)if(t=r.match(e)){var i=this[r];this.removeEventListener(t[1],i,i.$),delete this[r]}}var o="__on"+n,a=n.indexOf("."),l=$;a>0&&(n=n.slice(0,a));var c=To.get(n);return c&&(n=c,l=B),a?t?i:r:t?b:u}function $(n,t){return function(e){var r=ao.event;ao.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{ao.event=r}}}function B(n,t){var e=$(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function W(e){var r=".dragsuppress-"+ ++Do,i="click"+r,u=ao.select(t(e)).on("touchmove"+r,S).on("dragstart"+r,S).on("selectstart"+r,S);if(null==Ro&&(Ro="onselectstart"in e?!1:x(e.style,"userSelect")),Ro){var o=n(e).style,a=o[Ro];o[Ro]="none"}return function(n){if(u.on(r,null),Ro&&(o[Ro]=a),n){var t=function(){u.on(i,null)};u.on(i,function(){S(),t()},!0),setTimeout(t,0)}}}function J(n,e){e.changedTouches&&(e=e.changedTouches[0]);var r=n.ownerSVGElement||n;if(r.createSVGPoint){var i=r.createSVGPoint();if(0>Po){var u=t(n);if(u.scrollX||u.scrollY){r=ao.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var o=r[0][0].getScreenCTM();Po=!(o.f||o.e),r.remove()}}return Po?(i.x=e.pageX,i.y=e.pageY):(i.x=e.clientX,i.y=e.clientY),i=i.matrixTransform(n.getScreenCTM().inverse()),[i.x,i.y]}var a=n.getBoundingClientRect();return[e.clientX-a.left-n.clientLeft,e.clientY-a.top-n.clientTop]}function G(){return ao.event.changedTouches[0].identifier}function K(n){return n>0?1:0>n?-1:0}function Q(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function nn(n){return n>1?0:-1>n?Fo:Math.acos(n)}function tn(n){return n>1?Io:-1>n?-Io:Math.asin(n)}function en(n){return((n=Math.exp(n))-1/n)/2}function rn(n){return((n=Math.exp(n))+1/n)/2}function un(n){return((n=Math.exp(2*n))-1)/(n+1)}function on(n){return(n=Math.sin(n/2))*n}function an(){}function ln(n,t,e){return this instanceof ln?(this.h=+n,this.s=+t,void(this.l=+e)):arguments.length<2?n instanceof ln?new ln(n.h,n.s,n.l):_n(""+n,wn,ln):new ln(n,t,e)}function cn(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?u+(o-u)*n/60:180>n?o:240>n?u+(o-u)*(240-n)/60:u}function i(n){return Math.round(255*r(n))}var u,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,u=2*e-o,new mn(i(n+120),i(n),i(n-120))}function fn(n,t,e){return this instanceof fn?(this.h=+n,this.c=+t,void(this.l=+e)):arguments.length<2?n instanceof fn?new fn(n.h,n.c,n.l):n instanceof hn?gn(n.l,n.a,n.b):gn((n=Sn((n=ao.rgb(n)).r,n.g,n.b)).l,n.a,n.b):new fn(n,t,e)}function sn(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),new hn(e,Math.cos(n*=Yo)*t,Math.sin(n)*t)}function hn(n,t,e){return this instanceof hn?(this.l=+n,this.a=+t,void(this.b=+e)):arguments.length<2?n instanceof hn?new hn(n.l,n.a,n.b):n instanceof fn?sn(n.h,n.c,n.l):Sn((n=mn(n)).r,n.g,n.b):new hn(n,t,e)}function pn(n,t,e){var r=(n+16)/116,i=r+t/500,u=r-e/200;return i=vn(i)*na,r=vn(r)*ta,u=vn(u)*ea,new mn(yn(3.2404542*i-1.5371385*r-.4985314*u),yn(-.969266*i+1.8760108*r+.041556*u),yn(.0556434*i-.2040259*r+1.0572252*u))}function gn(n,t,e){return n>0?new fn(Math.atan2(e,t)*Zo,Math.sqrt(t*t+e*e),n):new fn(NaN,NaN,n)}function vn(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function dn(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function yn(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function mn(n,t,e){return this instanceof mn?(this.r=~~n,this.g=~~t,void(this.b=~~e)):arguments.length<2?n instanceof mn?new mn(n.r,n.g,n.b):_n(""+n,mn,cn):new mn(n,t,e)}function Mn(n){return new mn(n>>16,n>>8&255,255&n)}function xn(n){return Mn(n)+""}function bn(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function _n(n,t,e){var r,i,u,o=0,a=0,l=0;if(r=/([a-z]+)\((.*)\)/.exec(n=n.toLowerCase()))switch(i=r[2].split(","),r[1]){case"hsl":return e(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return t(Nn(i[0]),Nn(i[1]),Nn(i[2]))}return(u=ua.get(n))?t(u.r,u.g,u.b):(null==n||"#"!==n.charAt(0)||isNaN(u=parseInt(n.slice(1),16))||(4===n.length?(o=(3840&u)>>4,o=o>>4|o,a=240&u,a=a>>4|a,l=15&u,l=l<<4|l):7===n.length&&(o=(16711680&u)>>16,a=(65280&u)>>8,l=255&u)),t(o,a,l))}function wn(n,t,e){var r,i,u=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-u,l=(o+u)/2;return a?(i=.5>l?a/(o+u):a/(2-o-u),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=NaN,i=l>0&&1>l?0:r),new ln(r,i,l)}function Sn(n,t,e){n=kn(n),t=kn(t),e=kn(e);var r=dn((.4124564*n+.3575761*t+.1804375*e)/na),i=dn((.2126729*n+.7151522*t+.072175*e)/ta),u=dn((.0193339*n+.119192*t+.9503041*e)/ea);return hn(116*i-16,500*(r-i),200*(i-u))}function kn(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function Nn(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function En(n){return"function"==typeof n?n:function(){return n}}function An(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),Cn(t,e,n,r)}}function Cn(n,t,e,r){function i(){var n,t=l.status;if(!t&&Ln(l)||t>=200&&300>t||304===t){try{n=e.call(u,l)}catch(r){return void o.error.call(u,r)}o.load.call(u,n)}else o.error.call(u,l)}var u={},o=ao.dispatch("beforesend","progress","load","error"),a={},l=new XMLHttpRequest,c=null;return!this.XDomainRequest||"withCredentials"in l||!/^(http(s)?:)?\/\//.test(n)||(l=new XDomainRequest),"onload"in l?l.onload=l.onerror=i:l.onreadystatechange=function(){l.readyState>3&&i()},l.onprogress=function(n){var t=ao.event;ao.event=n;try{o.progress.call(u,l)}finally{ao.event=t}},u.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",u)},u.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",u):t},u.responseType=function(n){return arguments.length?(c=n,u):c},u.response=function(n){return e=n,u},["get","post"].forEach(function(n){u[n]=function(){return u.send.apply(u,[n].concat(co(arguments)))}}),u.send=function(e,r,i){if(2===arguments.length&&"function"==typeof r&&(i=r,r=null),l.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),l.setRequestHeader)for(var f in a)l.setRequestHeader(f,a[f]);return null!=t&&l.overrideMimeType&&l.overrideMimeType(t),null!=c&&(l.responseType=c),null!=i&&u.on("error",i).on("load",function(n){i(null,n)}),o.beforesend.call(u,l),l.send(null==r?null:r),u},u.abort=function(){return l.abort(),u},ao.rebind(u,o,"on"),null==r?u:u.get(zn(r))}function zn(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function Ln(n){var t=n.responseType;return t&&"text"!==t?n.response:n.responseText}function qn(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var i=e+t,u={c:n,t:i,n:null};return aa?aa.n=u:oa=u,aa=u,la||(ca=clearTimeout(ca),la=1,fa(Tn)),u}function Tn(){var n=Rn(),t=Dn()-n;t>24?(isFinite(t)&&(clearTimeout(ca),ca=setTimeout(Tn,t)),la=0):(la=1,fa(Tn))}function Rn(){for(var n=Date.now(),t=oa;t;)n>=t.t&&t.c(n-t.t)&&(t.c=null),t=t.n;return n}function Dn(){for(var n,t=oa,e=1/0;t;)t.c?(t.t<e&&(e=t.t),t=(n=t).n):t=n?n.n=t.n:oa=t.n;return aa=n,e}function Pn(n,t){return t-(n?Math.ceil(Math.log(n)/Math.LN10):1)}function Un(n,t){var e=Math.pow(10,3*xo(8-t));return{scale:t>8?function(n){return n/e}:function(n){return n*e},symbol:n}}function jn(n){var t=n.decimal,e=n.thousands,r=n.grouping,i=n.currency,u=r&&e?function(n,t){for(var i=n.length,u=[],o=0,a=r[0],l=0;i>0&&a>0&&(l+a+1>t&&(a=Math.max(1,t-l)),u.push(n.substring(i-=a,i+a)),!((l+=a+1)>t));)a=r[o=(o+1)%r.length];return u.reverse().join(e)}:m;return function(n){var e=ha.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"-",l=e[4]||"",c=e[5],f=+e[6],s=e[7],h=e[8],p=e[9],g=1,v="",d="",y=!1,m=!0;switch(h&&(h=+h.substring(1)),(c||"0"===r&&"="===o)&&(c=r="0",o="="),p){case"n":s=!0,p="g";break;case"%":g=100,d="%",p="f";break;case"p":g=100,d="%",p="r";break;case"b":case"o":case"x":case"X":"#"===l&&(v="0"+p.toLowerCase());case"c":m=!1;case"d":y=!0,h=0;break;case"s":g=-1,p="r"}"$"===l&&(v=i[0],d=i[1]),"r"!=p||h||(p="g"),null!=h&&("g"==p?h=Math.max(1,Math.min(21,h)):"e"!=p&&"f"!=p||(h=Math.max(0,Math.min(20,h)))),p=pa.get(p)||Fn;var M=c&&s;return function(n){var e=d;if(y&&n%1)return"";var i=0>n||0===n&&0>1/n?(n=-n,"-"):"-"===a?"":a;if(0>g){var l=ao.formatPrefix(n,h);n=l.scale(n),e=l.symbol+d}else n*=g;n=p(n,h);var x,b,_=n.lastIndexOf(".");if(0>_){var w=m?n.lastIndexOf("e"):-1;0>w?(x=n,b=""):(x=n.substring(0,w),b=n.substring(w))}else x=n.substring(0,_),b=t+n.substring(_+1);!c&&s&&(x=u(x,1/0));var S=v.length+x.length+b.length+(M?0:i.length),k=f>S?new Array(S=f-S+1).join(r):"";return M&&(x=u(k+x,k.length?f-b.length:1/0)),i+=v,n=x+b,("<"===o?i+n+k:">"===o?k+i+n:"^"===o?k.substring(0,S>>=1)+i+n+k.substring(S):i+(M?n:k+n))+e}}}function Fn(n){return n+""}function Hn(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function On(n,t,e){function r(t){var e=n(t),r=u(e,1);return r-t>t-e?e:r}function i(e){return t(e=n(new va(e-1)),1),e}function u(n,e){return t(n=new va(+n),e),n}function o(n,r,u){var o=i(n),a=[];if(u>1)for(;r>o;)e(o)%u||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{va=Hn;var r=new Hn;return r._=n,o(r,t,e)}finally{va=Date}}n.floor=n,n.round=r,n.ceil=i,n.offset=u,n.range=o;var l=n.utc=In(n);return l.floor=l,l.round=In(r),l.ceil=In(i),l.offset=In(u),l.range=a,n}function In(n){return function(t,e){try{va=Hn;var r=new Hn;return r._=t,n(r,e)._}finally{va=Date}}}function Yn(n){function t(n){function t(t){for(var e,i,u,o=[],a=-1,l=0;++a<r;)37===n.charCodeAt(a)&&(o.push(n.slice(l,a)),null!=(i=ya[e=n.charAt(++a)])&&(e=n.charAt(++a)),(u=A[e])&&(e=u(t,null==i?"e"===e?" ":"0":i)),o.push(e),l=a+1);return o.push(n.slice(l,a)),o.join("")}var r=n.length;return t.parse=function(t){var r={y:1900,m:0,d:1,H:0,M:0,S:0,L:0,Z:null},i=e(r,n,t,0);if(i!=t.length)return null;"p"in r&&(r.H=r.H%12+12*r.p);var u=null!=r.Z&&va!==Hn,o=new(u?Hn:va);return"j"in r?o.setFullYear(r.y,0,r.j):"W"in r||"U"in r?("w"in r||(r.w="W"in r?1:0),o.setFullYear(r.y,0,1),o.setFullYear(r.y,0,"W"in r?(r.w+6)%7+7*r.W-(o.getDay()+5)%7:r.w+7*r.U-(o.getDay()+6)%7)):o.setFullYear(r.y,r.m,r.d),o.setHours(r.H+(r.Z/100|0),r.M+r.Z%100,r.S,r.L),u?o._:o},t.toString=function(){return n},t}function e(n,t,e,r){for(var i,u,o,a=0,l=t.length,c=e.length;l>a;){if(r>=c)return-1;if(i=t.charCodeAt(a++),37===i){if(o=t.charAt(a++),u=C[o in ya?t.charAt(a++):o],!u||(r=u(n,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){_.lastIndex=0;var r=_.exec(t.slice(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){x.lastIndex=0;var r=x.exec(t.slice(e));return r?(n.w=b.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){N.lastIndex=0;var r=N.exec(t.slice(e));return r?(n.m=E.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.slice(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,A.c.toString(),t,r)}function l(n,t,r){return e(n,A.x.toString(),t,r)}function c(n,t,r){return e(n,A.X.toString(),t,r)}function f(n,t,e){var r=M.get(t.slice(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var s=n.dateTime,h=n.date,p=n.time,g=n.periods,v=n.days,d=n.shortDays,y=n.months,m=n.shortMonths;t.utc=function(n){function e(n){try{va=Hn;var t=new va;return t._=n,r(t)}finally{va=Date}}var r=t(n);return e.parse=function(n){try{va=Hn;var t=r.parse(n);return t&&t._}finally{va=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ct;var M=ao.map(),x=Vn(v),b=Xn(v),_=Vn(d),w=Xn(d),S=Vn(y),k=Xn(y),N=Vn(m),E=Xn(m);g.forEach(function(n,t){M.set(n.toLowerCase(),t)});var A={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return m[n.getMonth()]},B:function(n){return y[n.getMonth()]},c:t(s),d:function(n,t){return Zn(n.getDate(),t,2)},e:function(n,t){return Zn(n.getDate(),t,2)},H:function(n,t){return Zn(n.getHours(),t,2)},I:function(n,t){return Zn(n.getHours()%12||12,t,2)},j:function(n,t){return Zn(1+ga.dayOfYear(n),t,3)},L:function(n,t){return Zn(n.getMilliseconds(),t,3)},m:function(n,t){return Zn(n.getMonth()+1,t,2)},M:function(n,t){return Zn(n.getMinutes(),t,2)},p:function(n){return g[+(n.getHours()>=12)]},S:function(n,t){return Zn(n.getSeconds(),t,2)},U:function(n,t){return Zn(ga.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Zn(ga.mondayOfYear(n),t,2)},x:t(h),X:t(p),y:function(n,t){return Zn(n.getFullYear()%100,t,2)},Y:function(n,t){return Zn(n.getFullYear()%1e4,t,4)},Z:at,"%":function(){return"%"}},C={a:r,A:i,b:u,B:o,c:a,d:tt,e:tt,H:rt,I:rt,j:et,L:ot,m:nt,M:it,p:f,S:ut,U:Bn,w:$n,W:Wn,x:l,X:c,y:Gn,Y:Jn,Z:Kn,"%":lt};return t}function Zn(n,t,e){var r=0>n?"-":"",i=(r?-n:n)+"",u=i.length;return r+(e>u?new Array(e-u+1).join(t)+i:i)}function Vn(n){return new RegExp("^(?:"+n.map(ao.requote).join("|")+")","i")}function Xn(n){for(var t=new c,e=-1,r=n.length;++e<r;)t.set(n[e].toLowerCase(),e);return t}function $n(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+1));return r?(n.w=+r[0],e+r[0].length):-1}function Bn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e));return r?(n.U=+r[0],e+r[0].length):-1}function Wn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e));return r?(n.W=+r[0],e+r[0].length):-1}function Jn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+4));return r?(n.y=+r[0],e+r[0].length):-1}function Gn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.y=Qn(+r[0]),e+r[0].length):-1}function Kn(n,t,e){return/^[+-]\d{4}$/.test(t=t.slice(e,e+5))?(n.Z=-t,e+5):-1}function Qn(n){return n+(n>68?1900:2e3)}function nt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function tt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function et(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function rt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function it(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function ut(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ot(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function at(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=xo(t)/60|0,i=xo(t)%60;return e+Zn(r,"0",2)+Zn(i,"0",2)}function lt(n,t,e){Ma.lastIndex=0;var r=Ma.exec(t.slice(e,e+1));return r?e+r[0].length:-1}function ct(n){for(var t=n.length,e=-1;++e<t;)n[e][0]=this(n[e][0]);return function(t){for(var e=0,r=n[e];!r[1](t);)r=n[++e];return r[0](t)}}function ft(){}function st(n,t,e){var r=e.s=n+t,i=r-n,u=r-i;e.t=n-u+(t-i)}function ht(n,t){n&&wa.hasOwnProperty(n.type)&&wa[n.type](n,t)}function pt(n,t,e){var r,i=-1,u=n.length-e;for(t.lineStart();++i<u;)r=n[i],t.point(r[0],r[1],r[2]);t.lineEnd()}function gt(n,t){var e=-1,r=n.length;for(t.polygonStart();++e<r;)pt(n[e],t,1);t.polygonEnd()}function vt(){function n(n,t){n*=Yo,t=t*Yo/2+Fo/4;var e=n-r,o=e>=0?1:-1,a=o*e,l=Math.cos(t),c=Math.sin(t),f=u*c,s=i*l+f*Math.cos(a),h=f*o*Math.sin(a);ka.add(Math.atan2(h,s)),r=n,i=l,u=c}var t,e,r,i,u;Na.point=function(o,a){Na.point=n,r=(t=o)*Yo,i=Math.cos(a=(e=a)*Yo/2+Fo/4),u=Math.sin(a)},Na.lineEnd=function(){n(t,e)}}function dt(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function yt(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function mt(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function Mt(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function xt(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function bt(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function _t(n){return[Math.atan2(n[1],n[0]),tn(n[2])]}function wt(n,t){return xo(n[0]-t[0])<Uo&&xo(n[1]-t[1])<Uo}function St(n,t){n*=Yo;var e=Math.cos(t*=Yo);kt(e*Math.cos(n),e*Math.sin(n),Math.sin(t))}function kt(n,t,e){++Ea,Ca+=(n-Ca)/Ea,za+=(t-za)/Ea,La+=(e-La)/Ea}function Nt(){function n(n,i){n*=Yo;var u=Math.cos(i*=Yo),o=u*Math.cos(n),a=u*Math.sin(n),l=Math.sin(i),c=Math.atan2(Math.sqrt((c=e*l-r*a)*c+(c=r*o-t*l)*c+(c=t*a-e*o)*c),t*o+e*a+r*l);Aa+=c,qa+=c*(t+(t=o)),Ta+=c*(e+(e=a)),Ra+=c*(r+(r=l)),kt(t,e,r)}var t,e,r;ja.point=function(i,u){i*=Yo;var o=Math.cos(u*=Yo);t=o*Math.cos(i),e=o*Math.sin(i),r=Math.sin(u),ja.point=n,kt(t,e,r)}}function Et(){ja.point=St}function At(){function n(n,t){n*=Yo;var e=Math.cos(t*=Yo),o=e*Math.cos(n),a=e*Math.sin(n),l=Math.sin(t),c=i*l-u*a,f=u*o-r*l,s=r*a-i*o,h=Math.sqrt(c*c+f*f+s*s),p=r*o+i*a+u*l,g=h&&-nn(p)/h,v=Math.atan2(h,p);Da+=g*c,Pa+=g*f,Ua+=g*s,Aa+=v,qa+=v*(r+(r=o)),Ta+=v*(i+(i=a)),Ra+=v*(u+(u=l)),kt(r,i,u)}var t,e,r,i,u;ja.point=function(o,a){t=o,e=a,ja.point=n,o*=Yo;var l=Math.cos(a*=Yo);r=l*Math.cos(o),i=l*Math.sin(o),u=Math.sin(a),kt(r,i,u)},ja.lineEnd=function(){n(t,e),ja.lineEnd=Et,ja.point=St}}function Ct(n,t){function e(e,r){return e=n(e,r),t(e[0],e[1])}return n.invert&&t.invert&&(e.invert=function(e,r){return e=t.invert(e,r),e&&n.invert(e[0],e[1])}),e}function zt(){return!0}function Lt(n,t,e,r,i){var u=[],o=[];if(n.forEach(function(n){if(!((t=n.length-1)<=0)){var t,e=n[0],r=n[t];if(wt(e,r)){i.lineStart();for(var a=0;t>a;++a)i.point((e=n[a])[0],e[1]);return void i.lineEnd()}var l=new Tt(e,n,null,!0),c=new Tt(e,null,l,!1);l.o=c,u.push(l),o.push(c),l=new Tt(r,n,null,!1),c=new Tt(r,null,l,!0),l.o=c,u.push(l),o.push(c)}}),o.sort(t),qt(u),qt(o),u.length){for(var a=0,l=e,c=o.length;c>a;++a)o[a].e=l=!l;for(var f,s,h=u[0];;){for(var p=h,g=!0;p.v;)if((p=p.n)===h)return;f=p.z,i.lineStart();do{if(p.v=p.o.v=!0,p.e){if(g)for(var a=0,c=f.length;c>a;++a)i.point((s=f[a])[0],s[1]);else r(p.x,p.n.x,1,i);p=p.n}else{if(g){f=p.p.z;for(var a=f.length-1;a>=0;--a)i.point((s=f[a])[0],s[1])}else r(p.x,p.p.x,-1,i);p=p.p}p=p.o,f=p.z,g=!g}while(!p.v);i.lineEnd()}}}function qt(n){if(t=n.length){for(var t,e,r=0,i=n[0];++r<t;)i.n=e=n[r],e.p=i,i=e;i.n=e=n[0],e.p=i}}function Tt(n,t,e,r){this.x=n,this.z=t,this.o=e,this.e=r,this.v=!1,this.n=this.p=null}function Rt(n,t,e,r){return function(i,u){function o(t,e){var r=i(t,e);n(t=r[0],e=r[1])&&u.point(t,e)}function a(n,t){var e=i(n,t);d.point(e[0],e[1])}function l(){m.point=a,d.lineStart()}function c(){m.point=o,d.lineEnd()}function f(n,t){v.push([n,t]);var e=i(n,t);x.point(e[0],e[1])}function s(){x.lineStart(),v=[]}function h(){f(v[0][0],v[0][1]),x.lineEnd();var n,t=x.clean(),e=M.buffer(),r=e.length;if(v.pop(),g.push(v),v=null,r)if(1&t){n=e[0];var i,r=n.length-1,o=-1;if(r>0){for(b||(u.polygonStart(),b=!0),u.lineStart();++o<r;)u.point((i=n[o])[0],i[1]);u.lineEnd()}}else r>1&&2&t&&e.push(e.pop().concat(e.shift())),p.push(e.filter(Dt))}var p,g,v,d=t(u),y=i.invert(r[0],r[1]),m={point:o,lineStart:l,lineEnd:c,polygonStart:function(){m.point=f,m.lineStart=s,m.lineEnd=h,p=[],g=[]},polygonEnd:function(){m.point=o,m.lineStart=l,m.lineEnd=c,p=ao.merge(p);var n=Ot(y,g);p.length?(b||(u.polygonStart(),b=!0),Lt(p,Ut,n,e,u)):n&&(b||(u.polygonStart(),b=!0),u.lineStart(),e(null,null,1,u),u.lineEnd()),b&&(u.polygonEnd(),b=!1),p=g=null},sphere:function(){u.polygonStart(),u.lineStart(),e(null,null,1,u),u.lineEnd(),u.polygonEnd()}},M=Pt(),x=t(M),b=!1;return m}}function Dt(n){return n.length>1}function Pt(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:b,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Ut(n,t){return((n=n.x)[0]<0?n[1]-Io-Uo:Io-n[1])-((t=t.x)[0]<0?t[1]-Io-Uo:Io-t[1])}function jt(n){var t,e=NaN,r=NaN,i=NaN;return{lineStart:function(){n.lineStart(),t=1},point:function(u,o){var a=u>0?Fo:-Fo,l=xo(u-e);xo(l-Fo)<Uo?(n.point(e,r=(r+o)/2>0?Io:-Io),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(u,r),t=0):i!==a&&l>=Fo&&(xo(e-i)<Uo&&(e-=i*Uo),xo(u-a)<Uo&&(u-=a*Uo),r=Ft(e,r,u,o),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(a,r),t=0),n.point(e=u,r=o),i=a},lineEnd:function(){n.lineEnd(),e=r=NaN},clean:function(){return 2-t}}}function Ft(n,t,e,r){var i,u,o=Math.sin(n-e);return xo(o)>Uo?Math.atan((Math.sin(t)*(u=Math.cos(r))*Math.sin(e)-Math.sin(r)*(i=Math.cos(t))*Math.sin(n))/(i*u*o)):(t+r)/2}function Ht(n,t,e,r){var i;if(null==n)i=e*Io,r.point(-Fo,i),r.point(0,i),r.point(Fo,i),r.point(Fo,0),r.point(Fo,-i),r.point(0,-i),r.point(-Fo,-i),r.point(-Fo,0),r.point(-Fo,i);else if(xo(n[0]-t[0])>Uo){var u=n[0]<t[0]?Fo:-Fo;i=e*u/2,r.point(-u,i),r.point(0,i),r.point(u,i)}else r.point(t[0],t[1])}function Ot(n,t){var e=n[0],r=n[1],i=[Math.sin(e),-Math.cos(e),0],u=0,o=0;ka.reset();for(var a=0,l=t.length;l>a;++a){var c=t[a],f=c.length;if(f)for(var s=c[0],h=s[0],p=s[1]/2+Fo/4,g=Math.sin(p),v=Math.cos(p),d=1;;){d===f&&(d=0),n=c[d];var y=n[0],m=n[1]/2+Fo/4,M=Math.sin(m),x=Math.cos(m),b=y-h,_=b>=0?1:-1,w=_*b,S=w>Fo,k=g*M;if(ka.add(Math.atan2(k*_*Math.sin(w),v*x+k*Math.cos(w))),u+=S?b+_*Ho:b,S^h>=e^y>=e){var N=mt(dt(s),dt(n));bt(N);var E=mt(i,N);bt(E);var A=(S^b>=0?-1:1)*tn(E[2]);(r>A||r===A&&(N[0]||N[1]))&&(o+=S^b>=0?1:-1)}if(!d++)break;h=y,g=M,v=x,s=n}}return(-Uo>u||Uo>u&&-Uo>ka)^1&o}function It(n){function t(n,t){return Math.cos(n)*Math.cos(t)>u}function e(n){var e,u,l,c,f;return{lineStart:function(){c=l=!1,f=1},point:function(s,h){var p,g=[s,h],v=t(s,h),d=o?v?0:i(s,h):v?i(s+(0>s?Fo:-Fo),h):0;if(!e&&(c=l=v)&&n.lineStart(),v!==l&&(p=r(e,g),(wt(e,p)||wt(g,p))&&(g[0]+=Uo,g[1]+=Uo,v=t(g[0],g[1]))),v!==l)f=0,v?(n.lineStart(),p=r(g,e),n.point(p[0],p[1])):(p=r(e,g),n.point(p[0],p[1]),n.lineEnd()),e=p;else if(a&&e&&o^v){var y;d&u||!(y=r(g,e,!0))||(f=0,o?(n.lineStart(),n.point(y[0][0],y[0][1]),n.point(y[1][0],y[1][1]),n.lineEnd()):(n.point(y[1][0],y[1][1]),n.lineEnd(),n.lineStart(),n.point(y[0][0],y[0][1])))}!v||e&&wt(e,g)||n.point(g[0],g[1]),e=g,l=v,u=d},lineEnd:function(){l&&n.lineEnd(),e=null},clean:function(){return f|(c&&l)<<1}}}function r(n,t,e){var r=dt(n),i=dt(t),o=[1,0,0],a=mt(r,i),l=yt(a,a),c=a[0],f=l-c*c;if(!f)return!e&&n;var s=u*l/f,h=-u*c/f,p=mt(o,a),g=xt(o,s),v=xt(a,h);Mt(g,v);var d=p,y=yt(g,d),m=yt(d,d),M=y*y-m*(yt(g,g)-1);if(!(0>M)){var x=Math.sqrt(M),b=xt(d,(-y-x)/m);if(Mt(b,g),b=_t(b),!e)return b;var _,w=n[0],S=t[0],k=n[1],N=t[1];w>S&&(_=w,w=S,S=_);var E=S-w,A=xo(E-Fo)<Uo,C=A||Uo>E;if(!A&&k>N&&(_=k,k=N,N=_),C?A?k+N>0^b[1]<(xo(b[0]-w)<Uo?k:N):k<=b[1]&&b[1]<=N:E>Fo^(w<=b[0]&&b[0]<=S)){var z=xt(d,(-y+x)/m);return Mt(z,g),[b,_t(z)]}}}function i(t,e){var r=o?n:Fo-n,i=0;return-r>t?i|=1:t>r&&(i|=2),-r>e?i|=4:e>r&&(i|=8),i}var u=Math.cos(n),o=u>0,a=xo(u)>Uo,l=ve(n,6*Yo);return Rt(t,e,l,o?[0,-n]:[-Fo,n-Fo])}function Yt(n,t,e,r){return function(i){var u,o=i.a,a=i.b,l=o.x,c=o.y,f=a.x,s=a.y,h=0,p=1,g=f-l,v=s-c;if(u=n-l,g||!(u>0)){if(u/=g,0>g){if(h>u)return;p>u&&(p=u)}else if(g>0){if(u>p)return;u>h&&(h=u)}if(u=e-l,g||!(0>u)){if(u/=g,0>g){if(u>p)return;u>h&&(h=u)}else if(g>0){if(h>u)return;p>u&&(p=u)}if(u=t-c,v||!(u>0)){if(u/=v,0>v){if(h>u)return;p>u&&(p=u)}else if(v>0){if(u>p)return;u>h&&(h=u)}if(u=r-c,v||!(0>u)){if(u/=v,0>v){if(u>p)return;u>h&&(h=u)}else if(v>0){if(h>u)return;p>u&&(p=u)}return h>0&&(i.a={x:l+h*g,y:c+h*v}),1>p&&(i.b={x:l+p*g,y:c+p*v}),i}}}}}}function Zt(n,t,e,r){function i(r,i){return xo(r[0]-n)<Uo?i>0?0:3:xo(r[0]-e)<Uo?i>0?2:1:xo(r[1]-t)<Uo?i>0?1:0:i>0?3:2}function u(n,t){return o(n.x,t.x)}function o(n,t){var e=i(n,1),r=i(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function l(n){for(var t=0,e=d.length,r=n[1],i=0;e>i;++i)for(var u,o=1,a=d[i],l=a.length,c=a[0];l>o;++o)u=a[o],c[1]<=r?u[1]>r&&Q(c,u,n)>0&&++t:u[1]<=r&&Q(c,u,n)<0&&--t,c=u;return 0!==t}function c(u,a,l,c){var f=0,s=0;if(null==u||(f=i(u,l))!==(s=i(a,l))||o(u,a)<0^l>0){do c.point(0===f||3===f?n:e,f>1?r:t);while((f=(f+l+4)%4)!==s)}else c.point(a[0],a[1])}function f(i,u){return i>=n&&e>=i&&u>=t&&r>=u}function s(n,t){f(n,t)&&a.point(n,t)}function h(){C.point=g,d&&d.push(y=[]),S=!0,w=!1,b=_=NaN}function p(){v&&(g(m,M),x&&w&&E.rejoin(),v.push(E.buffer())),C.point=s,w&&a.lineEnd()}function g(n,t){n=Math.max(-Ha,Math.min(Ha,n)),t=Math.max(-Ha,Math.min(Ha,t));var e=f(n,t);if(d&&y.push([n,t]),S)m=n,M=t,x=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:b,y:_},b:{x:n,y:t}};A(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}b=n,_=t,w=e}var v,d,y,m,M,x,b,_,w,S,k,N=a,E=Pt(),A=Yt(n,t,e,r),C={point:s,lineStart:h,lineEnd:p,polygonStart:function(){a=E,v=[],d=[],k=!0},polygonEnd:function(){a=N,v=ao.merge(v);var t=l([n,r]),e=k&&t,i=v.length;(e||i)&&(a.polygonStart(),e&&(a.lineStart(),c(null,null,1,a),a.lineEnd()),i&&Lt(v,u,t,c,a),a.polygonEnd()),v=d=y=null}};return C}}function Vt(n){var t=0,e=Fo/3,r=ae(n),i=r(t,e);return i.parallels=function(n){return arguments.length?r(t=n[0]*Fo/180,e=n[1]*Fo/180):[t/Fo*180,e/Fo*180]},i}function Xt(n,t){function e(n,t){var e=Math.sqrt(u-2*i*Math.sin(t))/i;return[e*Math.sin(n*=i),o-e*Math.cos(n)]}var r=Math.sin(n),i=(r+Math.sin(t))/2,u=1+r*(2*i-r),o=Math.sqrt(u)/i;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/i,tn((u-(n*n+e*e)*i*i)/(2*i))]},e}function $t(){function n(n,t){Ia+=i*n-r*t,r=n,i=t}var t,e,r,i;$a.point=function(u,o){$a.point=n,t=r=u,e=i=o},$a.lineEnd=function(){n(t,e)}}function Bt(n,t){Ya>n&&(Ya=n),n>Va&&(Va=n),Za>t&&(Za=t),t>Xa&&(Xa=t)}function Wt(){function n(n,t){o.push("M",n,",",t,u)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function i(){o.push("Z")}var u=Jt(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return u=Jt(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Jt(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function Gt(n,t){Ca+=n,za+=t,++La}function Kt(){function n(n,r){var i=n-t,u=r-e,o=Math.sqrt(i*i+u*u);qa+=o*(t+n)/2,Ta+=o*(e+r)/2,Ra+=o,Gt(t=n,e=r)}var t,e;Wa.point=function(r,i){Wa.point=n,Gt(t=r,e=i)}}function Qt(){Wa.point=Gt}function ne(){function n(n,t){var e=n-r,u=t-i,o=Math.sqrt(e*e+u*u);qa+=o*(r+n)/2,Ta+=o*(i+t)/2,Ra+=o,o=i*n-r*t,Da+=o*(r+n),Pa+=o*(i+t),Ua+=3*o,Gt(r=n,i=t)}var t,e,r,i;Wa.point=function(u,o){Wa.point=n,Gt(t=r=u,e=i=o)},Wa.lineEnd=function(){n(t,e)}}function te(n){function t(t,e){n.moveTo(t+o,e),n.arc(t,e,o,0,Ho)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function i(){a.point=t}function u(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:i,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=i,a.point=t},pointRadius:function(n){return o=n,a},result:b};return a}function ee(n){function t(n){return(a?r:e)(n)}function e(t){return ue(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){M=NaN,S.point=u,t.lineStart()}function u(e,r){var u=dt([e,r]),o=n(e,r);i(M,x,m,b,_,w,M=o[0],x=o[1],m=e,b=u[0],_=u[1],w=u[2],a,t),t.point(M,x)}function o(){S.point=e,t.lineEnd()}function l(){
			r(),S.point=c,S.lineEnd=f}function c(n,t){u(s=n,h=t),p=M,g=x,v=b,d=_,y=w,S.point=u}function f(){i(M,x,m,b,_,w,p,g,s,v,d,y,a,t),S.lineEnd=o,o()}var s,h,p,g,v,d,y,m,M,x,b,_,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=l},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function i(t,e,r,a,l,c,f,s,h,p,g,v,d,y){var m=f-t,M=s-e,x=m*m+M*M;if(x>4*u&&d--){var b=a+p,_=l+g,w=c+v,S=Math.sqrt(b*b+_*_+w*w),k=Math.asin(w/=S),N=xo(xo(w)-1)<Uo||xo(r-h)<Uo?(r+h)/2:Math.atan2(_,b),E=n(N,k),A=E[0],C=E[1],z=A-t,L=C-e,q=M*z-m*L;(q*q/x>u||xo((m*z+M*L)/x-.5)>.3||o>a*p+l*g+c*v)&&(i(t,e,r,a,l,c,A,C,N,b/=S,_/=S,w,d,y),y.point(A,C),i(A,C,N,b,_,w,f,s,h,p,g,v,d,y))}}var u=.5,o=Math.cos(30*Yo),a=16;return t.precision=function(n){return arguments.length?(a=(u=n*n)>0&&16,t):Math.sqrt(u)},t}function re(n){var t=ee(function(t,e){return n([t*Zo,e*Zo])});return function(n){return le(t(n))}}function ie(n){this.stream=n}function ue(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function oe(n){return ae(function(){return n})()}function ae(n){function t(n){return n=a(n[0]*Yo,n[1]*Yo),[n[0]*h+l,c-n[1]*h]}function e(n){return n=a.invert((n[0]-l)/h,(c-n[1])/h),n&&[n[0]*Zo,n[1]*Zo]}function r(){a=Ct(o=se(y,M,x),u);var n=u(v,d);return l=p-n[0]*h,c=g+n[1]*h,i()}function i(){return f&&(f.valid=!1,f=null),t}var u,o,a,l,c,f,s=ee(function(n,t){return n=u(n,t),[n[0]*h+l,c-n[1]*h]}),h=150,p=480,g=250,v=0,d=0,y=0,M=0,x=0,b=Fa,_=m,w=null,S=null;return t.stream=function(n){return f&&(f.valid=!1),f=le(b(o,s(_(n)))),f.valid=!0,f},t.clipAngle=function(n){return arguments.length?(b=null==n?(w=n,Fa):It((w=+n)*Yo),i()):w},t.clipExtent=function(n){return arguments.length?(S=n,_=n?Zt(n[0][0],n[0][1],n[1][0],n[1][1]):m,i()):S},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(p=+n[0],g=+n[1],r()):[p,g]},t.center=function(n){return arguments.length?(v=n[0]%360*Yo,d=n[1]%360*Yo,r()):[v*Zo,d*Zo]},t.rotate=function(n){return arguments.length?(y=n[0]%360*Yo,M=n[1]%360*Yo,x=n.length>2?n[2]%360*Yo:0,r()):[y*Zo,M*Zo,x*Zo]},ao.rebind(t,s,"precision"),function(){return u=n.apply(this,arguments),t.invert=u.invert&&e,r()}}function le(n){return ue(n,function(t,e){n.point(t*Yo,e*Yo)})}function ce(n,t){return[n,t]}function fe(n,t){return[n>Fo?n-Ho:-Fo>n?n+Ho:n,t]}function se(n,t,e){return n?t||e?Ct(pe(n),ge(t,e)):pe(n):t||e?ge(t,e):fe}function he(n){return function(t,e){return t+=n,[t>Fo?t-Ho:-Fo>t?t+Ho:t,e]}}function pe(n){var t=he(n);return t.invert=he(-n),t}function ge(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*r+a*i;return[Math.atan2(l*u-f*o,a*r-c*i),tn(f*u+l*o)]}var r=Math.cos(n),i=Math.sin(n),u=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*u-l*o;return[Math.atan2(l*u+c*o,a*r+f*i),tn(f*r-a*i)]},e}function ve(n,t){var e=Math.cos(n),r=Math.sin(n);return function(i,u,o,a){var l=o*t;null!=i?(i=de(e,i),u=de(e,u),(o>0?u>i:i>u)&&(i+=o*Ho)):(i=n+o*Ho,u=n-.5*l);for(var c,f=i;o>0?f>u:u>f;f-=l)a.point((c=_t([e,-r*Math.cos(f),-r*Math.sin(f)]))[0],c[1])}}function de(n,t){var e=dt(t);e[0]-=n,bt(e);var r=nn(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Uo)%(2*Math.PI)}function ye(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function me(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function Me(n){return n.source}function xe(n){return n.target}function be(n,t,e,r){var i=Math.cos(t),u=Math.sin(t),o=Math.cos(r),a=Math.sin(r),l=i*Math.cos(n),c=i*Math.sin(n),f=o*Math.cos(e),s=o*Math.sin(e),h=2*Math.asin(Math.sqrt(on(r-t)+i*o*on(e-n))),p=1/Math.sin(h),g=h?function(n){var t=Math.sin(n*=h)*p,e=Math.sin(h-n)*p,r=e*l+t*f,i=e*c+t*s,o=e*u+t*a;return[Math.atan2(i,r)*Zo,Math.atan2(o,Math.sqrt(r*r+i*i))*Zo]}:function(){return[n*Zo,t*Zo]};return g.distance=h,g}function _e(){function n(n,i){var u=Math.sin(i*=Yo),o=Math.cos(i),a=xo((n*=Yo)-t),l=Math.cos(a);Ja+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*u-e*o*l)*a),e*u+r*o*l),t=n,e=u,r=o}var t,e,r;Ga.point=function(i,u){t=i*Yo,e=Math.sin(u*=Yo),r=Math.cos(u),Ga.point=n},Ga.lineEnd=function(){Ga.point=Ga.lineEnd=b}}function we(n,t){function e(t,e){var r=Math.cos(t),i=Math.cos(e),u=n(r*i);return[u*i*Math.sin(t),u*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),i=t(r),u=Math.sin(i),o=Math.cos(i);return[Math.atan2(n*u,r*o),Math.asin(r&&e*u/r)]},e}function Se(n,t){function e(n,t){o>0?-Io+Uo>t&&(t=-Io+Uo):t>Io-Uo&&(t=Io-Uo);var e=o/Math.pow(i(t),u);return[e*Math.sin(u*n),o-e*Math.cos(u*n)]}var r=Math.cos(n),i=function(n){return Math.tan(Fo/4+n/2)},u=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(i(t)/i(n)),o=r*Math.pow(i(n),u)/u;return u?(e.invert=function(n,t){var e=o-t,r=K(u)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/u,2*Math.atan(Math.pow(o/r,1/u))-Io]},e):Ne}function ke(n,t){function e(n,t){var e=u-t;return[e*Math.sin(i*n),u-e*Math.cos(i*n)]}var r=Math.cos(n),i=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),u=r/i+n;return xo(i)<Uo?ce:(e.invert=function(n,t){var e=u-t;return[Math.atan2(n,e)/i,u-K(i)*Math.sqrt(n*n+e*e)]},e)}function Ne(n,t){return[n,Math.log(Math.tan(Fo/4+t/2))]}function Ee(n){var t,e=oe(n),r=e.scale,i=e.translate,u=e.clipExtent;return e.scale=function(){var n=r.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.translate=function(){var n=i.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.clipExtent=function(n){var o=u.apply(e,arguments);if(o===e){if(t=null==n){var a=Fo*r(),l=i();u([[l[0]-a,l[1]-a],[l[0]+a,l[1]+a]])}}else t&&(o=null);return o},e.clipExtent(null)}function Ae(n,t){return[Math.log(Math.tan(Fo/4+t/2)),-n]}function Ce(n){return n[0]}function ze(n){return n[1]}function Le(n){for(var t=n.length,e=[0,1],r=2,i=2;t>i;i++){for(;r>1&&Q(n[e[r-2]],n[e[r-1]],n[i])<=0;)--r;e[r++]=i}return e.slice(0,r)}function qe(n,t){return n[0]-t[0]||n[1]-t[1]}function Te(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Re(n,t,e,r){var i=n[0],u=e[0],o=t[0]-i,a=r[0]-u,l=n[1],c=e[1],f=t[1]-l,s=r[1]-c,h=(a*(l-c)-s*(i-u))/(s*o-a*f);return[i+h*o,l+h*f]}function De(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Pe(){rr(this),this.edge=this.site=this.circle=null}function Ue(n){var t=cl.pop()||new Pe;return t.site=n,t}function je(n){Be(n),ol.remove(n),cl.push(n),rr(n)}function Fe(n){var t=n.circle,e=t.x,r=t.cy,i={x:e,y:r},u=n.P,o=n.N,a=[n];je(n);for(var l=u;l.circle&&xo(e-l.circle.x)<Uo&&xo(r-l.circle.cy)<Uo;)u=l.P,a.unshift(l),je(l),l=u;a.unshift(l),Be(l);for(var c=o;c.circle&&xo(e-c.circle.x)<Uo&&xo(r-c.circle.cy)<Uo;)o=c.N,a.push(c),je(c),c=o;a.push(c),Be(c);var f,s=a.length;for(f=1;s>f;++f)c=a[f],l=a[f-1],nr(c.edge,l.site,c.site,i);l=a[0],c=a[s-1],c.edge=Ke(l.site,c.site,null,i),$e(l),$e(c)}function He(n){for(var t,e,r,i,u=n.x,o=n.y,a=ol._;a;)if(r=Oe(a,o)-u,r>Uo)a=a.L;else{if(i=u-Ie(a,o),!(i>Uo)){r>-Uo?(t=a.P,e=a):i>-Uo?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var l=Ue(n);if(ol.insert(t,l),t||e){if(t===e)return Be(t),e=Ue(t.site),ol.insert(l,e),l.edge=e.edge=Ke(t.site,l.site),$e(t),void $e(e);if(!e)return void(l.edge=Ke(t.site,l.site));Be(t),Be(e);var c=t.site,f=c.x,s=c.y,h=n.x-f,p=n.y-s,g=e.site,v=g.x-f,d=g.y-s,y=2*(h*d-p*v),m=h*h+p*p,M=v*v+d*d,x={x:(d*m-p*M)/y+f,y:(h*M-v*m)/y+s};nr(e.edge,c,g,x),l.edge=Ke(c,n,null,x),e.edge=Ke(n,g,null,x),$e(t),$e(e)}}function Oe(n,t){var e=n.site,r=e.x,i=e.y,u=i-t;if(!u)return r;var o=n.P;if(!o)return-(1/0);e=o.site;var a=e.x,l=e.y,c=l-t;if(!c)return a;var f=a-r,s=1/u-1/c,h=f/c;return s?(-h+Math.sqrt(h*h-2*s*(f*f/(-2*c)-l+c/2+i-u/2)))/s+r:(r+a)/2}function Ie(n,t){var e=n.N;if(e)return Oe(e,t);var r=n.site;return r.y===t?r.x:1/0}function Ye(n){this.site=n,this.edges=[]}function Ze(n){for(var t,e,r,i,u,o,a,l,c,f,s=n[0][0],h=n[1][0],p=n[0][1],g=n[1][1],v=ul,d=v.length;d--;)if(u=v[d],u&&u.prepare())for(a=u.edges,l=a.length,o=0;l>o;)f=a[o].end(),r=f.x,i=f.y,c=a[++o%l].start(),t=c.x,e=c.y,(xo(r-t)>Uo||xo(i-e)>Uo)&&(a.splice(o,0,new tr(Qe(u.site,f,xo(r-s)<Uo&&g-i>Uo?{x:s,y:xo(t-s)<Uo?e:g}:xo(i-g)<Uo&&h-r>Uo?{x:xo(e-g)<Uo?t:h,y:g}:xo(r-h)<Uo&&i-p>Uo?{x:h,y:xo(t-h)<Uo?e:p}:xo(i-p)<Uo&&r-s>Uo?{x:xo(e-p)<Uo?t:s,y:p}:null),u.site,null)),++l)}function Ve(n,t){return t.angle-n.angle}function Xe(){rr(this),this.x=this.y=this.arc=this.site=this.cy=null}function $e(n){var t=n.P,e=n.N;if(t&&e){var r=t.site,i=n.site,u=e.site;if(r!==u){var o=i.x,a=i.y,l=r.x-o,c=r.y-a,f=u.x-o,s=u.y-a,h=2*(l*s-c*f);if(!(h>=-jo)){var p=l*l+c*c,g=f*f+s*s,v=(s*p-c*g)/h,d=(l*g-f*p)/h,s=d+a,y=fl.pop()||new Xe;y.arc=n,y.site=i,y.x=v+o,y.y=s+Math.sqrt(v*v+d*d),y.cy=s,n.circle=y;for(var m=null,M=ll._;M;)if(y.y<M.y||y.y===M.y&&y.x<=M.x){if(!M.L){m=M.P;break}M=M.L}else{if(!M.R){m=M;break}M=M.R}ll.insert(m,y),m||(al=y)}}}}function Be(n){var t=n.circle;t&&(t.P||(al=t.N),ll.remove(t),fl.push(t),rr(t),n.circle=null)}function We(n){for(var t,e=il,r=Yt(n[0][0],n[0][1],n[1][0],n[1][1]),i=e.length;i--;)t=e[i],(!Je(t,n)||!r(t)||xo(t.a.x-t.b.x)<Uo&&xo(t.a.y-t.b.y)<Uo)&&(t.a=t.b=null,e.splice(i,1))}function Je(n,t){var e=n.b;if(e)return!0;var r,i,u=n.a,o=t[0][0],a=t[1][0],l=t[0][1],c=t[1][1],f=n.l,s=n.r,h=f.x,p=f.y,g=s.x,v=s.y,d=(h+g)/2,y=(p+v)/2;if(v===p){if(o>d||d>=a)return;if(h>g){if(u){if(u.y>=c)return}else u={x:d,y:l};e={x:d,y:c}}else{if(u){if(u.y<l)return}else u={x:d,y:c};e={x:d,y:l}}}else if(r=(h-g)/(v-p),i=y-r*d,-1>r||r>1)if(h>g){if(u){if(u.y>=c)return}else u={x:(l-i)/r,y:l};e={x:(c-i)/r,y:c}}else{if(u){if(u.y<l)return}else u={x:(c-i)/r,y:c};e={x:(l-i)/r,y:l}}else if(v>p){if(u){if(u.x>=a)return}else u={x:o,y:r*o+i};e={x:a,y:r*a+i}}else{if(u){if(u.x<o)return}else u={x:a,y:r*a+i};e={x:o,y:r*o+i}}return n.a=u,n.b=e,!0}function Ge(n,t){this.l=n,this.r=t,this.a=this.b=null}function Ke(n,t,e,r){var i=new Ge(n,t);return il.push(i),e&&nr(i,n,t,e),r&&nr(i,t,n,r),ul[n.i].edges.push(new tr(i,n,t)),ul[t.i].edges.push(new tr(i,t,n)),i}function Qe(n,t,e){var r=new Ge(n,null);return r.a=t,r.b=e,il.push(r),r}function nr(n,t,e,r){n.a||n.b?n.l===e?n.b=r:n.a=r:(n.a=r,n.l=t,n.r=e)}function tr(n,t,e){var r=n.a,i=n.b;this.edge=n,this.site=t,this.angle=e?Math.atan2(e.y-t.y,e.x-t.x):n.l===t?Math.atan2(i.x-r.x,r.y-i.y):Math.atan2(r.x-i.x,i.y-r.y)}function er(){this._=null}function rr(n){n.U=n.C=n.L=n.R=n.P=n.N=null}function ir(n,t){var e=t,r=t.R,i=e.U;i?i.L===e?i.L=r:i.R=r:n._=r,r.U=i,e.U=r,e.R=r.L,e.R&&(e.R.U=e),r.L=e}function ur(n,t){var e=t,r=t.L,i=e.U;i?i.L===e?i.L=r:i.R=r:n._=r,r.U=i,e.U=r,e.L=r.R,e.L&&(e.L.U=e),r.R=e}function or(n){for(;n.L;)n=n.L;return n}function ar(n,t){var e,r,i,u=n.sort(lr).pop();for(il=[],ul=new Array(n.length),ol=new er,ll=new er;;)if(i=al,u&&(!i||u.y<i.y||u.y===i.y&&u.x<i.x))u.x===e&&u.y===r||(ul[u.i]=new Ye(u),He(u),e=u.x,r=u.y),u=n.pop();else{if(!i)break;Fe(i.arc)}t&&(We(t),Ze(t));var o={cells:ul,edges:il};return ol=ll=il=ul=null,o}function lr(n,t){return t.y-n.y||t.x-n.x}function cr(n,t,e){return(n.x-e.x)*(t.y-n.y)-(n.x-t.x)*(e.y-n.y)}function fr(n){return n.x}function sr(n){return n.y}function hr(){return{leaf:!0,nodes:[],point:null,x:null,y:null}}function pr(n,t,e,r,i,u){if(!n(t,e,r,i,u)){var o=.5*(e+i),a=.5*(r+u),l=t.nodes;l[0]&&pr(n,l[0],e,r,o,a),l[1]&&pr(n,l[1],o,r,i,a),l[2]&&pr(n,l[2],e,a,o,u),l[3]&&pr(n,l[3],o,a,i,u)}}function gr(n,t,e,r,i,u,o){var a,l=1/0;return function c(n,f,s,h,p){if(!(f>u||s>o||r>h||i>p)){if(g=n.point){var g,v=t-n.x,d=e-n.y,y=v*v+d*d;if(l>y){var m=Math.sqrt(l=y);r=t-m,i=e-m,u=t+m,o=e+m,a=g}}for(var M=n.nodes,x=.5*(f+h),b=.5*(s+p),_=t>=x,w=e>=b,S=w<<1|_,k=S+4;k>S;++S)if(n=M[3&S])switch(3&S){case 0:c(n,f,s,x,b);break;case 1:c(n,x,s,h,b);break;case 2:c(n,f,b,x,p);break;case 3:c(n,x,b,h,p)}}}(n,r,i,u,o),a}function vr(n,t){n=ao.rgb(n),t=ao.rgb(t);var e=n.r,r=n.g,i=n.b,u=t.r-e,o=t.g-r,a=t.b-i;return function(n){return"#"+bn(Math.round(e+u*n))+bn(Math.round(r+o*n))+bn(Math.round(i+a*n))}}function dr(n,t){var e,r={},i={};for(e in n)e in t?r[e]=Mr(n[e],t[e]):i[e]=n[e];for(e in t)e in n||(i[e]=t[e]);return function(n){for(e in r)i[e]=r[e](n);return i}}function yr(n,t){return n=+n,t=+t,function(e){return n*(1-e)+t*e}}function mr(n,t){var e,r,i,u=hl.lastIndex=pl.lastIndex=0,o=-1,a=[],l=[];for(n+="",t+="";(e=hl.exec(n))&&(r=pl.exec(t));)(i=r.index)>u&&(i=t.slice(u,i),a[o]?a[o]+=i:a[++o]=i),(e=e[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,l.push({i:o,x:yr(e,r)})),u=pl.lastIndex;return u<t.length&&(i=t.slice(u),a[o]?a[o]+=i:a[++o]=i),a.length<2?l[0]?(t=l[0].x,function(n){return t(n)+""}):function(){return t}:(t=l.length,function(n){for(var e,r=0;t>r;++r)a[(e=l[r]).i]=e.x(n);return a.join("")})}function Mr(n,t){for(var e,r=ao.interpolators.length;--r>=0&&!(e=ao.interpolators[r](n,t)););return e}function xr(n,t){var e,r=[],i=[],u=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(Mr(n[e],t[e]));for(;u>e;++e)i[e]=n[e];for(;o>e;++e)i[e]=t[e];return function(n){for(e=0;a>e;++e)i[e]=r[e](n);return i}}function br(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function _r(n){return function(t){return 1-n(1-t)}}function wr(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function Sr(n){return n*n}function kr(n){return n*n*n}function Nr(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function Er(n){return function(t){return Math.pow(t,n)}}function Ar(n){return 1-Math.cos(n*Io)}function Cr(n){return Math.pow(2,10*(n-1))}function zr(n){return 1-Math.sqrt(1-n*n)}function Lr(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/Ho*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*Ho/t)}}function qr(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function Tr(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Rr(n,t){n=ao.hcl(n),t=ao.hcl(t);var e=n.h,r=n.c,i=n.l,u=t.h-e,o=t.c-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return sn(e+u*n,r+o*n,i+a*n)+""}}function Dr(n,t){n=ao.hsl(n),t=ao.hsl(t);var e=n.h,r=n.s,i=n.l,u=t.h-e,o=t.s-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return cn(e+u*n,r+o*n,i+a*n)+""}}function Pr(n,t){n=ao.lab(n),t=ao.lab(t);var e=n.l,r=n.a,i=n.b,u=t.l-e,o=t.a-r,a=t.b-i;return function(n){return pn(e+u*n,r+o*n,i+a*n)+""}}function Ur(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function jr(n){var t=[n.a,n.b],e=[n.c,n.d],r=Hr(t),i=Fr(t,e),u=Hr(Or(e,t,-i))||0;t[0]*e[1]<e[0]*t[1]&&(t[0]*=-1,t[1]*=-1,r*=-1,i*=-1),this.rotate=(r?Math.atan2(t[1],t[0]):Math.atan2(-e[0],e[1]))*Zo,this.translate=[n.e,n.f],this.scale=[r,u],this.skew=u?Math.atan2(i,u)*Zo:0}function Fr(n,t){return n[0]*t[0]+n[1]*t[1]}function Hr(n){var t=Math.sqrt(Fr(n,n));return t&&(n[0]/=t,n[1]/=t),t}function Or(n,t,e){return n[0]+=e*t[0],n[1]+=e*t[1],n}function Ir(n){return n.length?n.pop()+",":""}function Yr(n,t,e,r){if(n[0]!==t[0]||n[1]!==t[1]){var i=e.push("translate(",null,",",null,")");r.push({i:i-4,x:yr(n[0],t[0])},{i:i-2,x:yr(n[1],t[1])})}else(t[0]||t[1])&&e.push("translate("+t+")")}function Zr(n,t,e,r){n!==t?(n-t>180?t+=360:t-n>180&&(n+=360),r.push({i:e.push(Ir(e)+"rotate(",null,")")-2,x:yr(n,t)})):t&&e.push(Ir(e)+"rotate("+t+")")}function Vr(n,t,e,r){n!==t?r.push({i:e.push(Ir(e)+"skewX(",null,")")-2,x:yr(n,t)}):t&&e.push(Ir(e)+"skewX("+t+")")}function Xr(n,t,e,r){if(n[0]!==t[0]||n[1]!==t[1]){var i=e.push(Ir(e)+"scale(",null,",",null,")");r.push({i:i-4,x:yr(n[0],t[0])},{i:i-2,x:yr(n[1],t[1])})}else 1===t[0]&&1===t[1]||e.push(Ir(e)+"scale("+t+")")}function $r(n,t){var e=[],r=[];return n=ao.transform(n),t=ao.transform(t),Yr(n.translate,t.translate,e,r),Zr(n.rotate,t.rotate,e,r),Vr(n.skew,t.skew,e,r),Xr(n.scale,t.scale,e,r),n=t=null,function(n){for(var t,i=-1,u=r.length;++i<u;)e[(t=r[i]).i]=t.x(n);return e.join("")}}function Br(n,t){return t=(t-=n=+n)||1/t,function(e){return(e-n)/t}}function Wr(n,t){return t=(t-=n=+n)||1/t,function(e){return Math.max(0,Math.min(1,(e-n)/t))}}function Jr(n){for(var t=n.source,e=n.target,r=Kr(t,e),i=[t];t!==r;)t=t.parent,i.push(t);for(var u=i.length;e!==r;)i.splice(u,0,e),e=e.parent;return i}function Gr(n){for(var t=[],e=n.parent;null!=e;)t.push(n),n=e,e=e.parent;return t.push(n),t}function Kr(n,t){if(n===t)return n;for(var e=Gr(n),r=Gr(t),i=e.pop(),u=r.pop(),o=null;i===u;)o=i,i=e.pop(),u=r.pop();return o}function Qr(n){n.fixed|=2}function ni(n){n.fixed&=-7}function ti(n){n.fixed|=4,n.px=n.x,n.py=n.y}function ei(n){n.fixed&=-5}function ri(n,t,e){var r=0,i=0;if(n.charge=0,!n.leaf)for(var u,o=n.nodes,a=o.length,l=-1;++l<a;)u=o[l],null!=u&&(ri(u,t,e),n.charge+=u.charge,r+=u.charge*u.cx,i+=u.charge*u.cy);if(n.point){n.leaf||(n.point.x+=Math.random()-.5,n.point.y+=Math.random()-.5);var c=t*e[n.point.index];n.charge+=n.pointCharge=c,r+=c*n.point.x,i+=c*n.point.y}n.cx=r/n.charge,n.cy=i/n.charge}function ii(n,t){return ao.rebind(n,t,"sort","children","value"),n.nodes=n,n.links=fi,n}function ui(n,t){for(var e=[n];null!=(n=e.pop());)if(t(n),(i=n.children)&&(r=i.length))for(var r,i;--r>=0;)e.push(i[r])}function oi(n,t){for(var e=[n],r=[];null!=(n=e.pop());)if(r.push(n),(u=n.children)&&(i=u.length))for(var i,u,o=-1;++o<i;)e.push(u[o]);for(;null!=(n=r.pop());)t(n)}function ai(n){return n.children}function li(n){return n.value}function ci(n,t){return t.value-n.value}function fi(n){return ao.merge(n.map(function(n){return(n.children||[]).map(function(t){return{source:n,target:t}})}))}function si(n){return n.x}function hi(n){return n.y}function pi(n,t,e){n.y0=t,n.y=e}function gi(n){return ao.range(n.length)}function vi(n){for(var t=-1,e=n[0].length,r=[];++t<e;)r[t]=0;return r}function di(n){for(var t,e=1,r=0,i=n[0][1],u=n.length;u>e;++e)(t=n[e][1])>i&&(r=e,i=t);return r}function yi(n){return n.reduce(mi,0)}function mi(n,t){return n+t[1]}function Mi(n,t){return xi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function xi(n,t){for(var e=-1,r=+n[0],i=(n[1]-r)/t,u=[];++e<=t;)u[e]=i*e+r;return u}function bi(n){return[ao.min(n),ao.max(n)]}function _i(n,t){return n.value-t.value}function wi(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function Si(n,t){n._pack_next=t,t._pack_prev=n}function ki(n,t){var e=t.x-n.x,r=t.y-n.y,i=n.r+t.r;return.999*i*i>e*e+r*r}function Ni(n){function t(n){f=Math.min(n.x-n.r,f),s=Math.max(n.x+n.r,s),h=Math.min(n.y-n.r,h),p=Math.max(n.y+n.r,p)}if((e=n.children)&&(c=e.length)){var e,r,i,u,o,a,l,c,f=1/0,s=-(1/0),h=1/0,p=-(1/0);if(e.forEach(Ei),r=e[0],r.x=-r.r,r.y=0,t(r),c>1&&(i=e[1],i.x=i.r,i.y=0,t(i),c>2))for(u=e[2],zi(r,i,u),t(u),wi(r,u),r._pack_prev=u,wi(u,i),i=r._pack_next,o=3;c>o;o++){zi(r,i,u=e[o]);var g=0,v=1,d=1;for(a=i._pack_next;a!==i;a=a._pack_next,v++)if(ki(a,u)){g=1;break}if(1==g)for(l=r._pack_prev;l!==a._pack_prev&&!ki(l,u);l=l._pack_prev,d++);g?(d>v||v==d&&i.r<r.r?Si(r,i=a):Si(r=l,i),o--):(wi(r,u),i=u,t(u))}var y=(f+s)/2,m=(h+p)/2,M=0;for(o=0;c>o;o++)u=e[o],u.x-=y,u.y-=m,M=Math.max(M,u.r+Math.sqrt(u.x*u.x+u.y*u.y));n.r=M,e.forEach(Ai)}}function Ei(n){n._pack_next=n._pack_prev=n}function Ai(n){delete n._pack_next,delete n._pack_prev}function Ci(n,t,e,r){var i=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,i)for(var u=-1,o=i.length;++u<o;)Ci(i[u],t,e,r)}function zi(n,t,e){var r=n.r+e.r,i=t.x-n.x,u=t.y-n.y;if(r&&(i||u)){var o=t.r+e.r,a=i*i+u*u;o*=o,r*=r;var l=.5+(r-o)/(2*a),c=Math.sqrt(Math.max(0,2*o*(r+a)-(r-=a)*r-o*o))/(2*a);e.x=n.x+l*i+c*u,e.y=n.y+l*u-c*i}else e.x=n.x+r,e.y=n.y}function Li(n,t){return n.parent==t.parent?1:2}function qi(n){var t=n.children;return t.length?t[0]:n.t}function Ti(n){var t,e=n.children;return(t=e.length)?e[t-1]:n.t}function Ri(n,t,e){var r=e/(t.i-n.i);t.c-=r,t.s+=e,n.c+=r,t.z+=e,t.m+=e}function Di(n){for(var t,e=0,r=0,i=n.children,u=i.length;--u>=0;)t=i[u],t.z+=e,t.m+=e,e+=t.s+(r+=t.c)}function Pi(n,t,e){return n.a.parent===t.parent?n.a:e}function Ui(n){return 1+ao.max(n,function(n){return n.y})}function ji(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Fi(n){var t=n.children;return t&&t.length?Fi(t[0]):n}function Hi(n){var t,e=n.children;return e&&(t=e.length)?Hi(e[t-1]):n}function Oi(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Ii(n,t){var e=n.x+t[3],r=n.y+t[0],i=n.dx-t[1]-t[3],u=n.dy-t[0]-t[2];return 0>i&&(e+=i/2,i=0),0>u&&(r+=u/2,u=0),{x:e,y:r,dx:i,dy:u}}function Yi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Zi(n){return n.rangeExtent?n.rangeExtent():Yi(n.range())}function Vi(n,t,e,r){var i=e(n[0],n[1]),u=r(t[0],t[1]);return function(n){return u(i(n))}}function Xi(n,t){var e,r=0,i=n.length-1,u=n[r],o=n[i];return u>o&&(e=r,r=i,i=e,e=u,u=o,o=e),n[r]=t.floor(u),n[i]=t.ceil(o),n}function $i(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:Sl}function Bi(n,t,e,r){var i=[],u=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]<n[0]&&(n=n.slice().reverse(),t=t.slice().reverse());++o<=a;)i.push(e(n[o-1],n[o])),u.push(r(t[o-1],t[o]));return function(t){var e=ao.bisect(n,t,1,a)-1;return u[e](i[e](t))}}function Wi(n,t,e,r){function i(){var i=Math.min(n.length,t.length)>2?Bi:Vi,l=r?Wr:Br;return o=i(n,t,l,e),a=i(t,n,l,Mr),u}function u(n){return o(n)}var o,a;return u.invert=function(n){return a(n)},u.domain=function(t){return arguments.length?(n=t.map(Number),i()):n},u.range=function(n){return arguments.length?(t=n,i()):t},u.rangeRound=function(n){return u.range(n).interpolate(Ur)},u.clamp=function(n){return arguments.length?(r=n,i()):r},u.interpolate=function(n){return arguments.length?(e=n,i()):e},u.ticks=function(t){return Qi(n,t)},u.tickFormat=function(t,e){return nu(n,t,e)},u.nice=function(t){return Gi(n,t),i()},u.copy=function(){return Wi(n,t,e,r)},i()}function Ji(n,t){return ao.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Gi(n,t){return Xi(n,$i(Ki(n,t)[2])),Xi(n,$i(Ki(n,t)[2])),n}function Ki(n,t){null==t&&(t=10);var e=Yi(n),r=e[1]-e[0],i=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),u=t/r*i;return.15>=u?i*=10:.35>=u?i*=5:.75>=u&&(i*=2),e[0]=Math.ceil(e[0]/i)*i,e[1]=Math.floor(e[1]/i)*i+.5*i,e[2]=i,e}function Qi(n,t){return ao.range.apply(ao,Ki(n,t))}function nu(n,t,e){var r=Ki(n,t);if(e){var i=ha.exec(e);if(i.shift(),"s"===i[8]){var u=ao.formatPrefix(Math.max(xo(r[0]),xo(r[1])));return i[7]||(i[7]="."+tu(u.scale(r[2]))),i[8]="f",e=ao.format(i.join("")),function(n){return e(u.scale(n))+u.symbol}}i[7]||(i[7]="."+eu(i[8],r)),e=i.join("")}else e=",."+tu(r[2])+"f";return ao.format(e)}function tu(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function eu(n,t){var e=tu(t[2]);return n in kl?Math.abs(e-tu(Math.max(xo(t[0]),xo(t[1]))))+ +("e"!==n):e-2*("%"===n)}function ru(n,t,e,r){function i(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function u(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(i(t))}return o.invert=function(t){return u(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(i)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(i)),o):t},o.nice=function(){var t=Xi(r.map(i),e?Math:El);return n.domain(t),r=t.map(u),o},o.ticks=function(){var n=Yi(r),o=[],a=n[0],l=n[1],c=Math.floor(i(a)),f=Math.ceil(i(l)),s=t%1?2:t;if(isFinite(f-c)){if(e){for(;f>c;c++)for(var h=1;s>h;h++)o.push(u(c)*h);o.push(u(c))}else for(o.push(u(c));c++<f;)for(var h=s-1;h>0;h--)o.push(u(c)*h);for(c=0;o[c]<a;c++);for(f=o.length;o[f-1]>l;f--);o=o.slice(c,f)}return o},o.tickFormat=function(n,e){if(!arguments.length)return Nl;arguments.length<2?e=Nl:"function"!=typeof e&&(e=ao.format(e));var r=Math.max(1,t*n/o.ticks().length);return function(n){var o=n/u(Math.round(i(n)));return t-.5>o*t&&(o*=t),r>=o?e(n):""}},o.copy=function(){return ru(n.copy(),t,e,r)},Ji(o,n)}function iu(n,t,e){function r(t){return n(i(t))}var i=uu(t),u=uu(1/t);return r.invert=function(t){return u(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(i)),r):e},r.ticks=function(n){return Qi(e,n)},r.tickFormat=function(n,t){return nu(e,n,t)},r.nice=function(n){return r.domain(Gi(e,n))},r.exponent=function(o){return arguments.length?(i=uu(t=o),u=uu(1/t),n.domain(e.map(i)),r):t},r.copy=function(){return iu(n.copy(),t,e)},Ji(r,n)}function uu(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function ou(n,t){function e(e){return u[((i.get(e)||("range"===t.t?i.set(e,n.push(e)):NaN))-1)%u.length]}function r(t,e){return ao.range(n.length).map(function(n){return t+e*n})}var i,u,o;return e.domain=function(r){if(!arguments.length)return n;n=[],i=new c;for(var u,o=-1,a=r.length;++o<a;)i.has(u=r[o])||i.set(u,n.push(u));return e[t.t].apply(e,t.a)},e.range=function(n){return arguments.length?(u=n,o=0,t={t:"range",a:arguments},e):u},e.rangePoints=function(i,a){arguments.length<2&&(a=0);var l=i[0],c=i[1],f=n.length<2?(l=(l+c)/2,0):(c-l)/(n.length-1+a);return u=r(l+f*a/2,f),o=0,t={t:"rangePoints",a:arguments},e},e.rangeRoundPoints=function(i,a){arguments.length<2&&(a=0);var l=i[0],c=i[1],f=n.length<2?(l=c=Math.round((l+c)/2),0):(c-l)/(n.length-1+a)|0;return u=r(l+Math.round(f*a/2+(c-l-(n.length-1+a)*f)/2),f),o=0,t={t:"rangeRoundPoints",a:arguments},e},e.rangeBands=function(i,a,l){arguments.length<2&&(a=0),arguments.length<3&&(l=a);var c=i[1]<i[0],f=i[c-0],s=i[1-c],h=(s-f)/(n.length-a+2*l);return u=r(f+h*l,h),c&&u.reverse(),o=h*(1-a),t={t:"rangeBands",a:arguments},e},e.rangeRoundBands=function(i,a,l){arguments.length<2&&(a=0),arguments.length<3&&(l=a);var c=i[1]<i[0],f=i[c-0],s=i[1-c],h=Math.floor((s-f)/(n.length-a+2*l));return u=r(f+Math.round((s-f-(n.length-a)*h)/2),h),c&&u.reverse(),o=Math.round(h*(1-a)),t={t:"rangeRoundBands",a:arguments},e},e.rangeBand=function(){return o},e.rangeExtent=function(){return Yi(t.a[0])},e.copy=function(){return ou(n,t)},e.domain(n)}function au(n,t){function u(){var e=0,r=t.length;for(a=[];++e<r;)a[e-1]=ao.quantile(n,e/r);return o}function o(n){return isNaN(n=+n)?void 0:t[ao.bisect(a,n)]}var a;return o.domain=function(t){return arguments.length?(n=t.map(r).filter(i).sort(e),u()):n},o.range=function(n){return arguments.length?(t=n,u()):t},o.quantiles=function(){return a},o.invertExtent=function(e){return e=t.indexOf(e),0>e?[NaN,NaN]:[e>0?a[e-1]:n[0],e<a.length?a[e]:n[n.length-1]]},o.copy=function(){return au(n,t)},u()}function lu(n,t,e){function r(t){return e[Math.max(0,Math.min(o,Math.floor(u*(t-n))))]}function i(){return u=e.length/(t-n),o=e.length-1,r}var u,o;return r.domain=function(e){return arguments.length?(n=+e[0],t=+e[e.length-1],i()):[n,t]},r.range=function(n){return arguments.length?(e=n,i()):e},r.invertExtent=function(t){return t=e.indexOf(t),t=0>t?NaN:t/u+n,[t,t+1/u]},r.copy=function(){return lu(n,t,e)},i()}function cu(n,t){function e(e){return e>=e?t[ao.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return cu(n,t)},e}function fu(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Qi(n,t)},t.tickFormat=function(t,e){return nu(n,t,e)},t.copy=function(){return fu(n)},t}function su(){return 0}function hu(n){return n.innerRadius}function pu(n){return n.outerRadius}function gu(n){return n.startAngle}function vu(n){return n.endAngle}function du(n){return n&&n.padAngle}function yu(n,t,e,r){return(n-e)*t-(t-r)*n>0?0:1}function mu(n,t,e,r,i){var u=n[0]-t[0],o=n[1]-t[1],a=(i?r:-r)/Math.sqrt(u*u+o*o),l=a*o,c=-a*u,f=n[0]+l,s=n[1]+c,h=t[0]+l,p=t[1]+c,g=(f+h)/2,v=(s+p)/2,d=h-f,y=p-s,m=d*d+y*y,M=e-r,x=f*p-h*s,b=(0>y?-1:1)*Math.sqrt(Math.max(0,M*M*m-x*x)),_=(x*y-d*b)/m,w=(-x*d-y*b)/m,S=(x*y+d*b)/m,k=(-x*d+y*b)/m,N=_-g,E=w-v,A=S-g,C=k-v;return N*N+E*E>A*A+C*C&&(_=S,w=k),[[_-l,w-c],[_*e/M,w*e/M]]}function Mu(n){function t(t){function o(){c.push("M",u(n(f),a))}for(var l,c=[],f=[],s=-1,h=t.length,p=En(e),g=En(r);++s<h;)i.call(this,l=t[s],s)?f.push([+p.call(this,l,s),+g.call(this,l,s)]):f.length&&(o(),f=[]);return f.length&&o(),c.length?c.join(""):null}var e=Ce,r=ze,i=zt,u=xu,o=u.key,a=.7;return t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t.defined=function(n){return arguments.length?(i=n,t):i},t.interpolate=function(n){return arguments.length?(o="function"==typeof n?u=n:(u=Tl.get(n)||xu).key,t):o},t.tension=function(n){return arguments.length?(a=n,t):a},t}function xu(n){return n.length>1?n.join("L"):n+"Z"}function bu(n){return n.join("L")+"Z"}function _u(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("H",(r[0]+(r=n[t])[0])/2,"V",r[1]);return e>1&&i.push("H",r[0]),i.join("")}function wu(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("V",(r=n[t])[1],"H",r[0]);return i.join("")}function Su(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("H",(r=n[t])[0],"V",r[1]);return i.join("")}function ku(n,t){return n.length<4?xu(n):n[1]+Au(n.slice(1,-1),Cu(n,t))}function Nu(n,t){return n.length<3?bu(n):n[0]+Au((n.push(n[0]),n),Cu([n[n.length-2]].concat(n,[n[1]]),t))}function Eu(n,t){return n.length<3?xu(n):n[0]+Au(n,Cu(n,t))}function Au(n,t){if(t.length<1||n.length!=t.length&&n.length!=t.length+2)return xu(n);var e=n.length!=t.length,r="",i=n[0],u=n[1],o=t[0],a=o,l=1;if(e&&(r+="Q"+(u[0]-2*o[0]/3)+","+(u[1]-2*o[1]/3)+","+u[0]+","+u[1],i=n[1],l=2),t.length>1){a=t[1],u=n[l],l++,r+="C"+(i[0]+o[0])+","+(i[1]+o[1])+","+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1];for(var c=2;c<t.length;c++,l++)u=n[l],a=t[c],r+="S"+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1]}if(e){var f=n[l];r+="Q"+(u[0]+2*a[0]/3)+","+(u[1]+2*a[1]/3)+","+f[0]+","+f[1]}return r}function Cu(n,t){for(var e,r=[],i=(1-t)/2,u=n[0],o=n[1],a=1,l=n.length;++a<l;)e=u,u=o,o=n[a],r.push([i*(o[0]-e[0]),i*(o[1]-e[1])]);return r}function zu(n){if(n.length<3)return xu(n);var t=1,e=n.length,r=n[0],i=r[0],u=r[1],o=[i,i,i,(r=n[1])[0]],a=[u,u,u,r[1]],l=[i,",",u,"L",Ru(Pl,o),",",Ru(Pl,a)];for(n.push(n[e-1]);++t<=e;)r=n[t],o.shift(),o.push(r[0]),a.shift(),a.push(r[1]),Du(l,o,a);return n.pop(),l.push("L",r),l.join("")}function Lu(n){if(n.length<4)return xu(n);for(var t,e=[],r=-1,i=n.length,u=[0],o=[0];++r<3;)t=n[r],u.push(t[0]),o.push(t[1]);for(e.push(Ru(Pl,u)+","+Ru(Pl,o)),--r;++r<i;)t=n[r],u.shift(),u.push(t[0]),o.shift(),o.push(t[1]),Du(e,u,o);return e.join("")}function qu(n){for(var t,e,r=-1,i=n.length,u=i+4,o=[],a=[];++r<4;)e=n[r%i],o.push(e[0]),a.push(e[1]);for(t=[Ru(Pl,o),",",Ru(Pl,a)],--r;++r<u;)e=n[r%i],o.shift(),o.push(e[0]),a.shift(),a.push(e[1]),Du(t,o,a);return t.join("")}function Tu(n,t){var e=n.length-1;if(e)for(var r,i,u=n[0][0],o=n[0][1],a=n[e][0]-u,l=n[e][1]-o,c=-1;++c<=e;)r=n[c],i=c/e,r[0]=t*r[0]+(1-t)*(u+i*a),r[1]=t*r[1]+(1-t)*(o+i*l);return zu(n)}function Ru(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]+n[3]*t[3]}function Du(n,t,e){n.push("C",Ru(Rl,t),",",Ru(Rl,e),",",Ru(Dl,t),",",Ru(Dl,e),",",Ru(Pl,t),",",Ru(Pl,e))}function Pu(n,t){return(t[1]-n[1])/(t[0]-n[0])}function Uu(n){for(var t=0,e=n.length-1,r=[],i=n[0],u=n[1],o=r[0]=Pu(i,u);++t<e;)r[t]=(o+(o=Pu(i=u,u=n[t+1])))/2;return r[t]=o,r}function ju(n){for(var t,e,r,i,u=[],o=Uu(n),a=-1,l=n.length-1;++a<l;)t=Pu(n[a],n[a+1]),xo(t)<Uo?o[a]=o[a+1]=0:(e=o[a]/t,r=o[a+1]/t,i=e*e+r*r,i>9&&(i=3*t/Math.sqrt(i),o[a]=i*e,o[a+1]=i*r));for(a=-1;++a<=l;)i=(n[Math.min(l,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),u.push([i||0,o[a]*i||0]);return u}function Fu(n){return n.length<3?xu(n):n[0]+Au(n,ju(n))}function Hu(n){for(var t,e,r,i=-1,u=n.length;++i<u;)t=n[i],e=t[0],r=t[1]-Io,t[0]=e*Math.cos(r),t[1]=e*Math.sin(r);return n}function Ou(n){function t(t){function l(){v.push("M",a(n(y),s),f,c(n(d.reverse()),s),"Z")}for(var h,p,g,v=[],d=[],y=[],m=-1,M=t.length,x=En(e),b=En(i),_=e===r?function(){
			return p}:En(r),w=i===u?function(){return g}:En(u);++m<M;)o.call(this,h=t[m],m)?(d.push([p=+x.call(this,h,m),g=+b.call(this,h,m)]),y.push([+_.call(this,h,m),+w.call(this,h,m)])):d.length&&(l(),d=[],y=[]);return d.length&&l(),v.length?v.join(""):null}var e=Ce,r=Ce,i=0,u=ze,o=zt,a=xu,l=a.key,c=a,f="L",s=.7;return t.x=function(n){return arguments.length?(e=r=n,t):r},t.x0=function(n){return arguments.length?(e=n,t):e},t.x1=function(n){return arguments.length?(r=n,t):r},t.y=function(n){return arguments.length?(i=u=n,t):u},t.y0=function(n){return arguments.length?(i=n,t):i},t.y1=function(n){return arguments.length?(u=n,t):u},t.defined=function(n){return arguments.length?(o=n,t):o},t.interpolate=function(n){return arguments.length?(l="function"==typeof n?a=n:(a=Tl.get(n)||xu).key,c=a.reverse||a,f=a.closed?"M":"L",t):l},t.tension=function(n){return arguments.length?(s=n,t):s},t}function Iu(n){return n.radius}function Yu(n){return[n.x,n.y]}function Zu(n){return function(){var t=n.apply(this,arguments),e=t[0],r=t[1]-Io;return[e*Math.cos(r),e*Math.sin(r)]}}function Vu(){return 64}function Xu(){return"circle"}function $u(n){var t=Math.sqrt(n/Fo);return"M0,"+t+"A"+t+","+t+" 0 1,1 0,"+-t+"A"+t+","+t+" 0 1,1 0,"+t+"Z"}function Bu(n){return function(){var t,e,r;(t=this[n])&&(r=t[e=t.active])&&(r.timer.c=null,r.timer.t=NaN,--t.count?delete t[e]:delete this[n],t.active+=.5,r.event&&r.event.interrupt.call(this,this.__data__,r.index))}}function Wu(n,t,e){return ko(n,Yl),n.namespace=t,n.id=e,n}function Ju(n,t,e,r){var i=n.id,u=n.namespace;return Y(n,"function"==typeof e?function(n,o,a){n[u][i].tween.set(t,r(e.call(n,n.__data__,o,a)))}:(e=r(e),function(n){n[u][i].tween.set(t,e)}))}function Gu(n){return null==n&&(n=""),function(){this.textContent=n}}function Ku(n){return null==n?"__transition__":"__transition_"+n+"__"}function Qu(n,t,e,r,i){function u(n){var t=v.delay;return f.t=t+l,n>=t?o(n-t):void(f.c=o)}function o(e){var i=g.active,u=g[i];u&&(u.timer.c=null,u.timer.t=NaN,--g.count,delete g[i],u.event&&u.event.interrupt.call(n,n.__data__,u.index));for(var o in g)if(r>+o){var c=g[o];c.timer.c=null,c.timer.t=NaN,--g.count,delete g[o]}f.c=a,qn(function(){return f.c&&a(e||1)&&(f.c=null,f.t=NaN),1},0,l),g.active=r,v.event&&v.event.start.call(n,n.__data__,t),p=[],v.tween.forEach(function(e,r){(r=r.call(n,n.__data__,t))&&p.push(r)}),h=v.ease,s=v.duration}function a(i){for(var u=i/s,o=h(u),a=p.length;a>0;)p[--a].call(n,o);return u>=1?(v.event&&v.event.end.call(n,n.__data__,t),--g.count?delete g[r]:delete n[e],1):void 0}var l,f,s,h,p,g=n[e]||(n[e]={active:0,count:0}),v=g[r];v||(l=i.time,f=qn(u,0,l),v=g[r]={tween:new c,time:l,timer:f,delay:i.delay,duration:i.duration,ease:i.ease,index:t},i=null,++g.count)}function no(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate("+(isFinite(r)?r:e(n))+",0)"})}function to(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate(0,"+(isFinite(r)?r:e(n))+")"})}function eo(n){return n.toISOString()}function ro(n,t,e){function r(t){return n(t)}function i(n,e){var r=n[1]-n[0],i=r/e,u=ao.bisect(Kl,i);return u==Kl.length?[t.year,Ki(n.map(function(n){return n/31536e6}),e)[2]]:u?t[i/Kl[u-1]<Kl[u]/i?u-1:u]:[tc,Ki(n,e)[2]]}return r.invert=function(t){return io(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain(t),r):n.domain().map(io)},r.nice=function(n,t){function e(e){return!isNaN(e)&&!n.range(e,io(+e+1),t).length}var u=r.domain(),o=Yi(u),a=null==n?i(o,10):"number"==typeof n&&i(o,n);return a&&(n=a[0],t=a[1]),r.domain(Xi(u,t>1?{floor:function(t){for(;e(t=n.floor(t));)t=io(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=io(+t+1);return t}}:n))},r.ticks=function(n,t){var e=Yi(r.domain()),u=null==n?i(e,10):"number"==typeof n?i(e,n):!n.range&&[{range:n},t];return u&&(n=u[0],t=u[1]),n.range(e[0],io(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return ro(n.copy(),t,e)},Ji(r,n)}function io(n){return new Date(n)}function uo(n){return JSON.parse(n.responseText)}function oo(n){var t=fo.createRange();return t.selectNode(fo.body),t.createContextualFragment(n.responseText)}var ao={version:"3.5.17"},lo=[].slice,co=function(n){return lo.call(n)},fo=this.document;if(fo)try{co(fo.documentElement.childNodes)[0].nodeType}catch(so){co=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}if(Date.now||(Date.now=function(){return+new Date}),fo)try{fo.createElement("DIV").style.setProperty("opacity",0,"")}catch(ho){var po=this.Element.prototype,go=po.setAttribute,vo=po.setAttributeNS,yo=this.CSSStyleDeclaration.prototype,mo=yo.setProperty;po.setAttribute=function(n,t){go.call(this,n,t+"")},po.setAttributeNS=function(n,t,e){vo.call(this,n,t,e+"")},yo.setProperty=function(n,t,e){mo.call(this,n,t+"",e)}}ao.ascending=e,ao.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:NaN},ao.min=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i<u;)if(null!=(r=n[i])&&r>=r){e=r;break}for(;++i<u;)null!=(r=n[i])&&e>r&&(e=r)}else{for(;++i<u;)if(null!=(r=t.call(n,n[i],i))&&r>=r){e=r;break}for(;++i<u;)null!=(r=t.call(n,n[i],i))&&e>r&&(e=r)}return e},ao.max=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i<u;)if(null!=(r=n[i])&&r>=r){e=r;break}for(;++i<u;)null!=(r=n[i])&&r>e&&(e=r)}else{for(;++i<u;)if(null!=(r=t.call(n,n[i],i))&&r>=r){e=r;break}for(;++i<u;)null!=(r=t.call(n,n[i],i))&&r>e&&(e=r)}return e},ao.extent=function(n,t){var e,r,i,u=-1,o=n.length;if(1===arguments.length){for(;++u<o;)if(null!=(r=n[u])&&r>=r){e=i=r;break}for(;++u<o;)null!=(r=n[u])&&(e>r&&(e=r),r>i&&(i=r))}else{for(;++u<o;)if(null!=(r=t.call(n,n[u],u))&&r>=r){e=i=r;break}for(;++u<o;)null!=(r=t.call(n,n[u],u))&&(e>r&&(e=r),r>i&&(i=r))}return[e,i]},ao.sum=function(n,t){var e,r=0,u=n.length,o=-1;if(1===arguments.length)for(;++o<u;)i(e=+n[o])&&(r+=e);else for(;++o<u;)i(e=+t.call(n,n[o],o))&&(r+=e);return r},ao.mean=function(n,t){var e,u=0,o=n.length,a=-1,l=o;if(1===arguments.length)for(;++a<o;)i(e=r(n[a]))?u+=e:--l;else for(;++a<o;)i(e=r(t.call(n,n[a],a)))?u+=e:--l;return l?u/l:void 0},ao.quantile=function(n,t){var e=(n.length-1)*t+1,r=Math.floor(e),i=+n[r-1],u=e-r;return u?i+u*(n[r]-i):i},ao.median=function(n,t){var u,o=[],a=n.length,l=-1;if(1===arguments.length)for(;++l<a;)i(u=r(n[l]))&&o.push(u);else for(;++l<a;)i(u=r(t.call(n,n[l],l)))&&o.push(u);return o.length?ao.quantile(o.sort(e),.5):void 0},ao.variance=function(n,t){var e,u,o=n.length,a=0,l=0,c=-1,f=0;if(1===arguments.length)for(;++c<o;)i(e=r(n[c]))&&(u=e-a,a+=u/++f,l+=u*(e-a));else for(;++c<o;)i(e=r(t.call(n,n[c],c)))&&(u=e-a,a+=u/++f,l+=u*(e-a));return f>1?l/(f-1):void 0},ao.deviation=function(){var n=ao.variance.apply(this,arguments);return n?Math.sqrt(n):n};var Mo=u(e);ao.bisectLeft=Mo.left,ao.bisect=ao.bisectRight=Mo.right,ao.bisector=function(n){return u(1===n.length?function(t,r){return e(n(t),r)}:n)},ao.shuffle=function(n,t,e){(u=arguments.length)<3&&(e=n.length,2>u&&(t=0));for(var r,i,u=e-t;u;)i=Math.random()*u--|0,r=n[u+t],n[u+t]=n[i+t],n[i+t]=r;return n},ao.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},ao.pairs=function(n){for(var t,e=0,r=n.length-1,i=n[0],u=new Array(0>r?0:r);r>e;)u[e]=[t=i,i=n[++e]];return u},ao.transpose=function(n){if(!(i=n.length))return[];for(var t=-1,e=ao.min(n,o),r=new Array(e);++t<e;)for(var i,u=-1,a=r[t]=new Array(i);++u<i;)a[u]=n[u][t];return r},ao.zip=function(){return ao.transpose(arguments)},ao.keys=function(n){var t=[];for(var e in n)t.push(e);return t},ao.values=function(n){var t=[];for(var e in n)t.push(n[e]);return t},ao.entries=function(n){var t=[];for(var e in n)t.push({key:e,value:n[e]});return t},ao.merge=function(n){for(var t,e,r,i=n.length,u=-1,o=0;++u<i;)o+=n[u].length;for(e=new Array(o);--i>=0;)for(r=n[i],t=r.length;--t>=0;)e[--o]=r[t];return e};var xo=Math.abs;ao.range=function(n,t,e){if(arguments.length<3&&(e=1,arguments.length<2&&(t=n,n=0)),(t-n)/e===1/0)throw new Error("infinite range");var r,i=[],u=a(xo(e)),o=-1;if(n*=u,t*=u,e*=u,0>e)for(;(r=n+e*++o)>t;)i.push(r/u);else for(;(r=n+e*++o)<t;)i.push(r/u);return i},ao.map=function(n,t){var e=new c;if(n instanceof c)n.forEach(function(n,t){e.set(n,t)});else if(Array.isArray(n)){var r,i=-1,u=n.length;if(1===arguments.length)for(;++i<u;)e.set(i,n[i]);else for(;++i<u;)e.set(t.call(n,r=n[i],i),r)}else for(var o in n)e.set(o,n[o]);return e};var bo="__proto__",_o="\x00";l(c,{has:h,get:function(n){return this._[f(n)]},set:function(n,t){return this._[f(n)]=t},remove:p,keys:g,values:function(){var n=[];for(var t in this._)n.push(this._[t]);return n},entries:function(){var n=[];for(var t in this._)n.push({key:s(t),value:this._[t]});return n},size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,s(t),this._[t])}}),ao.nest=function(){function n(t,o,a){if(a>=u.length)return r?r.call(i,o):e?o.sort(e):o;for(var l,f,s,h,p=-1,g=o.length,v=u[a++],d=new c;++p<g;)(h=d.get(l=v(f=o[p])))?h.push(f):d.set(l,[f]);return t?(f=t(),s=function(e,r){f.set(e,n(t,r,a))}):(f={},s=function(e,r){f[e]=n(t,r,a)}),d.forEach(s),f}function t(n,e){if(e>=u.length)return n;var r=[],i=o[e++];return n.forEach(function(n,i){r.push({key:n,values:t(i,e)})}),i?r.sort(function(n,t){return i(n.key,t.key)}):r}var e,r,i={},u=[],o=[];return i.map=function(t,e){return n(e,t,0)},i.entries=function(e){return t(n(ao.map,e,0),0)},i.key=function(n){return u.push(n),i},i.sortKeys=function(n){return o[u.length-1]=n,i},i.sortValues=function(n){return e=n,i},i.rollup=function(n){return r=n,i},i},ao.set=function(n){var t=new y;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},l(y,{has:h,add:function(n){return this._[f(n+="")]=!0,n},remove:p,values:g,size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,s(t))}}),ao.behavior={},ao.rebind=function(n,t){for(var e,r=1,i=arguments.length;++r<i;)n[e=arguments[r]]=M(n,t,t[e]);return n};var wo=["webkit","ms","moz","Moz","o","O"];ao.dispatch=function(){for(var n=new _,t=-1,e=arguments.length;++t<e;)n[arguments[t]]=w(n);return n},_.prototype.on=function(n,t){var e=n.indexOf("."),r="";if(e>=0&&(r=n.slice(e+1),n=n.slice(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},ao.event=null,ao.requote=function(n){return n.replace(So,"\\$&")};var So=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ko={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},No=function(n,t){return t.querySelector(n)},Eo=function(n,t){return t.querySelectorAll(n)},Ao=function(n,t){var e=n.matches||n[x(n,"matchesSelector")];return(Ao=function(n,t){return e.call(n,t)})(n,t)};"function"==typeof Sizzle&&(No=function(n,t){return Sizzle(n,t)[0]||null},Eo=Sizzle,Ao=Sizzle.matchesSelector),ao.selection=function(){return ao.select(fo.documentElement)};var Co=ao.selection.prototype=[];Co.select=function(n){var t,e,r,i,u=[];n=A(n);for(var o=-1,a=this.length;++o<a;){u.push(t=[]),t.parentNode=(r=this[o]).parentNode;for(var l=-1,c=r.length;++l<c;)(i=r[l])?(t.push(e=n.call(i,i.__data__,l,o)),e&&"__data__"in i&&(e.__data__=i.__data__)):t.push(null)}return E(u)},Co.selectAll=function(n){var t,e,r=[];n=C(n);for(var i=-1,u=this.length;++i<u;)for(var o=this[i],a=-1,l=o.length;++a<l;)(e=o[a])&&(r.push(t=co(n.call(e,e.__data__,a,i))),t.parentNode=e);return E(r)};var zo="http://www.w3.org/1999/xhtml",Lo={svg:"http://www.w3.org/2000/svg",xhtml:zo,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};ao.ns={prefix:Lo,qualify:function(n){var t=n.indexOf(":"),e=n;return t>=0&&"xmlns"!==(e=n.slice(0,t))&&(n=n.slice(t+1)),Lo.hasOwnProperty(e)?{space:Lo[e],local:n}:n}},Co.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=ao.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(z(t,n[t]));return this}return this.each(z(n,t))},Co.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=T(n)).length,i=-1;if(t=e.classList){for(;++i<r;)if(!t.contains(n[i]))return!1}else for(t=e.getAttribute("class");++i<r;)if(!q(n[i]).test(t))return!1;return!0}for(t in n)this.each(R(t,n[t]));return this}return this.each(R(n,t))},Co.style=function(n,e,r){var i=arguments.length;if(3>i){if("string"!=typeof n){2>i&&(e="");for(r in n)this.each(P(r,n[r],e));return this}if(2>i){var u=this.node();return t(u).getComputedStyle(u,null).getPropertyValue(n)}r=""}return this.each(P(n,e,r))},Co.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(U(t,n[t]));return this}return this.each(U(n,t))},Co.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},Co.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},Co.append=function(n){return n=j(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},Co.insert=function(n,t){return n=j(n),t=A(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},Co.remove=function(){return this.each(F)},Co.data=function(n,t){function e(n,e){var r,i,u,o=n.length,s=e.length,h=Math.min(o,s),p=new Array(s),g=new Array(s),v=new Array(o);if(t){var d,y=new c,m=new Array(o);for(r=-1;++r<o;)(i=n[r])&&(y.has(d=t.call(i,i.__data__,r))?v[r]=i:y.set(d,i),m[r]=d);for(r=-1;++r<s;)(i=y.get(d=t.call(e,u=e[r],r)))?i!==!0&&(p[r]=i,i.__data__=u):g[r]=H(u),y.set(d,!0);for(r=-1;++r<o;)r in m&&y.get(m[r])!==!0&&(v[r]=n[r])}else{for(r=-1;++r<h;)i=n[r],u=e[r],i?(i.__data__=u,p[r]=i):g[r]=H(u);for(;s>r;++r)g[r]=H(e[r]);for(;o>r;++r)v[r]=n[r]}g.update=p,g.parentNode=p.parentNode=v.parentNode=n.parentNode,a.push(g),l.push(p),f.push(v)}var r,i,u=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(r=this[0]).length);++u<o;)(i=r[u])&&(n[u]=i.__data__);return n}var a=Z([]),l=E([]),f=E([]);if("function"==typeof n)for(;++u<o;)e(r=this[u],n.call(r,r.parentNode.__data__,u));else for(;++u<o;)e(r=this[u],n);return l.enter=function(){return a},l.exit=function(){return f},l},Co.datum=function(n){return arguments.length?this.property("__data__",n):this.property("__data__")},Co.filter=function(n){var t,e,r,i=[];"function"!=typeof n&&(n=O(n));for(var u=0,o=this.length;o>u;u++){i.push(t=[]),t.parentNode=(e=this[u]).parentNode;for(var a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return E(i)},Co.order=function(){for(var n=-1,t=this.length;++n<t;)for(var e,r=this[n],i=r.length-1,u=r[i];--i>=0;)(e=r[i])&&(u&&u!==e.nextSibling&&u.parentNode.insertBefore(e,u),u=e);return this},Co.sort=function(n){n=I.apply(this,arguments);for(var t=-1,e=this.length;++t<e;)this[t].sort(n);return this.order()},Co.each=function(n){return Y(this,function(t,e,r){n.call(t,t.__data__,e,r)})},Co.call=function(n){var t=co(arguments);return n.apply(t[0]=this,t),this},Co.empty=function(){return!this.node()},Co.node=function(){for(var n=0,t=this.length;t>n;n++)for(var e=this[n],r=0,i=e.length;i>r;r++){var u=e[r];if(u)return u}return null},Co.size=function(){var n=0;return Y(this,function(){++n}),n};var qo=[];ao.selection.enter=Z,ao.selection.enter.prototype=qo,qo.append=Co.append,qo.empty=Co.empty,qo.node=Co.node,qo.call=Co.call,qo.size=Co.size,qo.select=function(n){for(var t,e,r,i,u,o=[],a=-1,l=this.length;++a<l;){r=(i=this[a]).update,o.push(t=[]),t.parentNode=i.parentNode;for(var c=-1,f=i.length;++c<f;)(u=i[c])?(t.push(r[c]=e=n.call(i.parentNode,u.__data__,c,a)),e.__data__=u.__data__):t.push(null)}return E(o)},qo.insert=function(n,t){return arguments.length<2&&(t=V(this)),Co.insert.call(this,n,t)},ao.select=function(t){var e;return"string"==typeof t?(e=[No(t,fo)],e.parentNode=fo.documentElement):(e=[t],e.parentNode=n(t)),E([e])},ao.selectAll=function(n){var t;return"string"==typeof n?(t=co(Eo(n,fo)),t.parentNode=fo.documentElement):(t=co(n),t.parentNode=null),E([t])},Co.on=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(X(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(X(n,t,e))};var To=ao.map({mouseenter:"mouseover",mouseleave:"mouseout"});fo&&To.forEach(function(n){"on"+n in fo&&To.remove(n)});var Ro,Do=0;ao.mouse=function(n){return J(n,k())};var Po=this.navigator&&/WebKit/.test(this.navigator.userAgent)?-1:0;ao.touch=function(n,t,e){if(arguments.length<3&&(e=t,t=k().changedTouches),t)for(var r,i=0,u=t.length;u>i;++i)if((r=t[i]).identifier===e)return J(n,r)},ao.behavior.drag=function(){function n(){this.on("mousedown.drag",u).on("touchstart.drag",o)}function e(n,t,e,u,o){return function(){function a(){var n,e,r=t(h,v);r&&(n=r[0]-M[0],e=r[1]-M[1],g|=n|e,M=r,p({type:"drag",x:r[0]+c[0],y:r[1]+c[1],dx:n,dy:e}))}function l(){t(h,v)&&(y.on(u+d,null).on(o+d,null),m(g),p({type:"dragend"}))}var c,f=this,s=ao.event.target.correspondingElement||ao.event.target,h=f.parentNode,p=r.of(f,arguments),g=0,v=n(),d=".drag"+(null==v?"":"-"+v),y=ao.select(e(s)).on(u+d,a).on(o+d,l),m=W(s),M=t(h,v);i?(c=i.apply(f,arguments),c=[c.x-M[0],c.y-M[1]]):c=[0,0],p({type:"dragstart"})}}var r=N(n,"drag","dragstart","dragend"),i=null,u=e(b,ao.mouse,t,"mousemove","mouseup"),o=e(G,ao.touch,m,"touchmove","touchend");return n.origin=function(t){return arguments.length?(i=t,n):i},ao.rebind(n,r,"on")},ao.touches=function(n,t){return arguments.length<2&&(t=k().touches),t?co(t).map(function(t){var e=J(n,t);return e.identifier=t.identifier,e}):[]};var Uo=1e-6,jo=Uo*Uo,Fo=Math.PI,Ho=2*Fo,Oo=Ho-Uo,Io=Fo/2,Yo=Fo/180,Zo=180/Fo,Vo=Math.SQRT2,Xo=2,$o=4;ao.interpolateZoom=function(n,t){var e,r,i=n[0],u=n[1],o=n[2],a=t[0],l=t[1],c=t[2],f=a-i,s=l-u,h=f*f+s*s;if(jo>h)r=Math.log(c/o)/Vo,e=function(n){return[i+n*f,u+n*s,o*Math.exp(Vo*n*r)]};else{var p=Math.sqrt(h),g=(c*c-o*o+$o*h)/(2*o*Xo*p),v=(c*c-o*o-$o*h)/(2*c*Xo*p),d=Math.log(Math.sqrt(g*g+1)-g),y=Math.log(Math.sqrt(v*v+1)-v);r=(y-d)/Vo,e=function(n){var t=n*r,e=rn(d),a=o/(Xo*p)*(e*un(Vo*t+d)-en(d));return[i+a*f,u+a*s,o*e/rn(Vo*t+d)]}}return e.duration=1e3*r,e},ao.behavior.zoom=function(){function n(n){n.on(L,s).on(Wo+".zoom",p).on("dblclick.zoom",g).on(R,h)}function e(n){return[(n[0]-k.x)/k.k,(n[1]-k.y)/k.k]}function r(n){return[n[0]*k.k+k.x,n[1]*k.k+k.y]}function i(n){k.k=Math.max(A[0],Math.min(A[1],n))}function u(n,t){t=r(t),k.x+=n[0]-t[0],k.y+=n[1]-t[1]}function o(t,e,r,o){t.__chart__={x:k.x,y:k.y,k:k.k},i(Math.pow(2,o)),u(d=e,r),t=ao.select(t),C>0&&(t=t.transition().duration(C)),t.call(n.event)}function a(){b&&b.domain(x.range().map(function(n){return(n-k.x)/k.k}).map(x.invert)),w&&w.domain(_.range().map(function(n){return(n-k.y)/k.k}).map(_.invert))}function l(n){z++||n({type:"zoomstart"})}function c(n){a(),n({type:"zoom",scale:k.k,translate:[k.x,k.y]})}function f(n){--z||(n({type:"zoomend"}),d=null)}function s(){function n(){a=1,u(ao.mouse(i),h),c(o)}function r(){s.on(q,null).on(T,null),p(a),f(o)}var i=this,o=D.of(i,arguments),a=0,s=ao.select(t(i)).on(q,n).on(T,r),h=e(ao.mouse(i)),p=W(i);Il.call(i),l(o)}function h(){function n(){var n=ao.touches(g);return p=k.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=e(n))}),n}function t(){var t=ao.event.target;ao.select(t).on(x,r).on(b,a),_.push(t);for(var e=ao.event.changedTouches,i=0,u=e.length;u>i;++i)d[e[i].identifier]=null;var l=n(),c=Date.now();if(1===l.length){if(500>c-M){var f=l[0];o(g,f,d[f.identifier],Math.floor(Math.log(k.k)/Math.LN2)+1),S()}M=c}else if(l.length>1){var f=l[0],s=l[1],h=f[0]-s[0],p=f[1]-s[1];y=h*h+p*p}}function r(){var n,t,e,r,o=ao.touches(g);Il.call(g);for(var a=0,l=o.length;l>a;++a,r=null)if(e=o[a],r=d[e.identifier]){if(t)break;n=e,t=r}if(r){var f=(f=e[0]-n[0])*f+(f=e[1]-n[1])*f,s=y&&Math.sqrt(f/y);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+r[0])/2,(t[1]+r[1])/2],i(s*p)}M=null,u(n,t),c(v)}function a(){if(ao.event.touches.length){for(var t=ao.event.changedTouches,e=0,r=t.length;r>e;++e)delete d[t[e].identifier];for(var i in d)return void n()}ao.selectAll(_).on(m,null),w.on(L,s).on(R,h),N(),f(v)}var p,g=this,v=D.of(g,arguments),d={},y=0,m=".zoom-"+ao.event.changedTouches[0].identifier,x="touchmove"+m,b="touchend"+m,_=[],w=ao.select(g),N=W(g);t(),l(v),w.on(L,null).on(R,t)}function p(){var n=D.of(this,arguments);m?clearTimeout(m):(Il.call(this),v=e(d=y||ao.mouse(this)),l(n)),m=setTimeout(function(){m=null,f(n)},50),S(),i(Math.pow(2,.002*Bo())*k.k),u(d,v),c(n)}function g(){var n=ao.mouse(this),t=Math.log(k.k)/Math.LN2;o(this,n,e(n),ao.event.shiftKey?Math.ceil(t)-1:Math.floor(t)+1)}var v,d,y,m,M,x,b,_,w,k={x:0,y:0,k:1},E=[960,500],A=Jo,C=250,z=0,L="mousedown.zoom",q="mousemove.zoom",T="mouseup.zoom",R="touchstart.zoom",D=N(n,"zoomstart","zoom","zoomend");return Wo||(Wo="onwheel"in fo?(Bo=function(){return-ao.event.deltaY*(ao.event.deltaMode?120:1)},"wheel"):"onmousewheel"in fo?(Bo=function(){return ao.event.wheelDelta},"mousewheel"):(Bo=function(){return-ao.event.detail},"MozMousePixelScroll")),n.event=function(n){n.each(function(){var n=D.of(this,arguments),t=k;Hl?ao.select(this).transition().each("start.zoom",function(){k=this.__chart__||{x:0,y:0,k:1},l(n)}).tween("zoom:zoom",function(){var e=E[0],r=E[1],i=d?d[0]:e/2,u=d?d[1]:r/2,o=ao.interpolateZoom([(i-k.x)/k.k,(u-k.y)/k.k,e/k.k],[(i-t.x)/t.k,(u-t.y)/t.k,e/t.k]);return function(t){var r=o(t),a=e/r[2];this.__chart__=k={x:i-r[0]*a,y:u-r[1]*a,k:a},c(n)}}).each("interrupt.zoom",function(){f(n)}).each("end.zoom",function(){f(n)}):(this.__chart__=k,l(n),c(n),f(n))})},n.translate=function(t){return arguments.length?(k={x:+t[0],y:+t[1],k:k.k},a(),n):[k.x,k.y]},n.scale=function(t){return arguments.length?(k={x:k.x,y:k.y,k:null},i(+t),a(),n):k.k},n.scaleExtent=function(t){return arguments.length?(A=null==t?Jo:[+t[0],+t[1]],n):A},n.center=function(t){return arguments.length?(y=t&&[+t[0],+t[1]],n):y},n.size=function(t){return arguments.length?(E=t&&[+t[0],+t[1]],n):E},n.duration=function(t){return arguments.length?(C=+t,n):C},n.x=function(t){return arguments.length?(b=t,x=t.copy(),k={x:0,y:0,k:1},n):b},n.y=function(t){return arguments.length?(w=t,_=t.copy(),k={x:0,y:0,k:1},n):w},ao.rebind(n,D,"on")};var Bo,Wo,Jo=[0,1/0];ao.color=an,an.prototype.toString=function(){return this.rgb()+""},ao.hsl=ln;var Go=ln.prototype=new an;Go.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,this.l/n)},Go.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,n*this.l)},Go.rgb=function(){return cn(this.h,this.s,this.l)},ao.hcl=fn;var Ko=fn.prototype=new an;Ko.brighter=function(n){return new fn(this.h,this.c,Math.min(100,this.l+Qo*(arguments.length?n:1)))},Ko.darker=function(n){return new fn(this.h,this.c,Math.max(0,this.l-Qo*(arguments.length?n:1)))},Ko.rgb=function(){return sn(this.h,this.c,this.l).rgb()},ao.lab=hn;var Qo=18,na=.95047,ta=1,ea=1.08883,ra=hn.prototype=new an;ra.brighter=function(n){return new hn(Math.min(100,this.l+Qo*(arguments.length?n:1)),this.a,this.b)},ra.darker=function(n){return new hn(Math.max(0,this.l-Qo*(arguments.length?n:1)),this.a,this.b)},ra.rgb=function(){return pn(this.l,this.a,this.b)},ao.rgb=mn;var ia=mn.prototype=new an;ia.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,i=30;return t||e||r?(t&&i>t&&(t=i),e&&i>e&&(e=i),r&&i>r&&(r=i),new mn(Math.min(255,t/n),Math.min(255,e/n),Math.min(255,r/n))):new mn(i,i,i)},ia.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new mn(n*this.r,n*this.g,n*this.b)},ia.hsl=function(){return wn(this.r,this.g,this.b)},ia.toString=function(){return"#"+bn(this.r)+bn(this.g)+bn(this.b)};var ua=ao.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});ua.forEach(function(n,t){ua.set(n,Mn(t))}),ao.functor=En,ao.xhr=An(m),ao.dsv=function(n,t){function e(n,e,u){arguments.length<3&&(u=e,e=null);var o=Cn(n,t,null==e?r:i(e),u);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:i(n)):e},o}function r(n){return e.parse(n.responseText)}function i(n){return function(t){return e.parse(t.responseText,n)}}function u(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),l=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var i=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(i(n),e)}:i})},e.parseRows=function(n,t){function e(){if(f>=c)return o;if(i)return i=!1,u;var t=f;if(34===n.charCodeAt(t)){for(var e=t;e++<c;)if(34===n.charCodeAt(e)){if(34!==n.charCodeAt(e+1))break;++e}f=e+2;var r=n.charCodeAt(e+1);return 13===r?(i=!0,10===n.charCodeAt(e+2)&&++f):10===r&&(i=!0),n.slice(t+1,e).replace(/""/g,'"')}for(;c>f;){var r=n.charCodeAt(f++),a=1;if(10===r)i=!0;else if(13===r)i=!0,10===n.charCodeAt(f)&&(++f,++a);else if(r!==l)continue;return n.slice(t,f-a)}return n.slice(t)}for(var r,i,u={},o={},a=[],c=n.length,f=0,s=0;(r=e())!==o;){for(var h=[];r!==u&&r!==o;)h.push(r),r=e();t&&null==(h=t(h,s++))||a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new y,i=[];return t.forEach(function(n){for(var t in n)r.has(t)||i.push(r.add(t))}),[i.map(o).join(n)].concat(t.map(function(t){return i.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(u).join("\n")},e},ao.csv=ao.dsv(",","text/csv"),ao.tsv=ao.dsv("	","text/tab-separated-values");var oa,aa,la,ca,fa=this[x(this,"requestAnimationFrame")]||function(n){setTimeout(n,17)};ao.timer=function(){qn.apply(this,arguments)},ao.timer.flush=function(){Rn(),Dn()},ao.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var sa=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Un);ao.formatPrefix=function(n,t){var e=0;return(n=+n)&&(0>n&&(n*=-1),t&&(n=ao.round(n,Pn(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((e-1)/3)))),sa[8+e/3]};var ha=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,pa=ao.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=ao.round(n,Pn(n,t))).toFixed(Math.max(0,Math.min(20,Pn(n*(1+1e-15),t))))}}),ga=ao.time={},va=Date;Hn.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){da.setUTCDate.apply(this._,arguments)},setDay:function(){da.setUTCDay.apply(this._,arguments)},setFullYear:function(){da.setUTCFullYear.apply(this._,arguments)},setHours:function(){da.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){da.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){da.setUTCMinutes.apply(this._,arguments)},setMonth:function(){da.setUTCMonth.apply(this._,arguments)},setSeconds:function(){da.setUTCSeconds.apply(this._,arguments)},setTime:function(){da.setTime.apply(this._,arguments)}};var da=Date.prototype;ga.year=On(function(n){return n=ga.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),ga.years=ga.year.range,ga.years.utc=ga.year.utc.range,ga.day=On(function(n){var t=new va(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),ga.days=ga.day.range,ga.days.utc=ga.day.utc.range,ga.dayOfYear=function(n){var t=ga.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=ga[n]=On(function(n){return(n=ga.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});ga[n+"s"]=e.range,ga[n+"s"].utc=e.utc.range,ga[n+"OfYear"]=function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)}}),ga.week=ga.sunday,ga.weeks=ga.sunday.range,ga.weeks.utc=ga.sunday.utc.range,ga.weekOfYear=ga.sundayOfYear;var ya={"-":"",_:" ",0:"0"},ma=/^\s*\d+/,Ma=/^%/;ao.locale=function(n){return{numberFormat:jn(n),timeFormat:Yn(n)}};var xa=ao.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],
			shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});ao.format=xa.numberFormat,ao.geo={},ft.prototype={s:0,t:0,add:function(n){st(n,this.t,ba),st(ba.s,this.s,this),this.s?this.t+=ba.t:this.s=ba.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var ba=new ft;ao.geo.stream=function(n,t){n&&_a.hasOwnProperty(n.type)?_a[n.type](n,t):ht(n,t)};var _a={Feature:function(n,t){ht(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,i=e.length;++r<i;)ht(e[r].geometry,t)}},wa={Sphere:function(n,t){t.sphere()},Point:function(n,t){n=n.coordinates,t.point(n[0],n[1],n[2])},MultiPoint:function(n,t){for(var e=n.coordinates,r=-1,i=e.length;++r<i;)n=e[r],t.point(n[0],n[1],n[2])},LineString:function(n,t){pt(n.coordinates,t,0)},MultiLineString:function(n,t){for(var e=n.coordinates,r=-1,i=e.length;++r<i;)pt(e[r],t,0)},Polygon:function(n,t){gt(n.coordinates,t)},MultiPolygon:function(n,t){for(var e=n.coordinates,r=-1,i=e.length;++r<i;)gt(e[r],t)},GeometryCollection:function(n,t){for(var e=n.geometries,r=-1,i=e.length;++r<i;)ht(e[r],t)}};ao.geo.area=function(n){return Sa=0,ao.geo.stream(n,Na),Sa};var Sa,ka=new ft,Na={sphere:function(){Sa+=4*Fo},point:b,lineStart:b,lineEnd:b,polygonStart:function(){ka.reset(),Na.lineStart=vt},polygonEnd:function(){var n=2*ka;Sa+=0>n?4*Fo+n:n,Na.lineStart=Na.lineEnd=Na.point=b}};ao.geo.bounds=function(){function n(n,t){M.push(x=[f=n,h=n]),s>t&&(s=t),t>p&&(p=t)}function t(t,e){var r=dt([t*Yo,e*Yo]);if(y){var i=mt(y,r),u=[i[1],-i[0],0],o=mt(u,i);bt(o),o=_t(o);var l=t-g,c=l>0?1:-1,v=o[0]*Zo*c,d=xo(l)>180;if(d^(v>c*g&&c*t>v)){var m=o[1]*Zo;m>p&&(p=m)}else if(v=(v+360)%360-180,d^(v>c*g&&c*t>v)){var m=-o[1]*Zo;s>m&&(s=m)}else s>e&&(s=e),e>p&&(p=e);d?g>t?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t):h>=f?(f>t&&(f=t),t>h&&(h=t)):t>g?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t)}else n(t,e);y=r,g=t}function e(){b.point=t}function r(){x[0]=f,x[1]=h,b.point=n,y=null}function i(n,e){if(y){var r=n-g;m+=xo(r)>180?r+(r>0?360:-360):r}else v=n,d=e;Na.point(n,e),t(n,e)}function u(){Na.lineStart()}function o(){i(v,d),Na.lineEnd(),xo(m)>Uo&&(f=-(h=180)),x[0]=f,x[1]=h,y=null}function a(n,t){return(t-=n)<0?t+360:t}function l(n,t){return n[0]-t[0]}function c(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:n<t[0]||t[1]<n}var f,s,h,p,g,v,d,y,m,M,x,b={point:n,lineStart:e,lineEnd:r,polygonStart:function(){b.point=i,b.lineStart=u,b.lineEnd=o,m=0,Na.polygonStart()},polygonEnd:function(){Na.polygonEnd(),b.point=n,b.lineStart=e,b.lineEnd=r,0>ka?(f=-(h=180),s=-(p=90)):m>Uo?p=90:-Uo>m&&(s=-90),x[0]=f,x[1]=h}};return function(n){p=h=-(f=s=1/0),M=[],ao.geo.stream(n,b);var t=M.length;if(t){M.sort(l);for(var e,r=1,i=M[0],u=[i];t>r;++r)e=M[r],c(e[0],i)||c(e[1],i)?(a(i[0],e[1])>a(i[0],i[1])&&(i[1]=e[1]),a(e[0],i[1])>a(i[0],i[1])&&(i[0]=e[0])):u.push(i=e);for(var o,e,g=-(1/0),t=u.length-1,r=0,i=u[t];t>=r;i=e,++r)e=u[r],(o=a(i[1],e[0]))>g&&(g=o,f=e[0],h=i[1])}return M=x=null,f===1/0||s===1/0?[[NaN,NaN],[NaN,NaN]]:[[f,s],[h,p]]}}(),ao.geo.centroid=function(n){Ea=Aa=Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,ja);var t=Da,e=Pa,r=Ua,i=t*t+e*e+r*r;return jo>i&&(t=qa,e=Ta,r=Ra,Uo>Aa&&(t=Ca,e=za,r=La),i=t*t+e*e+r*r,jo>i)?[NaN,NaN]:[Math.atan2(e,t)*Zo,tn(r/Math.sqrt(i))*Zo]};var Ea,Aa,Ca,za,La,qa,Ta,Ra,Da,Pa,Ua,ja={sphere:b,point:St,lineStart:Nt,lineEnd:Et,polygonStart:function(){ja.lineStart=At},polygonEnd:function(){ja.lineStart=Nt}},Fa=Rt(zt,jt,Ht,[-Fo,-Fo/2]),Ha=1e9;ao.geo.clipExtent=function(){var n,t,e,r,i,u,o={stream:function(n){return i&&(i.valid=!1),i=u(n),i.valid=!0,i},extent:function(a){return arguments.length?(u=Zt(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),i&&(i.valid=!1,i=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(ao.geo.conicEqualArea=function(){return Vt(Xt)}).raw=Xt,ao.geo.albers=function(){return ao.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},ao.geo.albersUsa=function(){function n(n){var u=n[0],o=n[1];return t=null,e(u,o),t||(r(u,o),t)||i(u,o),t}var t,e,r,i,u=ao.geo.albers(),o=ao.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=ao.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),l={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=u.scale(),e=u.translate(),r=(n[0]-e[0])/t,i=(n[1]-e[1])/t;return(i>=.12&&.234>i&&r>=-.425&&-.214>r?o:i>=.166&&.234>i&&r>=-.214&&-.115>r?a:u).invert(n)},n.stream=function(n){var t=u.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,i){t.point(n,i),e.point(n,i),r.point(n,i)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(u.precision(t),o.precision(t),a.precision(t),n):u.precision()},n.scale=function(t){return arguments.length?(u.scale(t),o.scale(.35*t),a.scale(t),n.translate(u.translate())):u.scale()},n.translate=function(t){if(!arguments.length)return u.translate();var c=u.scale(),f=+t[0],s=+t[1];return e=u.translate(t).clipExtent([[f-.455*c,s-.238*c],[f+.455*c,s+.238*c]]).stream(l).point,r=o.translate([f-.307*c,s+.201*c]).clipExtent([[f-.425*c+Uo,s+.12*c+Uo],[f-.214*c-Uo,s+.234*c-Uo]]).stream(l).point,i=a.translate([f-.205*c,s+.212*c]).clipExtent([[f-.214*c+Uo,s+.166*c+Uo],[f-.115*c-Uo,s+.234*c-Uo]]).stream(l).point,n},n.scale(1070)};var Oa,Ia,Ya,Za,Va,Xa,$a={point:b,lineStart:b,lineEnd:b,polygonStart:function(){Ia=0,$a.lineStart=$t},polygonEnd:function(){$a.lineStart=$a.lineEnd=$a.point=b,Oa+=xo(Ia/2)}},Ba={point:Bt,lineStart:b,lineEnd:b,polygonStart:b,polygonEnd:b},Wa={point:Gt,lineStart:Kt,lineEnd:Qt,polygonStart:function(){Wa.lineStart=ne},polygonEnd:function(){Wa.point=Gt,Wa.lineStart=Kt,Wa.lineEnd=Qt}};ao.geo.path=function(){function n(n){return n&&("function"==typeof a&&u.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=i(u)),ao.geo.stream(n,o)),u.result()}function t(){return o=null,n}var e,r,i,u,o,a=4.5;return n.area=function(n){return Oa=0,ao.geo.stream(n,i($a)),Oa},n.centroid=function(n){return Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,i(Wa)),Ua?[Da/Ua,Pa/Ua]:Ra?[qa/Ra,Ta/Ra]:La?[Ca/La,za/La]:[NaN,NaN]},n.bounds=function(n){return Va=Xa=-(Ya=Za=1/0),ao.geo.stream(n,i(Ba)),[[Ya,Za],[Va,Xa]]},n.projection=function(n){return arguments.length?(i=(e=n)?n.stream||re(n):m,t()):e},n.context=function(n){return arguments.length?(u=null==(r=n)?new Wt:new te(n),"function"!=typeof a&&u.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(u.pointRadius(+t),+t),n):a},n.projection(ao.geo.albersUsa()).context(null)},ao.geo.transform=function(n){return{stream:function(t){var e=new ie(t);for(var r in n)e[r]=n[r];return e}}},ie.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},ao.geo.projection=oe,ao.geo.projectionMutator=ae,(ao.geo.equirectangular=function(){return oe(ce)}).raw=ce.invert=ce,ao.geo.rotation=function(n){function t(t){return t=n(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t}return n=se(n[0]%360*Yo,n[1]*Yo,n.length>2?n[2]*Yo:0),t.invert=function(t){return t=n.invert(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t},t},fe.invert=ce,ao.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=se(-n[0]*Yo,-n[1]*Yo,0).invert,i=[];return e(null,null,1,{point:function(n,e){i.push(n=t(n,e)),n[0]*=Zo,n[1]*=Zo}}),{type:"Polygon",coordinates:[i]}}var t,e,r=[0,0],i=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=ve((t=+r)*Yo,i*Yo),n):t},n.precision=function(r){return arguments.length?(e=ve(t*Yo,(i=+r)*Yo),n):i},n.angle(90)},ao.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Yo,i=n[1]*Yo,u=t[1]*Yo,o=Math.sin(r),a=Math.cos(r),l=Math.sin(i),c=Math.cos(i),f=Math.sin(u),s=Math.cos(u);return Math.atan2(Math.sqrt((e=s*o)*e+(e=c*f-l*s*a)*e),l*f+c*s*a)},ao.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return ao.range(Math.ceil(u/d)*d,i,d).map(h).concat(ao.range(Math.ceil(c/y)*y,l,y).map(p)).concat(ao.range(Math.ceil(r/g)*g,e,g).filter(function(n){return xo(n%d)>Uo}).map(f)).concat(ao.range(Math.ceil(a/v)*v,o,v).filter(function(n){return xo(n%y)>Uo}).map(s))}var e,r,i,u,o,a,l,c,f,s,h,p,g=10,v=g,d=90,y=360,m=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(u).concat(p(l).slice(1),h(i).reverse().slice(1),p(c).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(u=+t[0][0],i=+t[1][0],c=+t[0][1],l=+t[1][1],u>i&&(t=u,u=i,i=t),c>l&&(t=c,c=l,l=t),n.precision(m)):[[u,c],[i,l]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(m)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],y=+t[1],n):[d,y]},n.minorStep=function(t){return arguments.length?(g=+t[0],v=+t[1],n):[g,v]},n.precision=function(t){return arguments.length?(m=+t,f=ye(a,o,90),s=me(r,e,m),h=ye(c,l,90),p=me(u,i,m),n):m},n.majorExtent([[-180,-90+Uo],[180,90-Uo]]).minorExtent([[-180,-80-Uo],[180,80+Uo]])},ao.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||i.apply(this,arguments)]}}var t,e,r=Me,i=xe;return n.distance=function(){return ao.geo.distance(t||r.apply(this,arguments),e||i.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(i=t,e="function"==typeof t?null:t,n):i},n.precision=function(){return arguments.length?n:0},n},ao.geo.interpolate=function(n,t){return be(n[0]*Yo,n[1]*Yo,t[0]*Yo,t[1]*Yo)},ao.geo.length=function(n){return Ja=0,ao.geo.stream(n,Ga),Ja};var Ja,Ga={sphere:b,point:b,lineStart:_e,lineEnd:b,polygonStart:b,polygonEnd:b},Ka=we(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(ao.geo.azimuthalEqualArea=function(){return oe(Ka)}).raw=Ka;var Qa=we(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},m);(ao.geo.azimuthalEquidistant=function(){return oe(Qa)}).raw=Qa,(ao.geo.conicConformal=function(){return Vt(Se)}).raw=Se,(ao.geo.conicEquidistant=function(){return Vt(ke)}).raw=ke;var nl=we(function(n){return 1/n},Math.atan);(ao.geo.gnomonic=function(){return oe(nl)}).raw=nl,Ne.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Io]},(ao.geo.mercator=function(){return Ee(Ne)}).raw=Ne;var tl=we(function(){return 1},Math.asin);(ao.geo.orthographic=function(){return oe(tl)}).raw=tl;var el=we(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(ao.geo.stereographic=function(){return oe(el)}).raw=el,Ae.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Io]},(ao.geo.transverseMercator=function(){var n=Ee(Ae),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[n[1],-n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},e([0,0,90])}).raw=Ae,ao.geom={},ao.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,i=En(e),u=En(r),o=n.length,a=[],l=[];for(t=0;o>t;t++)a.push([+i.call(this,n[t],t),+u.call(this,n[t],t),t]);for(a.sort(qe),t=0;o>t;t++)l.push([a[t][0],-a[t][1]]);var c=Le(a),f=Le(l),s=f[0]===c[0],h=f[f.length-1]===c[c.length-1],p=[];for(t=c.length-1;t>=0;--t)p.push(n[a[c[t]][2]]);for(t=+s;t<f.length-h;++t)p.push(n[a[f[t]][2]]);return p}var e=Ce,r=ze;return arguments.length?t(n):(t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t)},ao.geom.polygon=function(n){return ko(n,rl),n};var rl=ao.geom.polygon.prototype=[];rl.area=function(){for(var n,t=-1,e=this.length,r=this[e-1],i=0;++t<e;)n=r,r=this[t],i+=n[1]*r[0]-n[0]*r[1];return.5*i},rl.centroid=function(n){var t,e,r=-1,i=this.length,u=0,o=0,a=this[i-1];for(arguments.length||(n=-1/(6*this.area()));++r<i;)t=a,a=this[r],e=t[0]*a[1]-a[0]*t[1],u+=(t[0]+a[0])*e,o+=(t[1]+a[1])*e;return[u*n,o*n]},rl.clip=function(n){for(var t,e,r,i,u,o,a=De(n),l=-1,c=this.length-De(this),f=this[c-1];++l<c;){for(t=n.slice(),n.length=0,i=this[l],u=t[(r=t.length-a)-1],e=-1;++e<r;)o=t[e],Te(o,f,i)?(Te(u,f,i)||n.push(Re(u,o,f,i)),n.push(o)):Te(u,f,i)&&n.push(Re(u,o,f,i)),u=o;a&&n.push(n[0]),f=i}return n};var il,ul,ol,al,ll,cl=[],fl=[];Ye.prototype.prepare=function(){for(var n,t=this.edges,e=t.length;e--;)n=t[e].edge,n.b&&n.a||t.splice(e,1);return t.sort(Ve),t.length},tr.prototype={start:function(){return this.edge.l===this.site?this.edge.a:this.edge.b},end:function(){return this.edge.l===this.site?this.edge.b:this.edge.a}},er.prototype={insert:function(n,t){var e,r,i;if(n){if(t.P=n,t.N=n.N,n.N&&(n.N.P=t),n.N=t,n.R){for(n=n.R;n.L;)n=n.L;n.L=t}else n.R=t;e=n}else this._?(n=or(this._),t.P=null,t.N=n,n.P=n.L=t,e=n):(t.P=t.N=null,this._=t,e=null);for(t.L=t.R=null,t.U=e,t.C=!0,n=t;e&&e.C;)r=e.U,e===r.L?(i=r.R,i&&i.C?(e.C=i.C=!1,r.C=!0,n=r):(n===e.R&&(ir(this,e),n=e,e=n.U),e.C=!1,r.C=!0,ur(this,r))):(i=r.L,i&&i.C?(e.C=i.C=!1,r.C=!0,n=r):(n===e.L&&(ur(this,e),n=e,e=n.U),e.C=!1,r.C=!0,ir(this,r))),e=n.U;this._.C=!1},remove:function(n){n.N&&(n.N.P=n.P),n.P&&(n.P.N=n.N),n.N=n.P=null;var t,e,r,i=n.U,u=n.L,o=n.R;if(e=u?o?or(o):u:o,i?i.L===n?i.L=e:i.R=e:this._=e,u&&o?(r=e.C,e.C=n.C,e.L=u,u.U=e,e!==o?(i=e.U,e.U=n.U,n=e.R,i.L=n,e.R=o,o.U=e):(e.U=i,i=e,n=e.R)):(r=n.C,n=e),n&&(n.U=i),!r){if(n&&n.C)return void(n.C=!1);do{if(n===this._)break;if(n===i.L){if(t=i.R,t.C&&(t.C=!1,i.C=!0,ir(this,i),t=i.R),t.L&&t.L.C||t.R&&t.R.C){t.R&&t.R.C||(t.L.C=!1,t.C=!0,ur(this,t),t=i.R),t.C=i.C,i.C=t.R.C=!1,ir(this,i),n=this._;break}}else if(t=i.L,t.C&&(t.C=!1,i.C=!0,ur(this,i),t=i.L),t.L&&t.L.C||t.R&&t.R.C){t.L&&t.L.C||(t.R.C=!1,t.C=!0,ir(this,t),t=i.L),t.C=i.C,i.C=t.L.C=!1,ur(this,i),n=this._;break}t.C=!0,n=i,i=i.U}while(!n.C);n&&(n.C=!1)}}},ao.geom.voronoi=function(n){function t(n){var t=new Array(n.length),r=a[0][0],i=a[0][1],u=a[1][0],o=a[1][1];return ar(e(n),a).cells.forEach(function(e,a){var l=e.edges,c=e.site,f=t[a]=l.length?l.map(function(n){var t=n.start();return[t.x,t.y]}):c.x>=r&&c.x<=u&&c.y>=i&&c.y<=o?[[r,o],[u,o],[u,i],[r,i]]:[];f.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(u(n,t)/Uo)*Uo,y:Math.round(o(n,t)/Uo)*Uo,i:t}})}var r=Ce,i=ze,u=r,o=i,a=sl;return n?t(n):(t.links=function(n){return ar(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return ar(e(n)).cells.forEach(function(e,r){for(var i,u,o=e.site,a=e.edges.sort(Ve),l=-1,c=a.length,f=a[c-1].edge,s=f.l===o?f.r:f.l;++l<c;)i=f,u=s,f=a[l].edge,s=f.l===o?f.r:f.l,r<u.i&&r<s.i&&cr(o,u,s)<0&&t.push([n[r],n[u.i],n[s.i]])}),t},t.x=function(n){return arguments.length?(u=En(r=n),t):r},t.y=function(n){return arguments.length?(o=En(i=n),t):i},t.clipExtent=function(n){return arguments.length?(a=null==n?sl:n,t):a===sl?null:a},t.size=function(n){return arguments.length?t.clipExtent(n&&[[0,0],n]):a===sl?null:a&&a[1]},t)};var sl=[[-1e6,-1e6],[1e6,1e6]];ao.geom.delaunay=function(n){return ao.geom.voronoi().triangles(n)},ao.geom.quadtree=function(n,t,e,r,i){function u(n){function u(n,t,e,r,i,u,o,a){if(!isNaN(e)&&!isNaN(r))if(n.leaf){var l=n.x,f=n.y;if(null!=l)if(xo(l-e)+xo(f-r)<.01)c(n,t,e,r,i,u,o,a);else{var s=n.point;n.x=n.y=n.point=null,c(n,s,l,f,i,u,o,a),c(n,t,e,r,i,u,o,a)}else n.x=e,n.y=r,n.point=t}else c(n,t,e,r,i,u,o,a)}function c(n,t,e,r,i,o,a,l){var c=.5*(i+a),f=.5*(o+l),s=e>=c,h=r>=f,p=h<<1|s;n.leaf=!1,n=n.nodes[p]||(n.nodes[p]=hr()),s?i=c:a=c,h?o=f:l=f,u(n,t,e,r,i,o,a,l)}var f,s,h,p,g,v,d,y,m,M=En(a),x=En(l);if(null!=t)v=t,d=e,y=r,m=i;else if(y=m=-(v=d=1/0),s=[],h=[],g=n.length,o)for(p=0;g>p;++p)f=n[p],f.x<v&&(v=f.x),f.y<d&&(d=f.y),f.x>y&&(y=f.x),f.y>m&&(m=f.y),s.push(f.x),h.push(f.y);else for(p=0;g>p;++p){var b=+M(f=n[p],p),_=+x(f,p);v>b&&(v=b),d>_&&(d=_),b>y&&(y=b),_>m&&(m=_),s.push(b),h.push(_)}var w=y-v,S=m-d;w>S?m=d+w:y=v+S;var k=hr();if(k.add=function(n){u(k,n,+M(n,++p),+x(n,p),v,d,y,m)},k.visit=function(n){pr(n,k,v,d,y,m)},k.find=function(n){return gr(k,n[0],n[1],v,d,y,m)},p=-1,null==t){for(;++p<g;)u(k,n[p],s[p],h[p],v,d,y,m);--p}else n.forEach(k.add);return s=h=n=f=null,k}var o,a=Ce,l=ze;return(o=arguments.length)?(a=fr,l=sr,3===o&&(i=e,r=t,e=t=0),u(n)):(u.x=function(n){return arguments.length?(a=n,u):a},u.y=function(n){return arguments.length?(l=n,u):l},u.extent=function(n){return arguments.length?(null==n?t=e=r=i=null:(t=+n[0][0],e=+n[0][1],r=+n[1][0],i=+n[1][1]),u):null==t?null:[[t,e],[r,i]]},u.size=function(n){return arguments.length?(null==n?t=e=r=i=null:(t=e=0,r=+n[0],i=+n[1]),u):null==t?null:[r-t,i-e]},u)},ao.interpolateRgb=vr,ao.interpolateObject=dr,ao.interpolateNumber=yr,ao.interpolateString=mr;var hl=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,pl=new RegExp(hl.source,"g");ao.interpolate=Mr,ao.interpolators=[function(n,t){var e=typeof t;return("string"===e?ua.has(t.toLowerCase())||/^(#|rgb\(|hsl\()/i.test(t)?vr:mr:t instanceof an?vr:Array.isArray(t)?xr:"object"===e&&isNaN(t)?dr:yr)(n,t)}],ao.interpolateArray=xr;var gl=function(){return m},vl=ao.map({linear:gl,poly:Er,quad:function(){return Sr},cubic:function(){return kr},sin:function(){return Ar},exp:function(){return Cr},circle:function(){return zr},elastic:Lr,back:qr,bounce:function(){return Tr}}),dl=ao.map({"in":m,out:_r,"in-out":wr,"out-in":function(n){return wr(_r(n))}});ao.ease=function(n){var t=n.indexOf("-"),e=t>=0?n.slice(0,t):n,r=t>=0?n.slice(t+1):"in";return e=vl.get(e)||gl,r=dl.get(r)||m,br(r(e.apply(null,lo.call(arguments,1))))},ao.interpolateHcl=Rr,ao.interpolateHsl=Dr,ao.interpolateLab=Pr,ao.interpolateRound=Ur,ao.transform=function(n){var t=fo.createElementNS(ao.ns.prefix.svg,"g");return(ao.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new jr(e?e.matrix:yl)})(n)},jr.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var yl={a:1,b:0,c:0,d:1,e:0,f:0};ao.interpolateTransform=$r,ao.layout={},ao.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++e<r;)t.push(Jr(n[e]));return t}},ao.layout.chord=function(){function n(){var n,c,s,h,p,g={},v=[],d=ao.range(u),y=[];for(e=[],r=[],n=0,h=-1;++h<u;){for(c=0,p=-1;++p<u;)c+=i[h][p];v.push(c),y.push(ao.range(u)),n+=c}for(o&&d.sort(function(n,t){return o(v[n],v[t])}),a&&y.forEach(function(n,t){n.sort(function(n,e){return a(i[t][n],i[t][e])})}),n=(Ho-f*u)/n,c=0,h=-1;++h<u;){for(s=c,p=-1;++p<u;){var m=d[h],M=y[m][p],x=i[m][M],b=c,_=c+=x*n;g[m+"-"+M]={index:m,subindex:M,startAngle:b,endAngle:_,value:x}}r[m]={index:m,startAngle:s,endAngle:c,value:v[m]},c+=f}for(h=-1;++h<u;)for(p=h-1;++p<u;){var w=g[h+"-"+p],S=g[p+"-"+h];(w.value||S.value)&&e.push(w.value<S.value?{source:S,target:w}:{source:w,target:S})}l&&t()}function t(){e.sort(function(n,t){return l((n.source.value+n.target.value)/2,(t.source.value+t.target.value)/2)})}var e,r,i,u,o,a,l,c={},f=0;return c.matrix=function(n){return arguments.length?(u=(i=n)&&i.length,e=r=null,c):i},c.padding=function(n){return arguments.length?(f=n,e=r=null,c):f},c.sortGroups=function(n){return arguments.length?(o=n,e=r=null,c):o},c.sortSubgroups=function(n){return arguments.length?(a=n,e=null,c):a},c.sortChords=function(n){return arguments.length?(l=n,e&&t(),c):l},c.chords=function(){return e||n(),e},c.groups=function(){return r||n(),r},c},ao.layout.force=function(){function n(n){return function(t,e,r,i){if(t.point!==n){var u=t.cx-n.x,o=t.cy-n.y,a=i-e,l=u*u+o*o;if(l>a*a/y){if(v>l){var c=t.charge/l;n.px-=u*c,n.py-=o*c}return!0}if(t.point&&l&&v>l){var c=t.pointCharge/l;n.px-=u*c,n.py-=o*c}}return!t.charge}}function t(n){n.px=ao.event.x,n.py=ao.event.y,l.resume()}var e,r,i,u,o,a,l={},c=ao.dispatch("start","tick","end"),f=[1,1],s=.9,h=ml,p=Ml,g=-30,v=xl,d=.1,y=.64,M=[],x=[];return l.tick=function(){if((i*=.99)<.005)return e=null,c.end({type:"end",alpha:i=0}),!0;var t,r,l,h,p,v,y,m,b,_=M.length,w=x.length;for(r=0;w>r;++r)l=x[r],h=l.source,p=l.target,m=p.x-h.x,b=p.y-h.y,(v=m*m+b*b)&&(v=i*o[r]*((v=Math.sqrt(v))-u[r])/v,m*=v,b*=v,p.x-=m*(y=h.weight+p.weight?h.weight/(h.weight+p.weight):.5),p.y-=b*y,h.x+=m*(y=1-y),h.y+=b*y);if((y=i*d)&&(m=f[0]/2,b=f[1]/2,r=-1,y))for(;++r<_;)l=M[r],l.x+=(m-l.x)*y,l.y+=(b-l.y)*y;if(g)for(ri(t=ao.geom.quadtree(M),i,a),r=-1;++r<_;)(l=M[r]).fixed||t.visit(n(l));for(r=-1;++r<_;)l=M[r],l.fixed?(l.x=l.px,l.y=l.py):(l.x-=(l.px-(l.px=l.x))*s,l.y-=(l.py-(l.py=l.y))*s);c.tick({type:"tick",alpha:i})},l.nodes=function(n){return arguments.length?(M=n,l):M},l.links=function(n){return arguments.length?(x=n,l):x},l.size=function(n){return arguments.length?(f=n,l):f},l.linkDistance=function(n){return arguments.length?(h="function"==typeof n?n:+n,l):h},l.distance=l.linkDistance,l.linkStrength=function(n){return arguments.length?(p="function"==typeof n?n:+n,l):p},l.friction=function(n){return arguments.length?(s=+n,l):s},l.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,l):g},l.chargeDistance=function(n){return arguments.length?(v=n*n,l):Math.sqrt(v)},l.gravity=function(n){return arguments.length?(d=+n,l):d},l.theta=function(n){return arguments.length?(y=n*n,l):Math.sqrt(y)},l.alpha=function(n){return arguments.length?(n=+n,i?n>0?i=n:(e.c=null,e.t=NaN,e=null,c.end({type:"end",alpha:i=0})):n>0&&(c.start({type:"start",alpha:i=n}),e=qn(l.tick)),l):i},l.start=function(){function n(n,r){if(!e){for(e=new Array(i),l=0;i>l;++l)e[l]=[];for(l=0;c>l;++l){var u=x[l];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var o,a=e[t],l=-1,f=a.length;++l<f;)if(!isNaN(o=a[l][n]))return o;return Math.random()*r}var t,e,r,i=M.length,c=x.length,s=f[0],v=f[1];for(t=0;i>t;++t)(r=M[t]).index=t,r.weight=0;for(t=0;c>t;++t)r=x[t],"number"==typeof r.source&&(r.source=M[r.source]),"number"==typeof r.target&&(r.target=M[r.target]),++r.source.weight,++r.target.weight;for(t=0;i>t;++t)r=M[t],isNaN(r.x)&&(r.x=n("x",s)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof h)for(t=0;c>t;++t)u[t]=+h.call(this,x[t],t);else for(t=0;c>t;++t)u[t]=h;if(o=[],"function"==typeof p)for(t=0;c>t;++t)o[t]=+p.call(this,x[t],t);else for(t=0;c>t;++t)o[t]=p;if(a=[],"function"==typeof g)for(t=0;i>t;++t)a[t]=+g.call(this,M[t],t);else for(t=0;i>t;++t)a[t]=g;return l.resume()},l.resume=function(){return l.alpha(.1)},l.stop=function(){return l.alpha(0)},l.drag=function(){return r||(r=ao.behavior.drag().origin(m).on("dragstart.force",Qr).on("drag.force",t).on("dragend.force",ni)),arguments.length?void this.on("mouseover.force",ti).on("mouseout.force",ei).call(r):r},ao.rebind(l,c,"on")};var ml=20,Ml=1,xl=1/0;ao.layout.hierarchy=function(){function n(i){var u,o=[i],a=[];for(i.depth=0;null!=(u=o.pop());)if(a.push(u),(c=e.call(n,u,u.depth))&&(l=c.length)){for(var l,c,f;--l>=0;)o.push(f=c[l]),f.parent=u,f.depth=u.depth+1;r&&(u.value=0),u.children=c}else r&&(u.value=+r.call(n,u,u.depth)||0),delete u.children;return oi(i,function(n){var e,i;t&&(e=n.children)&&e.sort(t),r&&(i=n.parent)&&(i.value+=n.value)}),a}var t=ci,e=ai,r=li;return n.sort=function(e){return arguments.length?(t=e,n):t},n.children=function(t){return arguments.length?(e=t,n):e},n.value=function(t){return arguments.length?(r=t,n):r},n.revalue=function(t){return r&&(ui(t,function(n){n.children&&(n.value=0)}),oi(t,function(t){var e;t.children||(t.value=+r.call(n,t,t.depth)||0),(e=t.parent)&&(e.value+=t.value)})),t},n},ao.layout.partition=function(){function n(t,e,r,i){var u=t.children;if(t.x=e,t.y=t.depth*i,t.dx=r,t.dy=i,u&&(o=u.length)){var o,a,l,c=-1;for(r=t.value?r/t.value:0;++c<o;)n(a=u[c],e,l=a.value*r,i),e+=l}}function t(n){var e=n.children,r=0;if(e&&(i=e.length))for(var i,u=-1;++u<i;)r=Math.max(r,t(e[u]));return 1+r}function e(e,u){var o=r.call(this,e,u);return n(o[0],0,i[0],i[1]/t(o[0])),o}var r=ao.layout.hierarchy(),i=[1,1];return e.size=function(n){return arguments.length?(i=n,e):i},ii(e,r)},ao.layout.pie=function(){function n(o){var a,l=o.length,c=o.map(function(e,r){return+t.call(n,e,r)}),f=+("function"==typeof r?r.apply(this,arguments):r),s=("function"==typeof i?i.apply(this,arguments):i)-f,h=Math.min(Math.abs(s)/l,+("function"==typeof u?u.apply(this,arguments):u)),p=h*(0>s?-1:1),g=ao.sum(c),v=g?(s-l*p)/g:0,d=ao.range(l),y=[];return null!=e&&d.sort(e===bl?function(n,t){return c[t]-c[n]}:function(n,t){return e(o[n],o[t])}),d.forEach(function(n){y[n]={data:o[n],value:a=c[n],startAngle:f,endAngle:f+=a*v+p,padAngle:h}}),y}var t=Number,e=bl,r=0,i=Ho,u=0;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(i=t,n):i},n.padAngle=function(t){return arguments.length?(u=t,n):u},n};var bl={};ao.layout.stack=function(){function n(a,l){if(!(h=a.length))return a;var c=a.map(function(e,r){return t.call(n,e,r)}),f=c.map(function(t){return t.map(function(t,e){return[u.call(n,t,e),o.call(n,t,e)]})}),s=e.call(n,f,l);c=ao.permute(c,s),f=ao.permute(f,s);var h,p,g,v,d=r.call(n,f,l),y=c[0].length;for(g=0;y>g;++g)for(i.call(n,c[0][g],v=d[g],f[0][g][1]),p=1;h>p;++p)i.call(n,c[p][g],v+=f[p-1][g][1],f[p][g][1]);return a}var t=m,e=gi,r=vi,i=pi,u=si,o=hi;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:_l.get(t)||gi,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:wl.get(t)||vi,n):r},n.x=function(t){return arguments.length?(u=t,n):u},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(i=t,n):i},n};var _l=ao.map({"inside-out":function(n){var t,e,r=n.length,i=n.map(di),u=n.map(yi),o=ao.range(r).sort(function(n,t){return i[n]-i[t]}),a=0,l=0,c=[],f=[];for(t=0;r>t;++t)e=o[t],l>a?(a+=u[e],c.push(e)):(l+=u[e],f.push(e));return f.reverse().concat(c)},reverse:function(n){return ao.range(n.length).reverse()},"default":gi}),wl=ao.map({silhouette:function(n){var t,e,r,i=n.length,u=n[0].length,o=[],a=0,l=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;u>e;++e)l[e]=(a-o[e])/2;return l},wiggle:function(n){var t,e,r,i,u,o,a,l,c,f=n.length,s=n[0],h=s.length,p=[];for(p[0]=l=c=0,e=1;h>e;++e){for(t=0,i=0;f>t;++t)i+=n[t][e][1];for(t=0,u=0,a=s[e][0]-s[e-1][0];f>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;u+=o*n[t][e][1]}p[e]=l-=i?u/i*a:0,c>l&&(c=l)}for(e=0;h>e;++e)p[e]-=c;return p},expand:function(n){var t,e,r,i=n.length,u=n[0].length,o=1/i,a=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];if(r)for(t=0;i>t;t++)n[t][e][1]/=r;else for(t=0;i>t;t++)n[t][e][1]=o}for(e=0;u>e;++e)a[e]=0;return a},zero:vi});ao.layout.histogram=function(){function n(n,u){for(var o,a,l=[],c=n.map(e,this),f=r.call(this,c,u),s=i.call(this,f,c,u),u=-1,h=c.length,p=s.length-1,g=t?1:1/h;++u<p;)o=l[u]=[],o.dx=s[u+1]-(o.x=s[u]),o.y=0;if(p>0)for(u=-1;++u<h;)a=c[u],a>=f[0]&&a<=f[1]&&(o=l[ao.bisect(s,a,1,p)-1],o.y+=g,o.push(n[u]));return l}var t=!0,e=Number,r=bi,i=Mi;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=En(t),n):r},n.bins=function(t){return arguments.length?(i="number"==typeof t?function(n){return xi(n,t)}:En(t),n):i},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},ao.layout.pack=function(){function n(n,u){var o=e.call(this,n,u),a=o[0],l=i[0],c=i[1],f=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,oi(a,function(n){n.r=+f(n.value)}),oi(a,Ni),r){var s=r*(t?1:Math.max(2*a.r/l,2*a.r/c))/2;oi(a,function(n){n.r+=s}),oi(a,Ni),oi(a,function(n){n.r-=s})}return Ci(a,l/2,c/2,t?1:1/Math.max(2*a.r/l,2*a.r/c)),o}var t,e=ao.layout.hierarchy().sort(_i),r=0,i=[1,1];return n.size=function(t){return arguments.length?(i=t,n):i},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},ii(n,e)},ao.layout.tree=function(){function n(n,i){var f=o.call(this,n,i),s=f[0],h=t(s);if(oi(h,e),h.parent.m=-h.z,ui(h,r),c)ui(s,u);else{var p=s,g=s,v=s;ui(s,function(n){n.x<p.x&&(p=n),n.x>g.x&&(g=n),n.depth>v.depth&&(v=n)});var d=a(p,g)/2-p.x,y=l[0]/(g.x+a(g,p)/2+d),m=l[1]/(v.depth||1);ui(s,function(n){n.x=(n.x+d)*y,n.y=n.depth*m})}return f}function t(n){for(var t,e={A:null,children:[n]},r=[e];null!=(t=r.pop());)for(var i,u=t.children,o=0,a=u.length;a>o;++o)r.push((u[o]=i={_:u[o],parent:t,children:(i=u[o].children)&&i.slice()||[],A:null,a:null,z:0,m:0,c:0,s:0,t:null,i:o}).a=i);return e.children[0]}function e(n){var t=n.children,e=n.parent.children,r=n.i?e[n.i-1]:null;if(t.length){Di(n);var u=(t[0].z+t[t.length-1].z)/2;r?(n.z=r.z+a(n._,r._),n.m=n.z-u):n.z=u}else r&&(n.z=r.z+a(n._,r._));n.parent.A=i(n,r,n.parent.A||e[0])}function r(n){n._.x=n.z+n.parent.m,n.m+=n.parent.m}function i(n,t,e){if(t){for(var r,i=n,u=n,o=t,l=i.parent.children[0],c=i.m,f=u.m,s=o.m,h=l.m;o=Ti(o),i=qi(i),o&&i;)l=qi(l),u=Ti(u),u.a=n,r=o.z+s-i.z-c+a(o._,i._),r>0&&(Ri(Pi(o,n,e),n,r),c+=r,f+=r),s+=o.m,c+=i.m,h+=l.m,f+=u.m;o&&!Ti(u)&&(u.t=o,u.m+=s-f),i&&!qi(l)&&(l.t=i,l.m+=c-h,e=n)}return e}function u(n){n.x*=l[0],n.y=n.depth*l[1]}var o=ao.layout.hierarchy().sort(null).value(null),a=Li,l=[1,1],c=null;return n.separation=function(t){return arguments.length?(a=t,n):a},n.size=function(t){return arguments.length?(c=null==(l=t)?u:null,n):c?null:l},n.nodeSize=function(t){return arguments.length?(c=null==(l=t)?null:u,n):c?l:null},ii(n,o)},ao.layout.cluster=function(){function n(n,u){var o,a=t.call(this,n,u),l=a[0],c=0;oi(l,function(n){var t=n.children;t&&t.length?(n.x=ji(t),n.y=Ui(t)):(n.x=o?c+=e(n,o):0,n.y=0,o=n)});var f=Fi(l),s=Hi(l),h=f.x-e(f,s)/2,p=s.x+e(s,f)/2;return oi(l,i?function(n){n.x=(n.x-l.x)*r[0],n.y=(l.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(p-h)*r[0],n.y=(1-(l.y?n.y/l.y:1))*r[1]}),a}var t=ao.layout.hierarchy().sort(null).value(null),e=Li,r=[1,1],i=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(i=null==(r=t),n):i?null:r},n.nodeSize=function(t){return arguments.length?(i=null!=(r=t),n):i?r:null},ii(n,t)},ao.layout.treemap=function(){function n(n,t){for(var e,r,i=-1,u=n.length;++i<u;)r=(e=n[i]).value*(0>t?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var u=e.children;if(u&&u.length){var o,a,l,c=s(e),f=[],h=u.slice(),g=1/0,v="slice"===p?c.dx:"dice"===p?c.dy:"slice-dice"===p?1&e.depth?c.dy:c.dx:Math.min(c.dx,c.dy);for(n(h,c.dx*c.dy/e.value),f.area=0;(l=h.length)>0;)f.push(o=h[l-1]),f.area+=o.area,"squarify"!==p||(a=r(f,v))<=g?(h.pop(),g=a):(f.area-=f.pop().area,i(f,v,c,!1),v=Math.min(c.dx,c.dy),f.length=f.area=0,g=1/0);f.length&&(i(f,v,c,!0),f.length=f.area=0),u.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var u,o=s(t),a=r.slice(),l=[];for(n(a,o.dx*o.dy/t.value),l.area=0;u=a.pop();)l.push(u),l.area+=u.area,null!=u.z&&(i(l,u.z?o.dx:o.dy,o,!a.length),l.length=l.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,i=0,u=1/0,o=-1,a=n.length;++o<a;)(e=n[o].area)&&(u>e&&(u=e),e>i&&(i=e));return r*=r,t*=t,r?Math.max(t*i*g/r,r/(t*u*g)):1/0}function i(n,t,e,r){var i,u=-1,o=n.length,a=e.x,c=e.y,f=t?l(n.area/t):0;
			if(t==e.dx){for((r||f>e.dy)&&(f=e.dy);++u<o;)i=n[u],i.x=a,i.y=c,i.dy=f,a+=i.dx=Math.min(e.x+e.dx-a,f?l(i.area/f):0);i.z=!0,i.dx+=e.x+e.dx-a,e.y+=f,e.dy-=f}else{for((r||f>e.dx)&&(f=e.dx);++u<o;)i=n[u],i.x=a,i.y=c,i.dx=f,c+=i.dy=Math.min(e.y+e.dy-c,f?l(i.area/f):0);i.z=!1,i.dy+=e.y+e.dy-c,e.x+=f,e.dx-=f}}function u(r){var i=o||a(r),u=i[0];return u.x=u.y=0,u.value?(u.dx=c[0],u.dy=c[1]):u.dx=u.dy=0,o&&a.revalue(u),n([u],u.dx*u.dy/u.value),(o?e:t)(u),h&&(o=i),i}var o,a=ao.layout.hierarchy(),l=Math.round,c=[1,1],f=null,s=Oi,h=!1,p="squarify",g=.5*(1+Math.sqrt(5));return u.size=function(n){return arguments.length?(c=n,u):c},u.padding=function(n){function t(t){var e=n.call(u,t,t.depth);return null==e?Oi(t):Ii(t,"number"==typeof e?[e,e,e,e]:e)}function e(t){return Ii(t,n)}if(!arguments.length)return f;var r;return s=null==(f=n)?Oi:"function"==(r=typeof n)?t:"number"===r?(n=[n,n,n,n],e):e,u},u.round=function(n){return arguments.length?(l=n?Math.round:Number,u):l!=Number},u.sticky=function(n){return arguments.length?(h=n,o=null,u):h},u.ratio=function(n){return arguments.length?(g=n,u):g},u.mode=function(n){return arguments.length?(p=n+"",u):p},ii(u,a)},ao.random={normal:function(n,t){var e=arguments.length;return 2>e&&(t=1),1>e&&(n=0),function(){var e,r,i;do e=2*Math.random()-1,r=2*Math.random()-1,i=e*e+r*r;while(!i||i>1);return n+t*e*Math.sqrt(-2*Math.log(i)/i)}},logNormal:function(){var n=ao.random.normal.apply(ao,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=ao.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},ao.scale={};var Sl={floor:m,ceil:m};ao.scale.linear=function(){return Wi([0,1],[0,1],Mr,!1)};var kl={s:1,g:1,p:1,r:1,e:1};ao.scale.log=function(){return ru(ao.scale.linear().domain([0,1]),10,!0,[1,10])};var Nl=ao.format(".0e"),El={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};ao.scale.pow=function(){return iu(ao.scale.linear(),1,[0,1])},ao.scale.sqrt=function(){return ao.scale.pow().exponent(.5)},ao.scale.ordinal=function(){return ou([],{t:"range",a:[[]]})},ao.scale.category10=function(){return ao.scale.ordinal().range(Al)},ao.scale.category20=function(){return ao.scale.ordinal().range(Cl)},ao.scale.category20b=function(){return ao.scale.ordinal().range(zl)},ao.scale.category20c=function(){return ao.scale.ordinal().range(Ll)};var Al=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(xn),Cl=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(xn),zl=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(xn),Ll=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(xn);ao.scale.quantile=function(){return au([],[])},ao.scale.quantize=function(){return lu(0,1,[0,1])},ao.scale.threshold=function(){return cu([.5],[0,1])},ao.scale.identity=function(){return fu([0,1])},ao.svg={},ao.svg.arc=function(){function n(){var n=Math.max(0,+e.apply(this,arguments)),c=Math.max(0,+r.apply(this,arguments)),f=o.apply(this,arguments)-Io,s=a.apply(this,arguments)-Io,h=Math.abs(s-f),p=f>s?0:1;if(n>c&&(g=c,c=n,n=g),h>=Oo)return t(c,p)+(n?t(n,1-p):"")+"Z";var g,v,d,y,m,M,x,b,_,w,S,k,N=0,E=0,A=[];if((y=(+l.apply(this,arguments)||0)/2)&&(d=u===ql?Math.sqrt(n*n+c*c):+u.apply(this,arguments),p||(E*=-1),c&&(E=tn(d/c*Math.sin(y))),n&&(N=tn(d/n*Math.sin(y)))),c){m=c*Math.cos(f+E),M=c*Math.sin(f+E),x=c*Math.cos(s-E),b=c*Math.sin(s-E);var C=Math.abs(s-f-2*E)<=Fo?0:1;if(E&&yu(m,M,x,b)===p^C){var z=(f+s)/2;m=c*Math.cos(z),M=c*Math.sin(z),x=b=null}}else m=M=0;if(n){_=n*Math.cos(s-N),w=n*Math.sin(s-N),S=n*Math.cos(f+N),k=n*Math.sin(f+N);var L=Math.abs(f-s+2*N)<=Fo?0:1;if(N&&yu(_,w,S,k)===1-p^L){var q=(f+s)/2;_=n*Math.cos(q),w=n*Math.sin(q),S=k=null}}else _=w=0;if(h>Uo&&(g=Math.min(Math.abs(c-n)/2,+i.apply(this,arguments)))>.001){v=c>n^p?0:1;var T=g,R=g;if(Fo>h){var D=null==S?[_,w]:null==x?[m,M]:Re([m,M],[S,k],[x,b],[_,w]),P=m-D[0],U=M-D[1],j=x-D[0],F=b-D[1],H=1/Math.sin(Math.acos((P*j+U*F)/(Math.sqrt(P*P+U*U)*Math.sqrt(j*j+F*F)))/2),O=Math.sqrt(D[0]*D[0]+D[1]*D[1]);R=Math.min(g,(n-O)/(H-1)),T=Math.min(g,(c-O)/(H+1))}if(null!=x){var I=mu(null==S?[_,w]:[S,k],[m,M],c,T,p),Y=mu([x,b],[_,w],c,T,p);g===T?A.push("M",I[0],"A",T,",",T," 0 0,",v," ",I[1],"A",c,",",c," 0 ",1-p^yu(I[1][0],I[1][1],Y[1][0],Y[1][1]),",",p," ",Y[1],"A",T,",",T," 0 0,",v," ",Y[0]):A.push("M",I[0],"A",T,",",T," 0 1,",v," ",Y[0])}else A.push("M",m,",",M);if(null!=S){var Z=mu([m,M],[S,k],n,-R,p),V=mu([_,w],null==x?[m,M]:[x,b],n,-R,p);g===R?A.push("L",V[0],"A",R,",",R," 0 0,",v," ",V[1],"A",n,",",n," 0 ",p^yu(V[1][0],V[1][1],Z[1][0],Z[1][1]),",",1-p," ",Z[1],"A",R,",",R," 0 0,",v," ",Z[0]):A.push("L",V[0],"A",R,",",R," 0 0,",v," ",Z[0])}else A.push("L",_,",",w)}else A.push("M",m,",",M),null!=x&&A.push("A",c,",",c," 0 ",C,",",p," ",x,",",b),A.push("L",_,",",w),null!=S&&A.push("A",n,",",n," 0 ",L,",",1-p," ",S,",",k);return A.push("Z"),A.join("")}function t(n,t){return"M0,"+n+"A"+n+","+n+" 0 1,"+t+" 0,"+-n+"A"+n+","+n+" 0 1,"+t+" 0,"+n}var e=hu,r=pu,i=su,u=ql,o=gu,a=vu,l=du;return n.innerRadius=function(t){return arguments.length?(e=En(t),n):e},n.outerRadius=function(t){return arguments.length?(r=En(t),n):r},n.cornerRadius=function(t){return arguments.length?(i=En(t),n):i},n.padRadius=function(t){return arguments.length?(u=t==ql?ql:En(t),n):u},n.startAngle=function(t){return arguments.length?(o=En(t),n):o},n.endAngle=function(t){return arguments.length?(a=En(t),n):a},n.padAngle=function(t){return arguments.length?(l=En(t),n):l},n.centroid=function(){var n=(+e.apply(this,arguments)+ +r.apply(this,arguments))/2,t=(+o.apply(this,arguments)+ +a.apply(this,arguments))/2-Io;return[Math.cos(t)*n,Math.sin(t)*n]},n};var ql="auto";ao.svg.line=function(){return Mu(m)};var Tl=ao.map({linear:xu,"linear-closed":bu,step:_u,"step-before":wu,"step-after":Su,basis:zu,"basis-open":Lu,"basis-closed":qu,bundle:Tu,cardinal:Eu,"cardinal-open":ku,"cardinal-closed":Nu,monotone:Fu});Tl.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Rl=[0,2/3,1/3,0],Dl=[0,1/3,2/3,0],Pl=[0,1/6,2/3,1/6];ao.svg.line.radial=function(){var n=Mu(Hu);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},wu.reverse=Su,Su.reverse=wu,ao.svg.area=function(){return Ou(m)},ao.svg.area.radial=function(){var n=Ou(Hu);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},ao.svg.chord=function(){function n(n,a){var l=t(this,u,n,a),c=t(this,o,n,a);return"M"+l.p0+r(l.r,l.p1,l.a1-l.a0)+(e(l,c)?i(l.r,l.p1,l.r,l.p0):i(l.r,l.p1,c.r,c.p0)+r(c.r,c.p1,c.a1-c.a0)+i(c.r,c.p1,l.r,l.p0))+"Z"}function t(n,t,e,r){var i=t.call(n,e,r),u=a.call(n,i,r),o=l.call(n,i,r)-Io,f=c.call(n,i,r)-Io;return{r:u,a0:o,a1:f,p0:[u*Math.cos(o),u*Math.sin(o)],p1:[u*Math.cos(f),u*Math.sin(f)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Fo)+",1 "+t}function i(n,t,e,r){return"Q 0,0 "+r}var u=Me,o=xe,a=Iu,l=gu,c=vu;return n.radius=function(t){return arguments.length?(a=En(t),n):a},n.source=function(t){return arguments.length?(u=En(t),n):u},n.target=function(t){return arguments.length?(o=En(t),n):o},n.startAngle=function(t){return arguments.length?(l=En(t),n):l},n.endAngle=function(t){return arguments.length?(c=En(t),n):c},n},ao.svg.diagonal=function(){function n(n,i){var u=t.call(this,n,i),o=e.call(this,n,i),a=(u.y+o.y)/2,l=[u,{x:u.x,y:a},{x:o.x,y:a},o];return l=l.map(r),"M"+l[0]+"C"+l[1]+" "+l[2]+" "+l[3]}var t=Me,e=xe,r=Yu;return n.source=function(e){return arguments.length?(t=En(e),n):t},n.target=function(t){return arguments.length?(e=En(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},ao.svg.diagonal.radial=function(){var n=ao.svg.diagonal(),t=Yu,e=n.projection;return n.projection=function(n){return arguments.length?e(Zu(t=n)):t},n},ao.svg.symbol=function(){function n(n,r){return(Ul.get(t.call(this,n,r))||$u)(e.call(this,n,r))}var t=Xu,e=Vu;return n.type=function(e){return arguments.length?(t=En(e),n):t},n.size=function(t){return arguments.length?(e=En(t),n):e},n};var Ul=ao.map({circle:$u,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Fl)),e=t*Fl;return"M0,"+-t+"L"+e+",0 0,"+t+" "+-e+",0Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});ao.svg.symbolTypes=Ul.keys();var jl=Math.sqrt(3),Fl=Math.tan(30*Yo);Co.transition=function(n){for(var t,e,r=Hl||++Zl,i=Ku(n),u=[],o=Ol||{time:Date.now(),ease:Nr,delay:0,duration:250},a=-1,l=this.length;++a<l;){u.push(t=[]);for(var c=this[a],f=-1,s=c.length;++f<s;)(e=c[f])&&Qu(e,f,i,r,o),t.push(e)}return Wu(u,i,r)},Co.interrupt=function(n){return this.each(null==n?Il:Bu(Ku(n)))};var Hl,Ol,Il=Bu(Ku()),Yl=[],Zl=0;Yl.call=Co.call,Yl.empty=Co.empty,Yl.node=Co.node,Yl.size=Co.size,ao.transition=function(n,t){return n&&n.transition?Hl?n.transition(t):n:ao.selection().transition(n)},ao.transition.prototype=Yl,Yl.select=function(n){var t,e,r,i=this.id,u=this.namespace,o=[];n=A(n);for(var a=-1,l=this.length;++a<l;){o.push(t=[]);for(var c=this[a],f=-1,s=c.length;++f<s;)(r=c[f])&&(e=n.call(r,r.__data__,f,a))?("__data__"in r&&(e.__data__=r.__data__),Qu(e,f,u,i,r[u][i]),t.push(e)):t.push(null)}return Wu(o,u,i)},Yl.selectAll=function(n){var t,e,r,i,u,o=this.id,a=this.namespace,l=[];n=C(n);for(var c=-1,f=this.length;++c<f;)for(var s=this[c],h=-1,p=s.length;++h<p;)if(r=s[h]){u=r[a][o],e=n.call(r,r.__data__,h,c),l.push(t=[]);for(var g=-1,v=e.length;++g<v;)(i=e[g])&&Qu(i,g,a,o,u),t.push(i)}return Wu(l,a,o)},Yl.filter=function(n){var t,e,r,i=[];"function"!=typeof n&&(n=O(n));for(var u=0,o=this.length;o>u;u++){i.push(t=[]);for(var e=this[u],a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return Wu(i,this.namespace,this.id)},Yl.tween=function(n,t){var e=this.id,r=this.namespace;return arguments.length<2?this.node()[r][e].tween.get(n):Y(this,null==t?function(t){t[r][e].tween.remove(n)}:function(i){i[r][e].tween.set(n,t)})},Yl.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function i(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function u(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?$r:Mr,a=ao.ns.qualify(n);return Ju(this,"attr."+n,t,a.local?u:i)},Yl.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(i));return r&&function(n){this.setAttribute(i,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(i.space,i.local));return r&&function(n){this.setAttributeNS(i.space,i.local,r(n))}}var i=ao.ns.qualify(n);return this.tween("attr."+n,i.local?r:e)},Yl.style=function(n,e,r){function i(){this.style.removeProperty(n)}function u(e){return null==e?i:(e+="",function(){var i,u=t(this).getComputedStyle(this,null).getPropertyValue(n);return u!==e&&(i=Mr(u,e),function(t){this.style.setProperty(n,i(t),r)})})}var o=arguments.length;if(3>o){if("string"!=typeof n){2>o&&(e="");for(r in n)this.style(r,n[r],e);return this}r=""}return Ju(this,"style."+n,e,u)},Yl.styleTween=function(n,e,r){function i(i,u){var o=e.call(this,i,u,t(this).getComputedStyle(this,null).getPropertyValue(n));return o&&function(t){this.style.setProperty(n,o(t),r)}}return arguments.length<3&&(r=""),this.tween("style."+n,i)},Yl.text=function(n){return Ju(this,"text",n,Gu)},Yl.remove=function(){var n=this.namespace;return this.each("end.transition",function(){var t;this[n].count<2&&(t=this.parentNode)&&t.removeChild(this)})},Yl.ease=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].ease:("function"!=typeof n&&(n=ao.ease.apply(ao,arguments)),Y(this,function(r){r[e][t].ease=n}))},Yl.delay=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].delay:Y(this,"function"==typeof n?function(r,i,u){r[e][t].delay=+n.call(r,r.__data__,i,u)}:(n=+n,function(r){r[e][t].delay=n}))},Yl.duration=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].duration:Y(this,"function"==typeof n?function(r,i,u){r[e][t].duration=Math.max(1,n.call(r,r.__data__,i,u))}:(n=Math.max(1,n),function(r){r[e][t].duration=n}))},Yl.each=function(n,t){var e=this.id,r=this.namespace;if(arguments.length<2){var i=Ol,u=Hl;try{Hl=e,Y(this,function(t,i,u){Ol=t[r][e],n.call(t,t.__data__,i,u)})}finally{Ol=i,Hl=u}}else Y(this,function(i){var u=i[r][e];(u.event||(u.event=ao.dispatch("start","end","interrupt"))).on(n,t)});return this},Yl.transition=function(){for(var n,t,e,r,i=this.id,u=++Zl,o=this.namespace,a=[],l=0,c=this.length;c>l;l++){a.push(n=[]);for(var t=this[l],f=0,s=t.length;s>f;f++)(e=t[f])&&(r=e[o][i],Qu(e,f,o,u,{time:r.time,ease:r.ease,delay:r.delay+r.duration,duration:r.duration})),n.push(e)}return Wu(a,o,u)},ao.svg.axis=function(){function n(n){n.each(function(){var n,c=ao.select(this),f=this.__chart__||e,s=this.__chart__=e.copy(),h=null==l?s.ticks?s.ticks.apply(s,a):s.domain():l,p=null==t?s.tickFormat?s.tickFormat.apply(s,a):m:t,g=c.selectAll(".tick").data(h,s),v=g.enter().insert("g",".domain").attr("class","tick").style("opacity",Uo),d=ao.transition(g.exit()).style("opacity",Uo).remove(),y=ao.transition(g.order()).style("opacity",1),M=Math.max(i,0)+o,x=Zi(s),b=c.selectAll(".domain").data([0]),_=(b.enter().append("path").attr("class","domain"),ao.transition(b));v.append("line"),v.append("text");var w,S,k,N,E=v.select("line"),A=y.select("line"),C=g.select("text").text(p),z=v.select("text"),L=y.select("text"),q="top"===r||"left"===r?-1:1;if("bottom"===r||"top"===r?(n=no,w="x",k="y",S="x2",N="y2",C.attr("dy",0>q?"0em":".71em").style("text-anchor","middle"),_.attr("d","M"+x[0]+","+q*u+"V0H"+x[1]+"V"+q*u)):(n=to,w="y",k="x",S="y2",N="x2",C.attr("dy",".32em").style("text-anchor",0>q?"end":"start"),_.attr("d","M"+q*u+","+x[0]+"H0V"+x[1]+"H"+q*u)),E.attr(N,q*i),z.attr(k,q*M),A.attr(S,0).attr(N,q*i),L.attr(w,0).attr(k,q*M),s.rangeBand){var T=s,R=T.rangeBand()/2;f=s=function(n){return T(n)+R}}else f.rangeBand?f=s:d.call(n,s,f);v.call(n,f,s),y.call(n,s,s)})}var t,e=ao.scale.linear(),r=Vl,i=6,u=6,o=3,a=[10],l=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Xl?t+"":Vl,n):r},n.ticks=function(){return arguments.length?(a=co(arguments),n):a},n.tickValues=function(t){return arguments.length?(l=t,n):l},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(i=+t,u=+arguments[e-1],n):i},n.innerTickSize=function(t){return arguments.length?(i=+t,n):i},n.outerTickSize=function(t){return arguments.length?(u=+t,n):u},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Vl="bottom",Xl={top:1,right:1,bottom:1,left:1};ao.svg.brush=function(){function n(t){t.each(function(){var t=ao.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=t.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),t.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=t.selectAll(".resize").data(v,m);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return $l[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var l,s=ao.transition(t),h=ao.transition(o);c&&(l=Zi(c),h.attr("x",l[0]).attr("width",l[1]-l[0]),r(s)),f&&(l=Zi(f),h.attr("y",l[0]).attr("height",l[1]-l[0]),i(s)),e(s)})}function e(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+s[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function r(n){n.select(".extent").attr("x",s[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",s[1]-s[0])}function i(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function u(){function u(){32==ao.event.keyCode&&(C||(M=null,L[0]-=s[1],L[1]-=h[1],C=2),S())}function v(){32==ao.event.keyCode&&2==C&&(L[0]+=s[1],L[1]+=h[1],C=0,S())}function d(){var n=ao.mouse(b),t=!1;x&&(n[0]+=x[0],n[1]+=x[1]),C||(ao.event.altKey?(M||(M=[(s[0]+s[1])/2,(h[0]+h[1])/2]),L[0]=s[+(n[0]<M[0])],L[1]=h[+(n[1]<M[1])]):M=null),E&&y(n,c,0)&&(r(k),t=!0),A&&y(n,f,1)&&(i(k),t=!0),t&&(e(k),w({type:"brush",mode:C?"move":"resize"}))}function y(n,t,e){var r,i,u=Zi(t),l=u[0],c=u[1],f=L[e],v=e?h:s,d=v[1]-v[0];return C&&(l-=f,c-=d+f),r=(e?g:p)?Math.max(l,Math.min(c,n[e])):n[e],C?i=(r+=f)+d:(M&&(f=Math.max(l,Math.min(c,2*M[e]-r))),r>f?(i=r,r=f):i=f),v[0]!=r||v[1]!=i?(e?a=null:o=null,v[0]=r,v[1]=i,!0):void 0}function m(){d(),k.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),ao.select("body").style("cursor",null),q.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),z(),w({type:"brushend"})}var M,x,b=this,_=ao.select(ao.event.target),w=l.of(b,arguments),k=ao.select(b),N=_.datum(),E=!/^(n|s)$/.test(N)&&c,A=!/^(e|w)$/.test(N)&&f,C=_.classed("extent"),z=W(b),L=ao.mouse(b),q=ao.select(t(b)).on("keydown.brush",u).on("keyup.brush",v);if(ao.event.changedTouches?q.on("touchmove.brush",d).on("touchend.brush",m):q.on("mousemove.brush",d).on("mouseup.brush",m),k.interrupt().selectAll("*").interrupt(),C)L[0]=s[0]-L[0],L[1]=h[0]-L[1];else if(N){var T=+/w$/.test(N),R=+/^n/.test(N);x=[s[1-T]-L[0],h[1-R]-L[1]],L[0]=s[T],L[1]=h[R]}else ao.event.altKey&&(M=L.slice());k.style("pointer-events","none").selectAll(".resize").style("display",null),ao.select("body").style("cursor",_.style("cursor")),w({type:"brushstart"}),d()}var o,a,l=N(n,"brushstart","brush","brushend"),c=null,f=null,s=[0,0],h=[0,0],p=!0,g=!0,v=Bl[0];return n.event=function(n){n.each(function(){var n=l.of(this,arguments),t={x:s,y:h,i:o,j:a},e=this.__chart__||t;this.__chart__=t,Hl?ao.select(this).transition().each("start.brush",function(){o=e.i,a=e.j,s=e.x,h=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=xr(s,t.x),r=xr(h,t.y);return o=a=null,function(i){s=t.x=e(i),h=t.y=r(i),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){o=t.i,a=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,v=Bl[!c<<1|!f],n):c},n.y=function(t){return arguments.length?(f=t,v=Bl[!c<<1|!f],n):f},n.clamp=function(t){return arguments.length?(c&&f?(p=!!t[0],g=!!t[1]):c?p=!!t:f&&(g=!!t),n):c&&f?[p,g]:c?p:f?g:null},n.extent=function(t){var e,r,i,u,l;return arguments.length?(c&&(e=t[0],r=t[1],f&&(e=e[0],r=r[0]),o=[e,r],c.invert&&(e=c(e),r=c(r)),e>r&&(l=e,e=r,r=l),e==s[0]&&r==s[1]||(s=[e,r])),f&&(i=t[0],u=t[1],c&&(i=i[1],u=u[1]),a=[i,u],f.invert&&(i=f(i),u=f(u)),i>u&&(l=i,i=u,u=l),i==h[0]&&u==h[1]||(h=[i,u])),n):(c&&(o?(e=o[0],r=o[1]):(e=s[0],r=s[1],c.invert&&(e=c.invert(e),r=c.invert(r)),e>r&&(l=e,e=r,r=l))),f&&(a?(i=a[0],u=a[1]):(i=h[0],u=h[1],f.invert&&(i=f.invert(i),u=f.invert(u)),i>u&&(l=i,i=u,u=l))),c&&f?[[e,i],[r,u]]:c?[e,r]:f&&[i,u])},n.clear=function(){return n.empty()||(s=[0,0],h=[0,0],o=a=null),n},n.empty=function(){return!!c&&s[0]==s[1]||!!f&&h[0]==h[1]},ao.rebind(n,l,"on")};var $l={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Bl=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Wl=ga.format=xa.timeFormat,Jl=Wl.utc,Gl=Jl("%Y-%m-%dT%H:%M:%S.%LZ");Wl.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?eo:Gl,eo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},eo.toString=Gl.toString,ga.second=On(function(n){return new va(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),ga.seconds=ga.second.range,ga.seconds.utc=ga.second.utc.range,ga.minute=On(function(n){return new va(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),ga.minutes=ga.minute.range,ga.minutes.utc=ga.minute.utc.range,ga.hour=On(function(n){var t=n.getTimezoneOffset()/60;return new va(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),ga.hours=ga.hour.range,ga.hours.utc=ga.hour.utc.range,ga.month=On(function(n){return n=ga.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),ga.months=ga.month.range,ga.months.utc=ga.month.utc.range;var Kl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Ql=[[ga.second,1],[ga.second,5],[ga.second,15],[ga.second,30],[ga.minute,1],[ga.minute,5],[ga.minute,15],[ga.minute,30],[ga.hour,1],[ga.hour,3],[ga.hour,6],[ga.hour,12],[ga.day,1],[ga.day,2],[ga.week,1],[ga.month,1],[ga.month,3],[ga.year,1]],nc=Wl.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",zt]]),tc={range:function(n,t,e){return ao.range(Math.ceil(n/e)*e,+t,e).map(io)},floor:m,ceil:m};Ql.year=ga.year,ga.scale=function(){return ro(ao.scale.linear(),Ql,nc)};var ec=Ql.map(function(n){return[n[0].utc,n[1]]}),rc=Jl.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",zt]]);ec.year=ga.year.utc,ga.scale.utc=function(){return ro(ao.scale.linear(),ec,rc)},ao.text=An(function(n){return n.responseText}),ao.json=function(n,t){return Cn(n,"application/json",uo,t)},ao.html=function(n,t){return Cn(n,"text/html",oo,t)},ao.xml=An(function(n){return n.responseXML}),"function"==typeof define&&define.amd?(this.d3=ao,define(ao)):"object"==typeof module&&module.exports?module.exports=ao:this.d3=ao}();},{}],
			2:[function(require,module,exports){
			"use strict";

			var d3 = require('d3');

			//aigner: functions for drawing expand- and collapse-symbols
			function drawExpandSymbol(aCircle, line1, line2)
			{        
				 aCircle = aCircle
					.attr("r", 10)
					.style("stroke", "black")
					.style("stroke-width", 2)
					.style("fill", "#99CC00")
					.style("fill-opacity", .6)
				line1 = line1
					.attr("x1", parseFloat(aCircle.attr("cx"))-5)
					.attr("y1", parseFloat(aCircle.attr("cy")))
					.attr("x2", parseFloat(aCircle.attr("cx"))+5)
					.attr("y2", parseFloat(aCircle.attr("cy")))
					.style("stroke", "black")
					.style("stroke-width", 2);
				line2 = line2
					.attr("x1", parseFloat(aCircle.attr("cx")))
					.attr("y1", parseFloat(aCircle.attr("cy"))-5)
					.attr("x2", parseFloat(aCircle.attr("cx")))
					.attr("y2", parseFloat(aCircle.attr("cy"))+5)
					.style("stroke", "black")
					.style("stroke-width", 2);
			}
			function drawRemoveSymbol(anXdsm, aCircle, aMinus)
			{        
				 var xOffset=150;
				 var yOffset=12;
				 aCircle = aCircle
					.attr("cx", anXdsm.svg.attr("width")-xOffset)
					.attr("cy", yOffset)
					.attr("r", 10)
					.classed("remCircle",true)
				aMinus = aMinus
					.attr("x1", anXdsm.svg.attr("width")-xOffset+5)
					.attr("y1", yOffset)
					.attr("x2", anXdsm.svg.attr("width")-xOffset-5)
					.attr("y2", yOffset)
					.classed("remMinus",true)
			}
			//d3-context-menu for right-click-option
			d3.contextMenu = function (menu, openCallback) {

				// create the div element that will hold the context menu
				d3.selectAll('.d3-context-menu').data([1])
					.enter()
					.append('div')
					.attr('class', 'd3-context-menu');

				// close menu
				d3.select('body').on('click.d3-context-menu', function() {
					d3.select('.d3-context-menu').style('display', 'none');
				});

				// this gets executed when a contextmenu event occurs
				return function(data, index) {	
					var elm = this;

					d3.selectAll('.d3-context-menu').html('');
					var list = d3.selectAll('.d3-context-menu').append('ul');
						list.selectAll('li').data(menu).enter()
						.append('li')
						.html(function(d) {
							return d.title;
						})
						.on('mousedown', function(d, i) {
							d.onMouseDown(elm, data, index);
						})
						.on('mouseup', function(d, i) {
							d.onMouseUp(elm, data, index);
							d3.select('.d3-context-menu').style('display', 'none');
						})
						.on('mouseenter',function(d,i){
							d.onMouseOver(elm,data,index);
							if(d.childrenItems.length>0 )
								 {
                                  var li = this
								  d3.select(this).selectAll("ul").remove(); 
								  d3.select(this)
									.append("ul")
                                    .style("top",String(li.offsetTop-5)+"px")
									.selectAll("li")
									   .data(d.childrenItems)
										.enter().append("li")
										  .text(function(d) { return d.title; })
									 .on("mouseenter", function(d,i){
											d.onMouseOver(elm,data,index);
										})
									 .on('click',  function(d, i) {
											d.onMouseClick(elm, d, index);
										})
									 .on('mouseleave',function(d,i){
										
										});
								 }
							 else
								 return false;
						})
						.on('mouseleave',function(d,i){
							d3.select(this).selectAll("ul").style('display', 'none')                  
						});
					
					  

					// the openCallback allows an action to fire before the menu is displayed
					// an example usage would be closing a tooltip
					if (openCallback) openCallback(data, index);

					// display context menu
					d3.select('.d3-context-menu')
						.style('left', (d3.event.pageX - 2) + 'px')
						.style('top', (d3.event.pageY - 2) + 'px')
						.style('display', 'block');
                    
                    //Prevent the default event, which is the left-click. 
                    //This means, the context-menu will only appear on right mouse clicks
					d3.event.preventDefault();
                    
                    //Place context-menu always on top of everything esle
                    d3.select(".d3-context-menu").style("z-index",Number.MAX_SAFE_INTEGER);
				};
			};
				
			function tabulate(aTable,data,columns) {
				var thead = aTable.append('thead');
				var	tbody = aTable.append('tbody');  

				// create a row for each object in the data
				var rows = tbody.selectAll('tr')
				  .data(data)
				  .enter()
				  .append('tr');

				// create a cell in each row for each column
				var cells = rows.selectAll('td')
				  .data(function (row) {
					return columns.map(function (column) {
					  return {column: column, value: row[column]};
					});
				  })
				  .enter()
				  .append('td')
					.html(function (d) { return d.value; });

				return aTable;
			}

			//aigner: Move to front function
			d3.selection.prototype.moveToFront = function() {  
			  return this.each(function(){
				this.parentNode.appendChild(this);
			  });
			};
			//aigner: Move to back function
			d3.selection.prototype.moveToBack = function() {  
				return this.each(function() { 
					var firstChild = this.parentNode.firstChild; 
					if (firstChild) { 
						this.parentNode.insertBefore(this, firstChild); 
					} 
				});
			};


			var diameter = 1500,
				radius = diameter / 2,
				innerRadius = radius - 250;

			var cluster = d3.layout.cluster()
				.size([360, innerRadius])
				.sort(null)
				.value(function(d) { return d.size; });

			var bundle = d3.layout.bundle();

			var line = d3.svg.line.radial()
				.interpolate("bundle")
				.tension(0.5)
				.radius(function(d) { return d.y; })
				.angle(function(d) { return d.x / 180 * Math.PI; });

			var svg = d3.select("body").append("div").attr("class","edgeBundlesDiv")
				svg = svg.append("svg")
					.attr("class","circleSvg")
					.attr("width", 10000)
					.attr("height", 10000)
					.append("g")
					.attr("transform", "translate(" + radius*1.2 + "," + radius*1.2+ ")");
				
			var width = diameter*1.5;
			var imgWidth = width;

			var link = svg.append("g").selectAll(".edgeBundlesLink"),
				invisibleLink = svg.append("g").selectAll(".invisibleLink"),
				node = svg.append("g").selectAll(".edgeBundlesNode");
			
			//aigner: Here the data is read and the edge bundles is created
			//################################################################################################//			
			function startEdgeBundles(data, graphID) 
			{
				var graphs, currentGraph, varCategories;
				
				graphs =  data.graphs;
				for (var i=0;i<graphs.length;i++)
				{
					if (graphs[i].id==graphID)
					{
						currentGraph = graphs[i]
					}
				}
				
				varCategories =  data.categories;
				var classes = currentGraph.edgeBundles;
				
				//Highlight function, that shows usage of a node in the XDSM
                function highlight(data)
                {
                    var xPath = data.xPath
                    var allLinks = d3.selectAll(".edgeBundlesLink");
                    allLinks[0].forEach(function(aLink)
                    {
                        
                        aLink.__data__.pipeData_in = aLink.__data__.source.pipeline_data[aLink.__data__.target.name];
                        aLink.__data__.pipeData_out = aLink.__data__.target.pipeline_data[aLink.__data__.source.name];
                        aLink.__data__.pipeDataName_in = "";
                        aLink.__data__.pipeDataName_out = "";
                        aLink.__data__.name = "";
                        if (aLink.__data__.pipeData_in)
                        {
                            for (var i=0; i<aLink.__data__.pipeData_in.length; i++)
                            {
                                if (i==0){aLink.__data__.pipeDataName_in += aLink.__data__.pipeData_in[i];}
                                else{aLink.__data__.pipeDataName_in += "," + aLink.__data__.pipeData_in[i];}
                                
                            }
                            aLink.__data__.name += aLink.__data__.pipeDataName_in
                        }
                        if (aLink.__data__.pipeData_out)
                        {
                            for (var i=0; i<aLink.__data__.pipeData_out.length; i++)
                            {
                                if (i==0){aLink.__data__.pipeDataName_out += aLink.__data__.pipeData_out[i];}
                                else{aLink.__data__.pipeDataName_out += "," + aLink.__data__.pipeData_out[i];}
                            }
                            aLink.__data__.name += aLink.__data__.pipeDataName_out
                        }
                    })
                    
                    allLinks[0].forEach(function(p) {
                        var firstElement_tmp = p.__data__.name.split("/")[1]
                        var xPath_fromFirst = "/"+firstElement_tmp+xPath.split(firstElement_tmp)[1]
                        var p_name_list = p.__data__.name.split(",")
                        var highlight_on = false
                        p_name_list.forEach(function(p_name) 
                        {
                            if (include(p_name+"__",xPath_fromFirst+"__") || include(p_name,xPath_fromFirst+"/"))
                            {                                
                                highlight_on = true
                            }
                        })
                        if (highlight_on)
                        {
                            d3.select(p).style("stroke-opacity", 1.5)
                        }
                        else
                        {
                            d3.select(p).style("stroke-opacity", 0);			
                        }
                    });
                }
                
                function showVariableTable(aVariable)
                {						
                                        
                    var headLine = "Node Information (" + aVariable.name + ")";
                    var data = [];
                    // render the table(s)
                    data.push({ "name" : "Name", "value" : "\""+aVariable.name+"\"" })
                    data.push({ "name" : "xPath", "value" : aVariable.xPath })
                    if (aVariable.type){data.push({ "name" : "Type", "value" : aVariable.type })}
                    if (aVariable.level){data.push({ "name" : "Level", "value" : aVariable.level })}
                    if (aVariable.children || aVariable._children)
                    {
                        var childrenNum=0;
                        if (aVariable.children){childrenNum=childrenNum+aVariable.children.length}
                        if (aVariable._children){childrenNum=childrenNum+aVariable._children.length}
                        data.push({ "name" : "Number of children", "value" : childrenNum })
                    }
                    if (aVariable.dimension){data.push({ "name" : "Dimension", "value" : aVariable.dimension })}
                    else if(aVariable.dimension===null){data.push({ "name" : "Dimension", "value" : "undefined" })}
                    if (aVariable.value){data.push({ "name" : "Value(s)", "value" : aVariable.value })}
                    
                    var d3_body = d3.select("body");
                    
                    var panel_div = d3_body.append("div").attr("class", "myPanel panel-default")
                    panel_div.append("div").attr("class","panel-heading")
                        .append("div").attr("class","panel_title").append("h4").text(headLine)
                    var listGroup = panel_div.append("div").attr("class","panel-body")
                        .append("table").attr("id","myTable")
                        .append("tbody")
                        
                    data.forEach(function(listElement)
                    {
                        var row = listGroup.append("tr")
                        row.append("td").text(listElement.name)
                        row.append("td").text(listElement.value)
                        
                    })
                    $('.myPanel').lobiPanel({
                        reload: false,
                        editTitle: false,
                        expand: false,
                        unpin: false,
                        resize: "none",
                        minWidth: 200,
                        minHeight: 200,
                        maxWidth: 1100,
                        maxHeight: 1200,
                    });
                    $('.myPanel').lobiPanel('unpin');
                }
                
                //menu --> functions for right click options
                var nodeMenu = [
                {
                    title: 'Show node information',
                    onMouseDown: function(elm, d, i) {
                        showVariableTable(d);
                    },
                    onMouseUp: function(elm, d, i) {},
                    onMouseOver: function(elm, d, i) {},
                    childrenItems: []
                },
                {
                    title: 'Show usage of node in diagram',
                    onMouseDown: function(elm, d, i) {
                        d3.selectAll(".treeFrame").attr("fill-opacity", .5);
                        d3.selectAll(".nodeText").style("fill-opacity", 0.5);					
                        highlight(d);
                    },
                    onMouseUp: function(elm, d, i) {				
                        d3.selectAll(".edgeBundlesLink")
                            .style("stroke-opacity",.4)
                        d3.selectAll(".treeFrame").attr("fill-opacity", .8);
                        d3.selectAll(".nodeText").style("fill-opacity", 1);					
                    },
                    onMouseOver: function(elm, d, i) {
                    },
                    childrenItems: []
                },
                {
                    title: 'Copy x-path to clipboard',
                    onMouseDown: function(elm, d, i) {
                        window.prompt("Copy to clipboard: Ctrl+C, Enter", d.xPath);
                        d3.select('.d3-context-menu').style('display', 'none');
                    },
                    onMouseUp: function(elm, d, i) {
                    },
                    onMouseOver: function(elm, d, i) {
                    },
                    childrenItems: []
                }
                ]
                
				//################################################################################################//	
				var headerDiv =  d3.select(".edgeBundlesDiv").append("div").attr("class","panel panel-primary")
				headerDiv.append("div").attr("class","panel-heading text-center")
					.append("h3")
					.attr("class","panel-title")
					.style("font-family","Arial")
					.style("font-size","20pt")
					.text("Edge Bundles View")
				var name_tmp="";
				if (currentGraph.name){name_tmp=currentGraph.name}
				else{name_tmp="Graph " + currentGraph.id}
				headerDiv.append("div").attr("class","panel-body")
					.style("font-family","Arial")
					.style("font-size","16pt")
					.text("Graph name: " + name_tmp)
				headerDiv.append("div").attr("class","panel-body")
					.style("font-family","Arial")
					.style("font-size","16pt")
					.text("Graph description: " + currentGraph.description)
				//################################################################################################//	
				
                
                var quickMenuDiv = d3.select(".edgeBundlesDiv").append("div").attr("class","quickMenuDiv")
                    .style("top","-10px")
                    .style("margin-left","10pt")
                var changeViewButton = quickMenuDiv.append("button")
                    .attr("class","btn btn-info")
                    .attr("data-toggle","tooltip")
                    .attr("data-placement","top")
                    .attr("title","Change visualization type")
                    .style("margin-left","10px")
                    .style("margin-bottom","10px")
                changeViewButton.append("span")
                    .attr("class","glyphicon glyphicon-random")
                    .attr("aria-hidden","true")
                changeViewButton.on("mousedown", function()
                {
                    bootbox.hideAll();
                 
                    bootbox.hideAll();
                    bootbox.prompt({
                        closeButton: false,
                        title: "Change visualization type",
                        inputType: 'select',
                        value: '',
                        inputOptions: [
                            {
                                text: 'Please choose...',
                                value: '',
                            },
                            {
                                text: 'XDSM view',
                                value: 'xdsm',
                            },
                            {
                                text: 'Edge Bundles view',
                                value: 'edgeBundles',
                            },
                            {
                                text: 'Sankey Diagram',
                                value: 'sankeyDiagram',
                            }
                        ],
                        callback: function (theView) {                            
                            if (theView == "xdsm")
                            {
                                clearView();
                                xdsm_script(data,currentGraph.id)
                            }
                            else if (theView == "edgeBundles")
                            {
                                clearView();
                                edgeBundles_script(data,currentGraph.id)
                            }
                            else if (theView == "sankeyDiagram")
                            {
                                clearView();
                                sankeyDiagram_script(data,currentGraph.id)
                            }
                            else
                            {}
                        }
                    });
                })
                
                
                
				//aigner: Tree option menu to select which kind of tree view the user wants to see
				//#####################################################################//
				function showFullTree(categoryID, categoryDescr)
				{
					var name_tmp = "Full data model tree view; Categorization: " + categoryDescr;
					var emptyArray="";
					var allLinks = d3.selectAll(".edgeBundlesLink");
					var theSchema = currentGraph.variableSchemes[categoryID];
					createTreeLayout(name_tmp,theSchema,emptyArray,allLinks,nodeMenu);
				}
				
				var childrenItems = [];
				for (var j=0; j< varCategories.length; j++)
				{
					childrenItems.push({title: 'according to ' + varCategories[j].description,
								   varCategory: varCategories[j].name,
								   description: varCategories[j].description,
								   onMouseClick: function(elm, d, i) {showFullTree(d.varCategory,d.description)},
								   onMouseOver: function(elm,d,i){}})
				}
				var treeOptionMenu = [
				{
					title: 'Show variable tree...',
					onMouseDown: function(elm, d, i) {
					},
					onMouseUp: function(elm, d, i) {
					},
					onMouseOver: function(elm, d, i) {
					},
					childrenItems: childrenItems
				}
				]
				
                
                function showList(aTitle,aList,aMenu)
                {
                    if (aList.length != 0)
                    {
                        var d3_body = d3.select("body");
                        
                        var panel_div = d3_body.append("div").attr("class", "myPanel panel-default")
                        panel_div.append("div").attr("class","panel-heading")
                            .append("div").attr("class","panel_title").append("h4").text(aTitle)
                        panel_div.append("input")
                            .attr("id","myInput")
                            .attr("placeholder","Filter search...")
                            .attr("title","Type in a name")
                            .attr("onkeyup","filterSearch()")
                        var listGroup = panel_div.append("div").attr("class","panel-body")
                            .append("table").attr("id","myTable")
                            .append("tbody")
                            
                        var tr = listGroup
                            .selectAll('tr')
                            .data(aList).enter()
                                .append('tr')
                        var td = tr.append("td").html(function(d) { 
                            if (d.xPath){return d.xPath;} 
                            else {return d.name;} 
                        })
                        tr.on('contextmenu', d3.contextMenu(aMenu));
                        $('.myPanel').lobiPanel({
                            reload: false,
                            editTitle: false,
                            expand: false,
                            unpin: false,
                            resize: "none",
                            minWidth: 200,
                            minHeight: 200,
                            maxWidth: 1100,
                            maxHeight: 1200,
                        });
                        $('.myPanel').lobiPanel('unpin');
                        $('.myPanel').lobiPanel('height','5000');
                    }
                }
                
                
                var graph_menu = "      <nav>"
                          +"          <ul>"
                          +"             <li><a href='#'>Graph inspection <span>&#x25BE;</span></a>"
                          +"                  <ul>"                        
                          +"                      <li><a href='#'>Inspect data model<span>&#x25B8;</span></a>"
                          +"                          <ul class='data_model_view'>"
                          +"                              <li class='tree_view'><a href='#'>Tree view<span>&#x25B8;</span></a></li>"
                          +"                              <li class='list_view'><a href='#'>List view<span>&#x25B8;</span></a></li>"
                          +"                          </ul>"
                          +"                      </li>"
                          +"                  </ul>"
                          +"              </li>"
                          +"          </ul>"
                          +"      </nav>"
                          
				d3.select(".edgeBundlesDiv").append('div').attr("class","graph_menu").style("margin-left","20px").html(graph_menu);                
                
                
                //aigner: Data Model Tree View Button
				//####################################################################################################################
				var dropdown1 = d3.select(".tree_view")
				var links = dropdown1.append("ul").attr("class","view_options");
				for (var j=0; j< varCategories.length; j++)
				{
					//console.log(varCategories[j])
					var linkLi = links.append("li");
					var linkA = linkLi.append("a")
						.attr("id",j)
						.text(varCategories[j].description)
						.on("mouseover", function(){d3.select(this).style("cursor", "pointer")})
						.on("click", function()
						{		
							showFullTree(varCategories[this.id].name,varCategories[this.id].description)
						})
				}
				//####################################################################################################################
                
                //aigner: Data Model List View Button
				//####################################################################################################################
				//aigner: Function, that shows a list of elements
                function showList(aTitle,aList,aMenu)
                {
                    if (aList.length != 0)
                    {
                        var d3_body = d3.select("body");
                        
                        var panel_div = d3_body.append("div").attr("class", "myPanel panel-default")
                        panel_div.append("div").attr("class","panel-heading")
                            .append("div").attr("class","panel_title").append("h4").text(aTitle)
                        panel_div.append("input")
                            .attr("id","myInput")
                            .attr("placeholder","Filter search...")
                            .attr("title","Type in a name")
                            .attr("onkeyup","filterSearch()")
                        var listGroup = panel_div.append("div").attr("class","panel-body")
                            .append("table").attr("id","myTable")
                            .append("tbody")
                            
                        var tr = listGroup
                            .selectAll('tr')
                            .data(aList).enter()
                                .append('tr')
                        var td = tr.append("td").html(function(d) { 
                            if (d.xPath){return d.xPath;} 
                            else {return d.name;} 
                        })
                        tr.on('contextmenu', d3.contextMenu(aMenu));
                        $('.myPanel').lobiPanel({
                            reload: false,
                            editTitle: false,
                            expand: false,
                            unpin: false,
                            resize: "none",
                            minWidth: 200,
                            minHeight: 200,
                            maxWidth: 1100,
                            maxHeight: 1200,
                        });
                        $('.myPanel').lobiPanel('unpin');
                    }
                }
                
                var dropdownList = d3.select(".list_view")
				var linksList = dropdownList.append("ul").attr("class","view_options");
				for (var j=0; j< varCategories.length; j++)
				{
					//console.log(varCategories[j])
					var linkLi = linksList.append("li");
					var linkA = linkLi.append("a")
						.attr("id",j)
						.text(varCategories[j].description)
						.on("mouseover", function(){d3.select(this).style("cursor", "pointer")})
						.on("click", function()
						{	
                            var variables = JSON.parse(JSON.stringify(currentGraph.variableSchemes[varCategories[this.id].name]));
                            variables.forEach(function(variable)
                            {
                                variable.name = variable.xPath
                                //work around because nodeMenu expexts the data, to have a "data" object inside
                                variable.data = variable
                                variable.data.name = variable.xPath.split("/")[variable.xPath.split("/").length-1]
                            })
                            var title = "List view: full variable set categorized according to " + varCategories[this.id].description
                            showList(title,variables,nodeMenu)
						})
				}
                
				d3.select(".graph_menu").moveToBack()
				d3.select(".quickMenuDiv").moveToBack()
				headerDiv.moveToBack()
				
				
				var nodes = cluster.nodes(packageHierarchy(classes));
				var links = packageImports(nodes);
				
				links.forEach(function(d)
				{
					d.pipeData_in = d.source.pipeline_data[d.target.name];
					d.pipeData_out = d.target.pipeline_data[d.source.name];
					d.pipeDataName_in = "";
					d.pipeDataName_out = "";
					if (d.pipeData_in)
					{
						for (var i=0; i<d.pipeData_in.length; i++)
						{
							d.pipeDataName_in += "," + d.pipeData_in[i];
						}
					}
					if (d.pipeData_out)
					{
						for (var i=0; i<d.pipeData_out.length; i++)
						{
							d.pipeDataName_out += "," + d.pipeData_out[i];
						}

					}
				})
					
				link = link
				  .data(bundle(links))
				.enter().append("path")
				  .each(function(d) { 
					d.source = d[0];
					d.target = d[d.length - 1];})
				  .attr("class", "edgeBundlesLink")
				  .attr("d", line)

				
				
				invisibleLink = invisibleLink
				  .data(bundle(links))
					.enter().append("path")
				  .each(function(d) { 
					d.source = d[0];
					d.target = d[d.length - 1];
					d.createNew = true;})
				  .attr("class", "invisibleLink")
				  .attr("d", line)
				  
					
				function showLinkTree(aLink,aVarCategory, aCategoryDescr)
				{
					var theSchema = currentGraph.variableSchemes[aVarCategory];
					var name_tmp = "Variable flow: " + aLink.__data__.source.name +  "  →  " + aLink.__data__.target.name + "; Categorization: " + aCategoryDescr;
					createTreeLayout(name_tmp,theSchema,aLink.__data__,link,nodeMenu);
				}
				
				function showEdgeTable(aLink)
				{						
					aLink.pipeData_in = aLink.source.pipeline_data[aLink.target.name];
					aLink.pipeData_out = aLink.target.pipeline_data[aLink.source.name];
					aLink.pipeDataName_in = "";
					aLink.pipeDataName_out = "";
					aLink.name = "";
					if (aLink.pipeData_in)
					{
						for (var i=0; i<aLink.pipeData_in.length; i++)
						{
							if (i==0){aLink.pipeDataName_in += aLink.pipeData_in[i];}
							else{aLink.pipeDataName_in += "," + aLink.pipeData_in[i];}
							
						}
						aLink.name += aLink.pipeDataName_in
					}
					if (aLink.pipeData_out)
					{
						for (var i=0; i<aLink.pipeData_out.length; i++)
						{
							if (i==0){aLink.pipeDataName_out += aLink.pipeData_out[i];}
							else{aLink.pipeDataName_out += "," + aLink.pipeData_out[i];}
						}
						aLink.name += aLink.pipeDataName_out
					}
                    
                   
					var headLine = "Edge Information (" + aLink.source.key + " - " + aLink.target.key + ")";					
					var pipeData_in = aLink.pipeDataName_in;
					var pipeData_out = aLink.pipeDataName_in;
                    var theLeafNodes_in = JSON.parse(JSON.stringify(currentGraph.variableSchemes[varCategories[0].name]));
                    var theLeafNodes_out = JSON.parse(JSON.stringify(currentGraph.variableSchemes[varCategories[0].name]));
					prune_tree(pipeData_in,theLeafNodes_in)
					prune_tree(pipeData_out,theLeafNodes_out)
                    
                    var nullDim_in=false;
                    var dimension_in=0;
                    var undefLeafsNumber_in=0;
					var defLeafsNumber_in=0;
                    var nullDim_out=false;
                    var dimension_out=0;
                    var undefLeafsNumber_out=0;
					var defLeafsNumber_out=0;
                    function getUndefinedLeafNodes(theLeafNodes,defLeafsNumber,undefLeafsNumber,dimenson,nullDim)
                    {
                        for (var k=0;k<theLeafNodes.length;k++)
                        {
                            if (theLeafNodes[k].dimension!=null){dimension = dimension+theLeafNodes[k].dimension}
                            else{nullDim=true}
                            if (theLeafNodes[k].value.includes("could not be found")||theLeafNodes[k].value.includes("unknown"))
                            {
                                undefLeafsNumber ++;
                            }
                            else
                            {
                                defLeafsNumber++;
                            }
                        }		
                    }
                    getUndefinedLeafNodes(theLeafNodes_in,defLeafsNumber_in,undefLeafsNumber_in,dimension_in,nullDim_in)
                    getUndefinedLeafNodes(theLeafNodes_out,defLeafsNumber_out,undefLeafsNumber_out,dimension_out,nullDim_out)
					//Render data for table
					var data = [];
					data.push({ "name" : "Total number of connections", "value" : aLink.pipeDataName_in.split(",").length+aLink.pipeDataName_out.split(",").length })
					
                    data.push({ "name" : "Total number of incoming connections", "value" : aLink.pipeDataName_in.split(",").length })
                    data.push({ "name" : "Number of referenced incoming connections", "value" : defLeafsNumber_in })
                    data.push({ "name" : "Dimension of referenced outgoing connections", "value" : String(dimension_in) })
					if (undefLeafsNumber_in>0)
					{
						data.push({ "name" : "Number of unreferenced incoming connections", "value" :  undefLeafsNumber_in})					
					}
					
                    data.push({ "name" : "Total number of outgoing connections", "value" : aLink.pipeDataName_out.split(",").length })
					data.push({ "name" : "Number of referenced outgoing connections", "value" : defLeafsNumber_out })
                    data.push({ "name" : "Dimension of referenced outgoing connections", "value" : String(dimension_out) })
                    if (undefLeafsNumber_out>0)
					{
						data.push({ "name" : "Number of unreferenced incoming connections", "value" :  undefLeafsNumber_out})					
					}
					
					
                    var d3_body = d3.select("body");
                    var panel_div = d3_body.append("div").attr("class", "myPanel panel-default")
                    panel_div.append("div").attr("class","panel-heading")
                        .append("div").attr("class","panel_title").append("h4").text(headLine)
                    var listGroup = panel_div.append("div").attr("class","panel-body")
                        .append("table").attr("id","myTable")
                        .append("tbody")
                        
                    data.forEach(function(listElement)
                    {
                        var row = listGroup.append("tr")
                        row.append("td").text(listElement.name)
                        row.append("td").text(listElement.value)
                        
                    })
                    $('.myPanel').lobiPanel({
                        reload: false,
                        editTitle: false,
                        expand: false,
                        unpin: false,
                        resize: "none",
                        minWidth: 200,
                        minHeight: 200,
                        maxWidth: 1100,
                        maxHeight: 1200,
                    });
                    $('.myPanel').lobiPanel('unpin');
				}
				
				//linkMenu --> functions for right click options
				var linkChildrenItemsTree = [];
				for (var j=0; j< varCategories.length; j++)
				{
					linkChildrenItemsTree.push({title: 'according to ' + varCategories[j].description,
											varCategory: varCategories[j].name,
											description: varCategories[j].description,
											onMouseClick: function(elm, data, i) {showLinkTree(elm,data.varCategory,data.description)},
											onMouseOver: function(elm,data,i){}});
				}
                var linkChildrenItemsList = [];
                for (var j=0; j< varCategories.length; j++)
                {
                    linkChildrenItemsList.push({title: 'according to ' + varCategories[j].description,
                        varCategory: varCategories[j].name,
                        description: varCategories[j].description,
                        onMouseClick: function(elm, data, i) {                           
                            var title = "List view of variable flow: " + elm.__data__.source.id +  "  →  " + elm.__data__.target.id + "; Categorization: " + data.description;
                            var variables = prepareTreeData(currentGraph.variableSchemes[data.varCategory],elm.__data__)
                            variables.forEach(function(variable)
                            {
                                variable.name = variable.xPath
                                //work around because nodeMenu expexts the data, to have a "data" object inside
                                variable.data = variable
                                variable.data.name = variable.xPath.split("/")[variable.xPath.split("/").length-1]
                            })
                            
                            showList(title,variables,nodeMenu);
                        },
                        onMouseOver: function(elm,data,i){}});
                }
                
				var linkMenu = [
				{
					title: 'Show edge info',
					onMouseDown: function(elm, k, i) {
						showEdgeTable(k)
					},
					onMouseUp: function(elm, k, i) {
					},
					onMouseOver: function(elm, d, i) {
					},
					childrenItems: []
				},
				{
					title: 'Show variable tree...',
					onMouseDown: function(elm, k, i) {
					},
					onMouseUp: function(elm, k, i) {
					},
					onMouseOver: function(elm, d, i) {
					},
					childrenItems: linkChildrenItemsTree
				},
				{
					title: 'Show variable list...',
					onMouseDown: function(elm, k, i) {
					},
					onMouseUp: function(elm, k, i) {
					},
					onMouseOver: function(elm, d, i) {
					},
					childrenItems: linkChildrenItemsList
				}
				]  
				invisibleLink.append("svg:title").text("Click right to inspect")
				invisibleLink
				.on("mouseover", function(){d3.select(this).style("cursor", "pointer")})
				.on('contextmenu', d3.contextMenu(linkMenu));
				
				//aigner: CAUTION, HACK --> delete the obsolete object that contains duplicate information
				nodes.forEach(function(aNode,i)
				{
					if (aNode.children)
					{
						var index = nodes.indexOf(aNode);
						if (index > -1) 
						{
							nodes.splice(index, 1);
						}
					}
				});
				
				//aigner: Create the nodes in a circular view
				node = node
				  .data(nodes)
				.enter().append("text")
				  .attr("class", "edgeBundlesNode")
				  .attr("dy", ".31em")
				  .attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + (d.y + 8) + ",0)" + (d.x < 180 ? "" : "rotate(180)"); })
				  .style("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
				  .style("font-family","Arial")
				  .style("font-size","16pt")
				  .text(function(d) { return d.name; })
				  .on("mouseover", nodeMouseovered)
				  .on("mouseout", nodeMouseouted);
				
				//aigner: Right click options for tools
				//##############################################################################################################################
				//aigner: assign additional node information to nodes coming from xdsm information	
				var theXDSM = currentGraph.xdsm;
				nodes.forEach(function(aNode,i)
				{
					var xdsmNodes = theXDSM.nodes;
					for (var i=0; i< xdsmNodes.length; i++)
					{
						var xdsmNode = xdsmNodes[i];
						if (xdsmNode.name == aNode.name)
						{
							aNode.id = xdsmNode.id;
							aNode.metadata = xdsmNode.metadata;
							aNode.type = xdsmNode.type;
						}
					}
				});		
				
				//aigner: Table for competence/tool information
				//############################################################
                function showToolTable(aTool)
                {
                    var aToolNameSplit = aTool.name.split(': ')			
                    var headLine;
                    if (aToolNameSplit.length>1){headLine = "Competence Information: (" + aToolNameSplit[1] + ")";}
                    else {headLine = "Competence Information: (" + aToolNameSplit[0] + ")";}
                    
                    
                    var data = [];
                    // render the table(s)
                    if (aTool.metadata.length==0)
                    {
                        data.push({ "name" : "NO TOOL METADATA AVAILABLE", "value" : "..." })
                    }
                    function findSubMetaData(aMetaData)
                    {
                        for(var key in aMetaData)
                        {
                            if (typeof aMetaData[key] === 'object')
                            {
                                data.push({ "name" : key, "value" : ''}) ;
                                findSubMetaData(aMetaData[key]);
                            }
                            else
                            {
                                data.push({ "name" : key, "value" : aMetaData[key] })
                            }
                        }
                    }
                    for (var j=0; j < aTool.metadata.length;j++)
                    {
                        var metaData = aTool.metadata[j];
                        findSubMetaData(metaData);						
                    }
                
                    
                    var d3_body = d3.select("body");
                    
                    var panel_div = d3_body.append("div").attr("class", "myPanel panel-default")
                    panel_div.append("div").attr("class","panel-heading")
                        .append("div").attr("class","panel_title").append("h4").text(headLine)
                    var listGroup = panel_div.append("div").attr("class","panel-body")
                        .append("table").attr("id","myTable")
                        .append("tbody")
                        
                    data.forEach(function(listElement)
                    {
                        var row = listGroup.append("tr")
                        row.append("td").text(listElement.name)
                        row.append("td").text(listElement.value)
                        
                    })
                    $('.myPanel').lobiPanel({
                        reload: false,
                        editTitle: false,
                        expand: false,
                        unpin: false,
                        resize: "none",
                        minWidth: 200,
                        minHeight: 200,
                        maxWidth: 1100,
                        maxHeight: 1200,
                    });
                    $('.myPanel').lobiPanel('unpin');
                }
				//############################################################
				
				//aigner: Creation of input/output tree
				//############################################################
				function showIOTree(categoryName, categoryDescr, theNode, io)
				{	
					var links = d3.selectAll(".edgeBundlesLink")[0];
					var array={name_in:"",name_out:"",name:""};
					var name_tmp = "";
								
					links.forEach(function(d)
					{	
						var theLink = d.__data__;
						
						
						var pipeData_in = theLink.source.pipeline_data[theLink.target.name];
						var pipeData_out = theLink.target.pipeline_data[theLink.source.name];
						if (!pipeData_in){pipeData_in = [];}
						if (!pipeData_out){pipeData_out = [];}
						theLink.pipeData_in = theLink.source.pipeline_data[theLink.target.name];
						theLink.pipeData_out = theLink.target.pipeline_data[theLink.source.name];
						theLink.name_in = "";
						theLink.name_out = "";
						theLink.name = "";
						if (theLink.pipeData_in)
						{
							for (var i=0; i<theLink.pipeData_in.length; i++)
							{
								if (i==0){theLink.name_in += theLink.pipeData_in[i];}
								else{theLink.name_in += "," + theLink.pipeData_in[i];}
								
							}
							theLink.name += theLink.name_in
						}
						if (theLink.pipeData_out)
						{
							for (var i=0; i<theLink.pipeData_out.length; i++)
							{
								if (i==0){theLink.name_out += theLink.pipeData_out[i];}
								else{theLink.name_out += "," + theLink.pipeData_out[i];}
							}
							theLink.name += theLink.name_out
						}
					
						if (io=="in")
						{
							if (theLink.target == theNode)
							{
								if (array.name_in == ""){array.name_in +=theLink.name_out;}
								else{array.name_in += "," + theLink.name_out;}
								array.name_out = "";
								array.name = array.name_in;
							}
							if (theLink.source == theNode)
							{
								if (array.name_in == ""){array.name_in +=theLink.name_in;}
								else{array.name_in += "," + theLink.name_in;}
								array.name_out = "";
								array.name = array.name_in;
							}
							name_tmp = "Input tree view: " + theNode.id + "; Categorization: " + categoryDescr;
						}
						else if (io=="out")
						{
							if (theLink.source == theNode)
							{
								if (array.name_out == ""){array.name_out +=theLink.name_out;}
								else{array.name_out += "," + theLink.name_out;}
								array.name_in = "";
								array.name = array.name_out;
							}
							if (theLink.target == theNode)
							{
								if (array.name_out == ""){array.name_out +=theLink.name_in;}
								else{array.name_out += "," + theLink.name_in;}
								array.name_in = "";
								array.name = array.name_out;
							}
							name_tmp = "Output tree view: " + theNode.id + "; Categorization: " + categoryDescr;
						}
					})		
					var theSchema = currentGraph.variableSchemes[categoryName]		
					createTreeLayout(name_tmp,theSchema,array,link,nodeMenu)
				}
				//############################################################
                
                //aigner: Creation of input/output list
				//############################################################
				function prepareIOList(theNode, io)
				{	
					var links = d3.selectAll(".edgeBundlesLink")[0];
					var array={name_in:"",name_out:"",name:""};
								
					links.forEach(function(d)
					{	
						var theLink = d.__data__;
						
						
						var pipeData_in = theLink.source.pipeline_data[theLink.target.name];
						var pipeData_out = theLink.target.pipeline_data[theLink.source.name];
						if (!pipeData_in){pipeData_in = [];}
						if (!pipeData_out){pipeData_out = [];}
						theLink.pipeData_in = theLink.source.pipeline_data[theLink.target.name];
						theLink.pipeData_out = theLink.target.pipeline_data[theLink.source.name];
						theLink.name_in = "";
						theLink.name_out = "";
						theLink.name = "";
						if (theLink.pipeData_in)
						{
							for (var i=0; i<theLink.pipeData_in.length; i++)
							{
								if (i==0){theLink.name_in += theLink.pipeData_in[i];}
								else{theLink.name_in += "," + theLink.pipeData_in[i];}
								
							}
							theLink.name += theLink.name_in
						}
						if (theLink.pipeData_out)
						{
							for (var i=0; i<theLink.pipeData_out.length; i++)
							{
								if (i==0){theLink.name_out += theLink.pipeData_out[i];}
								else{theLink.name_out += "," + theLink.pipeData_out[i];}
							}
							theLink.name += theLink.name_out
						}
					
						if (io=="in")
						{
							if (theLink.target == theNode)
							{
								if (array.name_in == ""){array.name_in +=theLink.name_out;}
								else{array.name_in += "," + theLink.name_out;}
								array.name_out = "";
								array.name = array.name_in;
							}
							if (theLink.source == theNode)
							{
								if (array.name_in == ""){array.name_in +=theLink.name_in;}
								else{array.name_in += "," + theLink.name_in;}
								array.name_out = "";
								array.name = array.name_in;
							}
						}
						else if (io=="out")
						{
							if (theLink.source == theNode)
							{
								if (array.name_out == ""){array.name_out +=theLink.name_out;}
								else{array.name_out += "," + theLink.name_out;}
								array.name_in = "";
								array.name = array.name_out;
							}
							if (theLink.target == theNode)
							{
								if (array.name_out == ""){array.name_out +=theLink.name_in;}
								else{array.name_out += "," + theLink.name_in;}
								array.name_in = "";
								array.name = array.name_out;
							}
						}
					})		
					return array;
				}
				//############################################################
				
				var inputChildrenitemsTree = [];
				var outputChildrenitemsTree = [];
				for (var j=0; j< varCategories.length; j++)
				{
					inputChildrenitemsTree.push({title: 'according to ' + varCategories[j].description,
											 varCategory: varCategories[j].name,
											 description: varCategories[j].description,
											 onMouseClick: function(elm, data, i) {showIOTree(data.varCategory,data.description,elm.__data__,"in")},
											 onMouseOver: function(elm,data,i){}});
					outputChildrenitemsTree.push({title: 'according to ' + varCategories[j].description,
											 varCategory: varCategories[j].name,
											 description: varCategories[j].description,
											 onMouseClick: function(elm, data, i) {showIOTree(data.varCategory,data.description,elm.__data__,"out")},
											 onMouseOver: function(elm,data,i){}});
				}
                var inputChildrenitemsList = [];
				var outputChildrenitemsList = [];
				for (var j=0; j< varCategories.length; j++)
				{
					inputChildrenitemsList.push({title: 'according to ' + varCategories[j].description,
											 varCategory: varCategories[j].name,
											 description: varCategories[j].description,
											 onMouseClick: function(elm, data, i) {
                                                 var array = prepareIOList(elm.__data__,"in")
                                                 var variables = prepareTreeData(currentGraph.variableSchemes[data.varCategory],array)
                                                 var title = "List view of all inputs for " + elm.__data__.id + "; Categorization: " + data.description;
                                                 showList(title,variables,nodeMenu)
                                             },
											 onMouseOver: function(elm,data,i){}});
					outputChildrenitemsList.push({title: 'according to ' + varCategories[j].description,
											 varCategory: varCategories[j].name,
											 description: varCategories[j].description,
											 onMouseClick: function(elm, data, i) {
                                                var array = prepareIOList(elm.__data__,"out")
                                                var variables = prepareTreeData(currentGraph.variableSchemes[data.varCategory],array)
                                                var title = "List view of all outputs from " + elm.__data__.id + "; Categorization: " + data.description;
                                                showList(title,variables,nodeMenu)
                                             },
											 onMouseOver: function(elm,data,i){}});
				}
				//menu --> functions for right click options
				var toolMenu = [
				{
					title: 'Show competence info',
					onMouseDown: function(elm, k, i) {
						showToolTable(k);
					},
					onMouseUp: function(elm, k, i) {
					},
					onMouseOver: function(elm, d, i) {
					},
					childrenItems: []
				},
				{
					title: 'Show input variable tree...',
					onMouseDown: function(elm, k, i) {
					},
					onMouseUp: function(elm, k, i) {
					},
					onMouseOver: function(elm, d, i) {
					},
					childrenItems: inputChildrenitemsTree
				},
				{
					title: 'Show output variable tree...',
					onMouseDown: function(elm, k, i) {
					},
					onMouseUp: function(elm, k, i) {
					},
					onMouseOver: function(elm, d, i) {
					},
					childrenItems: outputChildrenitemsTree
				},
				{
					title: 'Show input variable list...',
					onMouseDown: function(elm, k, i) {
					},
					onMouseUp: function(elm, k, i) {
					},
					onMouseOver: function(elm, d, i) {
					},
					childrenItems: inputChildrenitemsList
				},
				{
					title: 'Show output variable list...',
					onMouseDown: function(elm, k, i) {
					},
					onMouseUp: function(elm, k, i) {
					},
					onMouseOver: function(elm, d, i) {
					},
					childrenItems: outputChildrenitemsList
				}
				]
				
				node.on('contextmenu', d3.contextMenu(toolMenu));
				//##############################################################################################################################
			};
			startEdgeBundles(data,graphID);

			function nodeMouseovered(d) {
				d3.select(this).append("svg:title").text("Click right to inspect");
				d3.select(this).style("cursor", "pointer")
			  node
				  .each(function(n) { 
				  n.target = n.source = false; });

			  link
				  .classed("edgeBundlesLink--target", function(l) { if (l.target === d) return l.source.source = true; })
				  .classed("edgeBundlesLink--source", function(l) { if (l.source === d) return l.target.target = true; })
				  .filter(function(l) { return l.target === d || l.source === d; })
				  .each(function() { this.parentNode.appendChild(this); });
				  
			  node
				  .classed("edgeBundlesNode--target", function(n) { return n.target; })
				  .classed("edgeBundlesNode--source", function(n) { return n.source; })
			}

			function nodeMouseouted(d) {
			  link
				  .classed("edgeBundlesLink--target", false)
				  .classed("edgeBundlesLink--source", false);

			  node
				  .classed("edgeBundlesNode--target", false)
				  .classed("edgeBundlesNode--source", false);
			}

			d3.select(self.frameElement).style("height", diameter + "px");

			// Lazily construct the package hierarchy from class names.
			function packageHierarchy(classes) {
			  var map = {};

			  function find(name, data) {
				var node = map[name], i;
				if (!node) {
				  node = map[name] = data || {name: name, children: []};
				  if (name.length) {
					node.parent = find(name.substring(0, i = name.lastIndexOf("/")));
					node.parent.children.push(node);
					node.key = name.substring(i + 1);
				  }
				}
				return node;
			  }

			  classes.forEach(function(d) {
				find(d.name, d);
			  });

			  return map[""];
			}

			// Return a list of imports for the given array of nodes.
			function packageImports(nodes) {
			  var map = {},
			  map2 = {},
			  input = [];

			  // Compute a map from name to node.
			  nodes.forEach(function(d) {
				map[d.name] = d;
				map2[d.pipeline_data] = d;
			  });

			  // For each import, construct a link from the source to target node.
			  nodes.forEach(function(d) 
			  {
				if(d.input)
				{
					d.input.forEach(function(i) 
					{
						if (map[i])
						{
							input.push({source: map[d.name], 
									target: map[i],
									pipeline_data: map2[d.pipeline_data]});	
						}
					})
				}

			   });
			  return input;
			}
            
            
            function prepareTreeData(aSchema,aLink)
            {
                var theLink = aLink;
				if (aLink.source && aLink.target)
				{
					var pipeData_in = theLink.source.pipeline_data[theLink.target.name];
					var pipeData_out = theLink.target.pipeline_data[theLink.source.name];
					if (!pipeData_in){pipeData_in = [];}
					if (!pipeData_out){pipeData_out = [];}
					theLink.pipeData_in = theLink.source.pipeline_data[theLink.target.name];
					theLink.pipeData_out = theLink.target.pipeline_data[theLink.source.name];
					theLink.name_in = "";
					theLink.name_out = "";
					theLink.name = "";
					if (theLink.pipeData_in)
					{
						for (var i=0; i<theLink.pipeData_in.length; i++)
						{
							if (i==0){theLink.name_in += theLink.pipeData_in[i];}
							else{theLink.name_in += "," + theLink.pipeData_in[i];}
							
						}
						theLink.name += theLink.name_in
					}
					if (theLink.pipeData_out)
					{
						for (var i=0; i<theLink.pipeData_out.length; i++)
						{
							if (i==0){theLink.name_out += theLink.pipeData_out[i];}
							else{theLink.name_out += "," + theLink.pipeData_out[i];}
						}
						theLink.name += theLink.name_out
					}
				}
                
                var treeData_in = (JSON.parse(JSON.stringify(aSchema)));				
                var treeData_out = (JSON.parse(JSON.stringify(aSchema)));				
                //aigner: Here, the minimalized tree is created!                    
                //The tree will only be pruned if there is pipeData, such as in an edge or for the input of a tool
                if (theLink)
                {
                    if (theLink.name_in)
                    {
                        prune_tree(theLink.name_in, treeData_in, "in");
                    }
                    else{treeData_in=[]}
                    if (theLink.name_out)
                    {
                        prune_tree(theLink.name_out, treeData_out, "out");
                    }
                    else{treeData_out=[]}
                }
                    
                //build tree layout for vistoms
                var treeData = treeData_in.concat(treeData_out)
                
                return treeData;
            }
            
			function createTreeLayout(theName, schema,aLink,theAllLinks,nodeMenu)
			{
                //aigner: Build the tree layout
                //######################################################################
				var treeData = prepareTreeData(schema, aLink)
                var newTree = {};
                buildTree(newTree, treeData)                    
                treeData = newTree
                //######################################################################
                
				var width= 1000;
				var height= 500;
				var xOffset = 10;
				var xOffset2 = 100;
				var canvas = d3.select(".circleSvg");
				// Calculate total nodes, max label length
				var totalNodes = 0;
				var maxLabelLength = 0;
				// variables for drag/drop
				var selectedNode = null;
				var draggingNode = null;
				// Misc. variables
				var i = 0;
				var duration = 500;
				var root;
				

				// size of the diagram
				var viewerWidth = width/3;
				var viewerHeight = height-50;

				var tree = d3.layout.tree()
					.size([viewerHeight, viewerWidth])

				// define a d3 diagonal projection for use by the node paths later on.
				var diagonal = d3.svg.diagonal()
					.projection(function(d) {
						return [d.y+xOffset, d.x];
					});

				// A recursive helper function for performing some setup by walking through all nodes

				function visit(parent, visitFn, childrenFn) {
					if (!parent) return;

					visitFn(parent);

					var children = childrenFn(parent);
					if (children) {
						var count = children.length;
						for (var i = 0; i < count; i++) {
							visit(children[i], visitFn, childrenFn);
						}
					}
				}

				// Call visit function to establish maxLabelLength
				visit(treeData, function(d) {
					totalNodes++;
					maxLabelLength = Math.max(d.name.length, maxLabelLength);
				}, function(d) {
					return d.children && d.children.length > 0 ? d.children : null;
				});
				
				function getMaxLength(data)
				{
					var maxLen=0;;
					for (var i = 0; i < data.length; i++)
					{
						maxLen = Math.max(data[i].name.length, maxLen);
					}
					for (var i = 0; i < data.length; i++)
					{
						data[i].labelLength = maxLen;
						if (data[i].children)
						{getMaxLength(data[i].children);}
						if (data[i]._children)
						{getMaxLength(data[i]._children);}
					}
					
				}
				if(treeData._children)
				{getMaxLength(treeData._children);}
				if(treeData.children)
				{getMaxLength(treeData.children);}
				treeData.labelLength = treeData.name.length;

				// sort the tree according to the node names
				function sortTree() {
					tree.sort(function(a, b) {
						return b.name.toLowerCase() < a.name.toLowerCase() ? 1 : -1;
					});
				}
				// Sort the tree initially incase the JSON isn't in a sorted order.
				//sortTree()


				// Collapse the node and all it's children
				function collapse(d) {
				  if(d.children) {
					d._children = d.children
					d._children.forEach(collapse)
					d.children = null
				  }
				}
				// Collapse the node and all it's children
				function expand(d) {
				  if(d._children) {
					d.children = d._children
					d.children.forEach(expand)
					d._children = null
				  }
				}
				// Toggle children on click.
				function click(d) 
				{
					if (d.children) {
						d._children = d.children;
						d.children = null;
					} else 
					{
						d.children = d._children;
						d._children = null;
					}
					update(d,theAllLinks);
				}
				// Collapse/expand entire tree on double-click
				function dblclick(d) 
				{
					if(d.children) 
					{
						collapse(d);
					}
					else if(d._children)
					{
						expand(d);
					}
					update(d,theAllLinks);
				}	

				
				
				//aigner: Here the tree layout is created
                var d3_body = d3.select("body")
                var lobiID = String(getRandomInt(0,1000))
                var divClassName = "treeDiv"+lobiID
                var treeLayoutdiv = d3_body.append("div").attr("class",divClassName+" panel-default")
                                                .style("left","200px")
                                                .style("top","200px")
                                                .style("position", "absolute")
                treeLayoutdiv.append("div").attr("class","panel-heading")
                    .append("div").attr("class","panel_title").append("h4").text(theName)
                $('.'+divClassName).lobiPanel({
                    reload: false,
                    editTitle: false,
                    unpin: false,
                    minWidth: 200,
                    maxWidth: 100000,
                    minHeight: 200,
                    maxHeight: 100000,
                });
                $('.'+divClassName).lobiPanel('unpin');										
				var treeLayoutSVG = treeLayoutdiv.append("svg").attr("class","treeLayoutSVG")
				var treeGroup = treeLayoutSVG.append("g").attr("class","treeGroup").style("position","absolute")
								.attr("transform", "translate(50,0)");
				var margin = {top: 20, right: 90, bottom: 20, left: 90},
							  width = 960 - margin.left - margin.right,
							  height = 500 - margin.top - margin.bottom;

				// append the svg object to the body of the page
				// appends a 'group' element to 'svg'
				// moves the 'group' element to the top left margin
				var offset_tmp = 60;
				treeLayoutSVG = treeLayoutSVG.attr("width", width + 1.2*margin.right + margin.left+offset_tmp)
						 .attr("height", height + margin.top + margin.bottom+offset_tmp);
				treeLayoutdiv.attr("width", width + 1.2*margin.right + margin.left+offset_tmp)
						 .attr("height", height + margin.top + margin.bottom+offset_tmp)
						 .on("mouseover",function(){d3.select(this).style("cursor", "grab")})
				treeGroup = treeGroup
					.attr("width", width + margin.right + margin.left)
					.attr("height", height + margin.top + margin.bottom);
					
				
				
				var frame = treeGroup.append("rect")
					.attr("class","treeFrame")
					.attr("stroke-width", 1)
					.attr("stroke", "white")
					.attr("fill-opacity", .8);				
				
				
				// Define the root
				root = treeData;
				root.x0 = 0;
				root.y0 = 0;
				
				// Append a group which holds all nodes and which the zoom Listener can act upon.
				var svgGroup_xOff = root.name.length*10+50
				var svgGroup = treeGroup.append("g")
					.attr("class","tree_"+theName)
					.attr("transform", "translate("+ svgGroup_xOff + "," + String(margin.top+10) + ")");
				
				collapse(root);
				update(root,theAllLinks);
			
				
				function update(source,allLinks) {
					// Compute the new height, function counts total children of root node and sets tree height accordingly.
					// This prevents the layout looking squashed when new nodes are made visible or looking sparse when nodes are removed
					// This makes the layout more consistent.
					var levelWidth = [1];
					var levelLength = [1];
					var childCount = function(level, n) {

						if (n.children && n.children.length > 0) {
							if (levelWidth.length <= level + 1) levelWidth.push(0);
							
							levelWidth[level + 1] += n.children.length;
							n.children.forEach(function(d) {
								childCount(level + 1, d);
							});
						}
					};
					childCount(0, root);

					var newHeight = d3.max(levelWidth) * 60; // 20 pixels per line
					var newWidth = viewerWidth+300;
					
					//BENNI: fill in viewerHeight instead of newHeight for other expanding/collapsing beaviour
					tree = tree.size([newHeight, newWidth]);
					
					frame = frame.attr("width",newWidth)
						.attr("height",newHeight+60)
						.attr("fill","white");
						
					
					
					// Compute the new tree layout.
					var nodes = tree.nodes(root),
						links = tree.links(nodes);
						
						
					//aigner: count all descendants of a node
					function countDescendants(node, counter) 
					{
						if (node._children)
						{
							node._children.forEach(function(n)
							{
								counter = countDescendants(n, counter);
							});
						}
						else if (node.children)
						{
							node.children.forEach(function(n)
							{
								counter = countDescendants(n, counter);
							});
						}
						else
						{
							counter ++;
						}
						return counter;
					}
					
					
					var depths = [];
					var labelLength = [];
					nodes.forEach(function(d) 
					{				
						//aigner: If node is collapsed show number of ancestors
						if (d._children)
						{
							d._childrenNum = countDescendants(d, 0);
							d.text = d.name + " (" + d._childrenNum + ")";
						}
						else 
						{
							d.text = d.name
						}
						
						//aigner: Find maximum labelLength for each level
						if (!depths.includes(d.depth))
						{
							depths.push(d.depth);
							labelLength.push(d.text.length);
						}
						else
						{
							if (d.depth!=0){labelLength[depths.indexOf(d.depth)] = Math.max(d.text.length, labelLength[depths.indexOf(d.depth)]);}
						}
					});
					// Set widths between levels based on labelLength of each level.
					var newWidth=0;	
					var depth_tmp = 0;			
					nodes.forEach(function(d) 
					{
						if (d.parent)
						{
							d.y = d.parent.y+(labelLength[depths.indexOf(d.depth)])*10+50;
						}
						else{d.y=0}
						
						
						if (newWidth<d.y)
						{newWidth=d.y;}
						
						// console.log("##############")
						// console.log(d.name)
						// console.log("x = " + d.x)
						// console.log("y = " + d.y)
						// console.log("depth = " + depths.indexOf(d.depth))
						// console.log("##############")
					});	
					
					
					newWidth=Math.max(newWidth,getTextWidth(theName,"Arial 12pt"));					
					//aigner: Adjust height and width of the frame
					$('.'+divClassName).lobiPanel('setWidth', newWidth + margin.top + margin.bottom +400+maxLabelLength*25+offset_tmp);
                    $('.'+divClassName).lobiPanel('setHeight', newHeight + margin.top + 2*margin.bottom+offset_tmp);
					treeLayoutSVG = treeLayoutSVG.attr("height", newHeight + margin.top + margin.bottom+offset_tmp)
					treeLayoutSVG = treeLayoutSVG.attr("width", newWidth + margin.top + margin.bottom +300+maxLabelLength*25+offset_tmp)
					frame = frame.attr("height", newHeight + margin.top + margin.bottom)
					frame = frame.attr("width", newWidth + margin.top + margin.bottom+300+maxLabelLength*15)


					// Update the nodes…
					var treeNode = svgGroup.selectAll("g.treeNode")
						.data(nodes, function(d) {
							return d.id || (d.id = ++i);
						});

					var dblclick_timer = false;
					// Enter any new nodes at the parent's previous position.
					var nodeEnter = treeNode.enter().append('g')
					  .attr('class', 'treeNode')
					  .attr("transform", function(d) {
						return "translate(" + source.y0 + "," + source.x0 + ")";
					})
					.on("mousedown", function(d) {
						if (d3.event.which != 3)
						{
							// if double click timer is active, this click is the double click
							if ( dblclick_timer )
							{
								clearTimeout(dblclick_timer)
								dblclick_timer = false
								// double click code code comes here
								//console.log("DOUBLE CLICK")
								dblclick(d);
							}
							// otherwise, what to do after single click (double click has timed out)
							else dblclick_timer = setTimeout( function(){
								dblclick_timer = false
								// single click code code comes here
								//console.log("SINGLE CLICK")
								click(d);
							}, 250)
						}
					})
						
					
					nodeEnter.append("circle")
						.attr('class', 'nodeCircle')
						.attr("r", 0)
						.style("fill", function(d) {
							if (d._children)
							{
								if(d.pipeLineIn && !d.pipeLineOut){return '#ea9999'}
								else if(!d.pipeLineIn && d.pipeLineOut){return '#d6ea99'}
								else {return "lightsteelblue"}
							}
							else{return "#fff";}
						})		
						.style("stroke", function(d) {
							if(d.pipeLineIn && !d.pipeLineOut){
								//console.log(d);
								return '#CC0000'}
							else if(!d.pipeLineIn && d.pipeLineOut){
								//console.log(d);
								return '#99CC00'}
						})
						.attr("cx", function(d) {return xOffset});

					nodeEnter.append("text")
						.attr("x", function(d) 
						{
								return d.children || d._children ? -10+xOffset : 10+xOffset;
						})
						.attr("dy", ".35em")
						.attr('class', 'nodeText')
						.attr("text-anchor", function(d) {
							return d.children || d._children ? "end" : "start";
						})
						.text(function(d) {
							return d.text;
						})
						.style("fill-opacity", 0)
						

					// Update the text to reflect whether node has children or not.
					treeNode.select('text')
						.attr("x", function(d) {
							return d.children || d._children ? -10+xOffset : 10+xOffset;
						})
						.attr("text-anchor", function(d) {
							return d.children || d._children ? "end" : "start";
						})
						.text(function(d) {
							return d.text;
						});

					// Change the circle fill depending on whether it has children and is collapsed
					treeNode.select("circle.nodeCircle")
						.attr("r", 4.5)
						.style("fill", function(d) {
							if (d._children)
							{
								if(d.pipeLineIn && !d.pipeLineOut){return '#ea9999'}
								else if(!d.pipeLineIn && d.pipeLineOut){return '#d6ea99'}
								else {return "lightsteelblue"}
							}
							else{return "#fff";}
						})			
					
                    //Function writeTreeToXML goes through tree nodes and puts the into an xml document
                    function writeTreeToXML(aNode,anXMLDoc,anXPath)
                    {
                        //Variable "children" 
                        //--> One children variable, no matter whether a node has "_children" (collapsed) or "children" (expanded)
                        var children;
                        if (aNode._children){children = aNode._children;}
                        else if (aNode.children){children = aNode.children;}
                        
                        //Get current xml element with its xPath
                        var element = anXMLDoc.evaluate(anXPath,anXMLDoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ).singleNodeValue;
                        if (element != null) {element.value = '...';}
                        
                        //If a node has children (collapsed or expanded), loop through them
                        if (children)
                        {
                            for (var i=0; i < children.length;i++)
                            {
                                //Name of the new XML element --> childName
                                var child = children[i];
                                var childName = child.name.split(/[\[\]]+/);//Split childName at "[]" which is the uID 
                                var cleanChildName = childName[0].split(/[\+\*\^\-\ \#]+/);//Split childName all special characters
                                var newNode = anXMLDoc.createElement(String(cleanChildName[0]));
                                
                                //The children are appended to the xPath --> newXPath
                                var newXPath = anXPath+"/"+cleanChildName[0];
                                
                                //If childName contains a uID, make the uID an attribute
                                if (childName[1])
                                {
                                    if (parseInt(childName[1]))
                                    {
                                        var dummyID = childName[1];
                                        newNode.setAttribute("dummyID", dummyID)
                                        newXPath = newXPath+"[@dummyID='"+dummyID+"']";
                                    }
                                    else
                                    {
                                        var uID = childName[1];
                                        newNode.setAttribute("uID", uID)
                                        newXPath = newXPath+"[@uID='"+uID+"']";
                                    }
                                }
                                if (cleanChildName.length>1) {newNode.setAttribute("elementName", childName[0])};
                                
                                //Append the newNode to the xml structure
                                element.appendChild(newNode);
                                
                                
                                
                                ////aigner: Sorting of XML elements according to "uID"
                                // var items = element.children;
                                // var itemsArr = [];
                                // for (var j in items) {
                                    // if (items[j].nodeType == 1) { // get rid of the whitespace text nodes
                                        // itemsArr.push(items[j]);
                                    // }
                                // }
                                // itemsArr.sort(function(a,b){
                                    // if (a.getAttribute("uID") < b.getAttribute("uID"))
                                        // return -1;
                                    // if (a.getAttribute("uID") > b.getAttribute("uID"))
                                        // return 1;
                                    // return 0;
                                    // });

                                // for (j = 0; j < itemsArr.length; ++j) {
                                  // element.appendChild(itemsArr[j]);
                                // }
                                
                                //call function writeTreeToXML recursively for all children
                                writeTreeToXML(child,anXMLDoc,newXPath)
                            }
                        }
                        else
                        {
                            if (aNode.value){element.innerHTML = String(aNode.value);}
                            else{element.innerHTML = " ";}
                        }
                        //return the xml document
                        return anXMLDoc;
                    }
                    
                    function removeAttributeInAllElements(aDocument,attribute)
                    {
                      var matchingElements = [];
                      var allElements = aDocument.getElementsByTagName('*');
                      for (var i = 0, n = allElements.length; i < n; i++)
                      {
                        if (allElements[i].getAttribute(attribute) !== null)
                        {
                            allElements[i].removeAttribute(attribute);
                        }
                      }
                      return matchingElements;
                    }
                    
                    function putAncestorsInXMLString(strWrapper, aNode)
                    {
                        //Name of the new XML element
                        var nodeName = aNode.name.split(/[\[\]]+/);//Split name at "[]" which is the uID 
                        var cleanNodeName = nodeName[0].split(/[\+\*\^\-\ \#]+/);//Split nodeName all special characters
                        //If nodeName contains a uID, make the uID an attribute
                        if (nodeName[1])
                        {
                            var uID = nodeName[1];
                            strWrapper.val = "<"+cleanNodeName[0]+" uID='"+ uID +"'>"+strWrapper.val+"</"+cleanNodeName[0]+">";;
                        }
                        else
                        {
                            strWrapper.val = "<"+cleanNodeName[0]+">"+strWrapper.val+"</"+cleanNodeName[0]+">";;
                        }
                        var aParent = aNode.parent;
                        if (aParent)
                        {
                            return putAncestorsInXMLString(strWrapper, aParent);
                        }
                        else 
                        {
                            return strWrapper;
                        }
                    }
                    
                    function putAncestorsInXPath(strWrapper, aNode)
                    {
                        //Name of the new XML element
                        var nodeName = aNode.name.split(/[\[\]]+/);//Split name at "[]" which is the uID 
                        var cleanNodeName = nodeName[0].split(/[\+\*\^\-\ \#]+/);//Split nodeName all special characters
                        //If nodeName contains a uID, make the uID an attribute
                        if (nodeName[1])
                        {
                            var uID = nodeName[1];
                            strWrapper.val = "/"+cleanNodeName[0]+"[@uID='"+uID+"']"+strWrapper.val;
                        }
                        else
                        {
                            strWrapper.val = "/"+cleanNodeName[0]+strWrapper.val;
                        }
                        var aParent = aNode.parent;
                        if (aParent)
                        {
                            return putAncestorsInXPath(strWrapper, aParent);
                        }
                        else 
                        {
                            return strWrapper;
                        }
                    }
                    
                    // Function to download data to a file
                    function download(filename, text) {
                        var element = document.createElement('a');
                        element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
                        element.setAttribute('download', filename);

                        element.style.display = 'none';
                        document.body.appendChild(element);

                        element.click();

                        document.body.removeChild(element);
                    }
                    
                    
                    var nodeTreeMenu = nodeMenu;
                    var treeMenu = [
                    {
						title: 'Download full tree as XML-file',
						onMouseDown: function(elm, d, i) {
							//Begin xml structure with the first element
							var xmlString = "<"+nodes[0].name+">"+"</"+nodes[0].name+">";
							//Create a new xml document
							var parser = new DOMParser();
							var xmlDocument = parser.parseFromString(xmlString, "text/xml"); //important to use "text/xml"
							//Get initial xPath of the tree and pass it to the function "writeTreeToXML"
							var initialXPath = "/"+nodes[0].name;								
							writeTreeToXML(nodes[0],xmlDocument,initialXPath);
							//remove all attributes dummyID
							removeAttributeInAllElements(xmlDocument,'dummyID');
							//Make the xml document a string
							var serializer = new XMLSerializer();
							var xmlString = serializer.serializeToString(xmlDocument);
							xmlString = vkbeautify.xml(xmlString);														
							//Download a document with the xml-schema
							download(theName+'_full.xml', xmlString);						
						},
						onMouseUp: function(elm, d, i) {
						},
						onMouseOver: function(elm, d, i) {
						},
						childrenItems: []
					},
					{
						title: 'Download tree as XML-file from current node',
						onMouseDown: function(elm, d, i) {
							var xmlString = putAncestorsInXMLString({ val : '' }, d).val;
							var initialXPath = putAncestorsInXPath({ val : '' }, d).val;												
							//Create a new xml document
							var parser = new DOMParser();
							var xmlDocument = parser.parseFromString(xmlString, "text/xml"); //important to use "text/xml"
							//Get initial xPath of the tree and pass it to the function "writeCurrentTreeToXML"
							writeTreeToXML(d,xmlDocument,initialXPath);
							//remove all attributes dummyID
							removeAttributeInAllElements(xmlDocument,'dummyID');
							//Make the xml document a string
							var serializer = new XMLSerializer();
							xmlString = serializer.serializeToString(xmlDocument);
							xmlString = vkbeautify.xml(xmlString);														
							//Download a document with the xml-schema
							download(theName+"_"+d.name+'.xml', xmlString);						
						},
						onMouseUp: function(elm, d, i) {
						},
						onMouseOver: function(elm, d, i) {
						},
						childrenItems: []
					}
					]
                    var nodeTreeMenu = nodeMenu.concat(treeMenu)
					
					nodeEnter.append("svg:title").text("Click left to expand, click right to inspect")
					
					nodeEnter = nodeEnter.on('contextmenu', d3.contextMenu(nodeTreeMenu));
								
					// UPDATE		
					// Transition nodes to their new position.
					var nodeUpdate = treeNode.transition()
						.duration(duration)
						.attr("transform", function(d) {
							return "translate(" + d.y + "," + d.x + ")";
						});

					// Fade the text in
					nodeUpdate.select("text")
						.style("fill-opacity", 1);
					
					//New object for invisible nodes
					var exitNodes = [];
					
					// Remove any exiting nodes
					var nodeExit = treeNode.exit().transition()
					  .duration(duration)
					  .attr("transform", function(d) {
						  return "translate(" + source.y + "," + source.x + ")";
					  })
					  .remove();
					
					function translation(d) 
					{
						
						var closestAncestor;
						closestAncestor = findClosestAncestor(nodes,d);
						exitNodes.push(d);
						exitNodes[exitNodes.length-1].x = closestAncestor.x;
						exitNodes[exitNodes.length-1].y = closestAncestor.y;
						return "translate(" + closestAncestor.y + "," + closestAncestor.x + ")";
					}
					
					function findClosestAncestor(allNodes, element) 
					{
						var level = 0;
						var closestAncestor;
						for (var i=0;i<allNodes.length;i++)
						{
							if (!allNodes[i].level){allNodes[i].level=0};
							if (isDescendant(allNodes[i],element) && allNodes[i].level >= level)
							{
								level = allNodes[i].level;
								closestAncestor = allNodes[i];
							}
						}
						return closestAncestor;
					}
					
					function isDescendant(parent, child) 
					{
						 var node = child.parent;
						 while (node != null) 
						 {
							if (node == parent) 
							{
								return true;
							}
							node = node.parent;
						 }
						 return false;
					}
				

					// Update the links…
					var link = svgGroup.selectAll("path.treeLink")
						.data(links, function(d) {
							return d.target.id;
						});

					// Enter any new links at the parent's previous position.
					link.enter().insert("path", "g")
						.attr("class", "treeLink")
						.attr("d", function(d) {
							var o = {
								x: source.x0,
								y: source.y0
							};
							return diagonal({
								source: o,
								target: o
							});
						});

					// Transition links to their new position.
					link.transition()
						.duration(duration)
						.attr("d", diagonal);

					// Transition exiting nodes to the parent's new position.
					link.exit().transition()
						.duration(duration)
						.attr("d", function(d) {
							var o = {
								x: source.x,
								y: source.y
							};
							return diagonal({
								source: o,
								target: o
							});
						})
						.remove();
					
					// Stash the old positions for transition.
					nodes.forEach(function(d) {						
						d.x0 = d.x;
						d.y0 = d.y;
					});
				}
			}

			},{"d3":1}]},{},[2]);
		}
		
		function sankeyDiagram_script(data, graphID)
		{
			(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
			!function(){function n(n){return n&&(n.ownerDocument||n.document||n).documentElement}function t(n){return n&&(n.ownerDocument&&n.ownerDocument.defaultView||n.document&&n||n.defaultView)}function e(n,t){return t>n?-1:n>t?1:n>=t?0:NaN}function r(n){return null===n?NaN:+n}function i(n){return!isNaN(n)}function u(n){return{left:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)<0?r=u+1:i=u}return r},right:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)>0?i=u:r=u+1}return r}}}function o(n){return n.length}function a(n){for(var t=1;n*t%1;)t*=10;return t}function l(n,t){for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}function c(){this._=Object.create(null)}function f(n){return(n+="")===bo||n[0]===_o?_o+n:n}function s(n){return(n+="")[0]===_o?n.slice(1):n}function h(n){return f(n)in this._}function p(n){return(n=f(n))in this._&&delete this._[n]}function g(){var n=[];for(var t in this._)n.push(s(t));return n}function v(){var n=0;for(var t in this._)++n;return n}function d(){for(var n in this._)return!1;return!0}function y(){this._=Object.create(null)}function m(n){return n}function M(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function x(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(var e=0,r=wo.length;r>e;++e){var i=wo[e]+t;if(i in n)return i}}function b(){}function _(){}function w(n){function t(){for(var t,r=e,i=-1,u=r.length;++i<u;)(t=r[i].on)&&t.apply(this,arguments);return n}var e=[],r=new c;return t.on=function(t,i){var u,o=r.get(t);return arguments.length<2?o&&o.on:(o&&(o.on=null,e=e.slice(0,u=e.indexOf(o)).concat(e.slice(u+1)),r.remove(t)),i&&e.push(r.set(t,{on:i})),n)},t}function S(){ao.event.preventDefault()}function k(){for(var n,t=ao.event;n=t.sourceEvent;)t=n;return t}function N(n){for(var t=new _,e=0,r=arguments.length;++e<r;)t[arguments[e]]=w(t);return t.of=function(e,r){return function(i){try{var u=i.sourceEvent=ao.event;i.target=n,ao.event=i,t[i.type].apply(e,r)}finally{ao.event=u}}},t}function E(n){return ko(n,Co),n}function A(n){return"function"==typeof n?n:function(){return No(n,this)}}function C(n){return"function"==typeof n?n:function(){return Eo(n,this)}}function z(n,t){function e(){this.removeAttribute(n)}function r(){this.removeAttributeNS(n.space,n.local)}function i(){this.setAttribute(n,t)}function u(){this.setAttributeNS(n.space,n.local,t)}function o(){var e=t.apply(this,arguments);null==e?this.removeAttribute(n):this.setAttribute(n,e)}function a(){var e=t.apply(this,arguments);null==e?this.removeAttributeNS(n.space,n.local):this.setAttributeNS(n.space,n.local,e)}return n=ao.ns.qualify(n),null==t?n.local?r:e:"function"==typeof t?n.local?a:o:n.local?u:i}function L(n){return n.trim().replace(/\s+/g," ")}function q(n){return new RegExp("(?:^|\\s+)"+ao.requote(n)+"(?:\\s+|$)","g")}function T(n){return(n+"").trim().split(/^|\s+/)}function R(n,t){function e(){for(var e=-1;++e<i;)n[e](this,t)}function r(){for(var e=-1,r=t.apply(this,arguments);++e<i;)n[e](this,r)}n=T(n).map(D);var i=n.length;return"function"==typeof t?r:e}function D(n){var t=q(n);return function(e,r){if(i=e.classList)return r?i.add(n):i.remove(n);var i=e.getAttribute("class")||"";r?(t.lastIndex=0,t.test(i)||e.setAttribute("class",L(i+" "+n))):e.setAttribute("class",L(i.replace(t," ")))}}function P(n,t,e){function r(){this.style.removeProperty(n)}function i(){this.style.setProperty(n,t,e)}function u(){var r=t.apply(this,arguments);null==r?this.style.removeProperty(n):this.style.setProperty(n,r,e)}return null==t?r:"function"==typeof t?u:i}function U(n,t){function e(){delete this[n]}function r(){this[n]=t}function i(){var e=t.apply(this,arguments);null==e?delete this[n]:this[n]=e}return null==t?e:"function"==typeof t?i:r}function j(n){function t(){var t=this.ownerDocument,e=this.namespaceURI;return e===zo&&t.documentElement.namespaceURI===zo?t.createElement(n):t.createElementNS(e,n)}function e(){return this.ownerDocument.createElementNS(n.space,n.local)}return"function"==typeof n?n:(n=ao.ns.qualify(n)).local?e:t}function F(){var n=this.parentNode;n&&n.removeChild(this)}function H(n){return{__data__:n}}function O(n){return function(){return Ao(this,n)}}function I(n){return arguments.length||(n=e),function(t,e){return t&&e?n(t.__data__,e.__data__):!t-!e}}function Y(n,t){for(var e=0,r=n.length;r>e;e++)for(var i,u=n[e],o=0,a=u.length;a>o;o++)(i=u[o])&&t(i,o,e);return n}function Z(n){return ko(n,qo),n}function V(n){var t,e;return function(r,i,u){var o,a=n[u].update,l=a.length;for(u!=e&&(e=u,t=0),i>=t&&(t=i+1);!(o=a[t])&&++t<l;);return o}}function X(n,t,e){function r(){var t=this[o];t&&(this.removeEventListener(n,t,t.$),delete this[o])}function i(){var i=l(t,co(arguments));r.call(this),this.addEventListener(n,this[o]=i,i.$=e),i._=t}function u(){var t,e=new RegExp("^__on([^.]+)"+ao.requote(n)+"$");for(var r in this)if(t=r.match(e)){var i=this[r];this.removeEventListener(t[1],i,i.$),delete this[r]}}var o="__on"+n,a=n.indexOf("."),l=$;a>0&&(n=n.slice(0,a));var c=To.get(n);return c&&(n=c,l=B),a?t?i:r:t?b:u}function $(n,t){return function(e){var r=ao.event;ao.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{ao.event=r}}}function B(n,t){var e=$(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function W(e){var r=".dragsuppress-"+ ++Do,i="click"+r,u=ao.select(t(e)).on("touchmove"+r,S).on("dragstart"+r,S).on("selectstart"+r,S);if(null==Ro&&(Ro="onselectstart"in e?!1:x(e.style,"userSelect")),Ro){var o=n(e).style,a=o[Ro];o[Ro]="none"}return function(n){if(u.on(r,null),Ro&&(o[Ro]=a),n){var t=function(){u.on(i,null)};u.on(i,function(){S(),t()},!0),setTimeout(t,0)}}}function J(n,e){e.changedTouches&&(e=e.changedTouches[0]);var r=n.ownerSVGElement||n;if(r.createSVGPoint){var i=r.createSVGPoint();if(0>Po){var u=t(n);if(u.scrollX||u.scrollY){r=ao.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var o=r[0][0].getScreenCTM();Po=!(o.f||o.e),r.remove()}}return Po?(i.x=e.pageX,i.y=e.pageY):(i.x=e.clientX,i.y=e.clientY),i=i.matrixTransform(n.getScreenCTM().inverse()),[i.x,i.y]}var a=n.getBoundingClientRect();return[e.clientX-a.left-n.clientLeft,e.clientY-a.top-n.clientTop]}function G(){return ao.event.changedTouches[0].identifier}function K(n){return n>0?1:0>n?-1:0}function Q(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function nn(n){return n>1?0:-1>n?Fo:Math.acos(n)}function tn(n){return n>1?Io:-1>n?-Io:Math.asin(n)}function en(n){return((n=Math.exp(n))-1/n)/2}function rn(n){return((n=Math.exp(n))+1/n)/2}function un(n){return((n=Math.exp(2*n))-1)/(n+1)}function on(n){return(n=Math.sin(n/2))*n}function an(){}function ln(n,t,e){return this instanceof ln?(this.h=+n,this.s=+t,void(this.l=+e)):arguments.length<2?n instanceof ln?new ln(n.h,n.s,n.l):_n(""+n,wn,ln):new ln(n,t,e)}function cn(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?u+(o-u)*n/60:180>n?o:240>n?u+(o-u)*(240-n)/60:u}function i(n){return Math.round(255*r(n))}var u,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,u=2*e-o,new mn(i(n+120),i(n),i(n-120))}function fn(n,t,e){return this instanceof fn?(this.h=+n,this.c=+t,void(this.l=+e)):arguments.length<2?n instanceof fn?new fn(n.h,n.c,n.l):n instanceof hn?gn(n.l,n.a,n.b):gn((n=Sn((n=ao.rgb(n)).r,n.g,n.b)).l,n.a,n.b):new fn(n,t,e)}function sn(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),new hn(e,Math.cos(n*=Yo)*t,Math.sin(n)*t)}function hn(n,t,e){return this instanceof hn?(this.l=+n,this.a=+t,void(this.b=+e)):arguments.length<2?n instanceof hn?new hn(n.l,n.a,n.b):n instanceof fn?sn(n.h,n.c,n.l):Sn((n=mn(n)).r,n.g,n.b):new hn(n,t,e)}function pn(n,t,e){var r=(n+16)/116,i=r+t/500,u=r-e/200;return i=vn(i)*na,r=vn(r)*ta,u=vn(u)*ea,new mn(yn(3.2404542*i-1.5371385*r-.4985314*u),yn(-.969266*i+1.8760108*r+.041556*u),yn(.0556434*i-.2040259*r+1.0572252*u))}function gn(n,t,e){return n>0?new fn(Math.atan2(e,t)*Zo,Math.sqrt(t*t+e*e),n):new fn(NaN,NaN,n)}function vn(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function dn(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function yn(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function mn(n,t,e){return this instanceof mn?(this.r=~~n,this.g=~~t,void(this.b=~~e)):arguments.length<2?n instanceof mn?new mn(n.r,n.g,n.b):_n(""+n,mn,cn):new mn(n,t,e)}function Mn(n){return new mn(n>>16,n>>8&255,255&n)}function xn(n){return Mn(n)+""}function bn(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function _n(n,t,e){var r,i,u,o=0,a=0,l=0;if(r=/([a-z]+)\((.*)\)/.exec(n=n.toLowerCase()))switch(i=r[2].split(","),r[1]){case"hsl":return e(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return t(Nn(i[0]),Nn(i[1]),Nn(i[2]))}return(u=ua.get(n))?t(u.r,u.g,u.b):(null==n||"#"!==n.charAt(0)||isNaN(u=parseInt(n.slice(1),16))||(4===n.length?(o=(3840&u)>>4,o=o>>4|o,a=240&u,a=a>>4|a,l=15&u,l=l<<4|l):7===n.length&&(o=(16711680&u)>>16,a=(65280&u)>>8,l=255&u)),t(o,a,l))}function wn(n,t,e){var r,i,u=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-u,l=(o+u)/2;return a?(i=.5>l?a/(o+u):a/(2-o-u),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=NaN,i=l>0&&1>l?0:r),new ln(r,i,l)}function Sn(n,t,e){n=kn(n),t=kn(t),e=kn(e);var r=dn((.4124564*n+.3575761*t+.1804375*e)/na),i=dn((.2126729*n+.7151522*t+.072175*e)/ta),u=dn((.0193339*n+.119192*t+.9503041*e)/ea);return hn(116*i-16,500*(r-i),200*(i-u))}function kn(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function Nn(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function En(n){return"function"==typeof n?n:function(){return n}}function An(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),Cn(t,e,n,r)}}function Cn(n,t,e,r){function i(){var n,t=l.status;if(!t&&Ln(l)||t>=200&&300>t||304===t){try{n=e.call(u,l)}catch(r){return void o.error.call(u,r)}o.load.call(u,n)}else o.error.call(u,l)}var u={},o=ao.dispatch("beforesend","progress","load","error"),a={},l=new XMLHttpRequest,c=null;return!this.XDomainRequest||"withCredentials"in l||!/^(http(s)?:)?\/\//.test(n)||(l=new XDomainRequest),"onload"in l?l.onload=l.onerror=i:l.onreadystatechange=function(){l.readyState>3&&i()},l.onprogress=function(n){var t=ao.event;ao.event=n;try{o.progress.call(u,l)}finally{ao.event=t}},u.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",u)},u.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",u):t},u.responseType=function(n){return arguments.length?(c=n,u):c},u.response=function(n){return e=n,u},["get","post"].forEach(function(n){u[n]=function(){return u.send.apply(u,[n].concat(co(arguments)))}}),u.send=function(e,r,i){if(2===arguments.length&&"function"==typeof r&&(i=r,r=null),l.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),l.setRequestHeader)for(var f in a)l.setRequestHeader(f,a[f]);return null!=t&&l.overrideMimeType&&l.overrideMimeType(t),null!=c&&(l.responseType=c),null!=i&&u.on("error",i).on("load",function(n){i(null,n)}),o.beforesend.call(u,l),l.send(null==r?null:r),u},u.abort=function(){return l.abort(),u},ao.rebind(u,o,"on"),null==r?u:u.get(zn(r))}function zn(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function Ln(n){var t=n.responseType;return t&&"text"!==t?n.response:n.responseText}function qn(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var i=e+t,u={c:n,t:i,n:null};return aa?aa.n=u:oa=u,aa=u,la||(ca=clearTimeout(ca),la=1,fa(Tn)),u}function Tn(){var n=Rn(),t=Dn()-n;t>24?(isFinite(t)&&(clearTimeout(ca),ca=setTimeout(Tn,t)),la=0):(la=1,fa(Tn))}function Rn(){for(var n=Date.now(),t=oa;t;)n>=t.t&&t.c(n-t.t)&&(t.c=null),t=t.n;return n}function Dn(){for(var n,t=oa,e=1/0;t;)t.c?(t.t<e&&(e=t.t),t=(n=t).n):t=n?n.n=t.n:oa=t.n;return aa=n,e}function Pn(n,t){return t-(n?Math.ceil(Math.log(n)/Math.LN10):1)}function Un(n,t){var e=Math.pow(10,3*xo(8-t));return{scale:t>8?function(n){return n/e}:function(n){return n*e},symbol:n}}function jn(n){var t=n.decimal,e=n.thousands,r=n.grouping,i=n.currency,u=r&&e?function(n,t){for(var i=n.length,u=[],o=0,a=r[0],l=0;i>0&&a>0&&(l+a+1>t&&(a=Math.max(1,t-l)),u.push(n.substring(i-=a,i+a)),!((l+=a+1)>t));)a=r[o=(o+1)%r.length];return u.reverse().join(e)}:m;return function(n){var e=ha.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"-",l=e[4]||"",c=e[5],f=+e[6],s=e[7],h=e[8],p=e[9],g=1,v="",d="",y=!1,m=!0;switch(h&&(h=+h.substring(1)),(c||"0"===r&&"="===o)&&(c=r="0",o="="),p){case"n":s=!0,p="g";break;case"%":g=100,d="%",p="f";break;case"p":g=100,d="%",p="r";break;case"b":case"o":case"x":case"X":"#"===l&&(v="0"+p.toLowerCase());case"c":m=!1;case"d":y=!0,h=0;break;case"s":g=-1,p="r"}"$"===l&&(v=i[0],d=i[1]),"r"!=p||h||(p="g"),null!=h&&("g"==p?h=Math.max(1,Math.min(21,h)):"e"!=p&&"f"!=p||(h=Math.max(0,Math.min(20,h)))),p=pa.get(p)||Fn;var M=c&&s;return function(n){var e=d;if(y&&n%1)return"";var i=0>n||0===n&&0>1/n?(n=-n,"-"):"-"===a?"":a;if(0>g){var l=ao.formatPrefix(n,h);n=l.scale(n),e=l.symbol+d}else n*=g;n=p(n,h);var x,b,_=n.lastIndexOf(".");if(0>_){var w=m?n.lastIndexOf("e"):-1;0>w?(x=n,b=""):(x=n.substring(0,w),b=n.substring(w))}else x=n.substring(0,_),b=t+n.substring(_+1);!c&&s&&(x=u(x,1/0));var S=v.length+x.length+b.length+(M?0:i.length),k=f>S?new Array(S=f-S+1).join(r):"";return M&&(x=u(k+x,k.length?f-b.length:1/0)),i+=v,n=x+b,("<"===o?i+n+k:">"===o?k+i+n:"^"===o?k.substring(0,S>>=1)+i+n+k.substring(S):i+(M?n:k+n))+e}}}function Fn(n){return n+""}function Hn(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function On(n,t,e){function r(t){var e=n(t),r=u(e,1);return r-t>t-e?e:r}function i(e){return t(e=n(new va(e-1)),1),e}function u(n,e){return t(n=new va(+n),e),n}function o(n,r,u){var o=i(n),a=[];if(u>1)for(;r>o;)e(o)%u||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{va=Hn;var r=new Hn;return r._=n,o(r,t,e)}finally{va=Date}}n.floor=n,n.round=r,n.ceil=i,n.offset=u,n.range=o;var l=n.utc=In(n);return l.floor=l,l.round=In(r),l.ceil=In(i),l.offset=In(u),l.range=a,n}function In(n){return function(t,e){try{va=Hn;var r=new Hn;return r._=t,n(r,e)._}finally{va=Date}}}function Yn(n){function t(n){function t(t){for(var e,i,u,o=[],a=-1,l=0;++a<r;)37===n.charCodeAt(a)&&(o.push(n.slice(l,a)),null!=(i=ya[e=n.charAt(++a)])&&(e=n.charAt(++a)),(u=A[e])&&(e=u(t,null==i?"e"===e?" ":"0":i)),o.push(e),l=a+1);return o.push(n.slice(l,a)),o.join("")}var r=n.length;return t.parse=function(t){var r={y:1900,m:0,d:1,H:0,M:0,S:0,L:0,Z:null},i=e(r,n,t,0);if(i!=t.length)return null;"p"in r&&(r.H=r.H%12+12*r.p);var u=null!=r.Z&&va!==Hn,o=new(u?Hn:va);return"j"in r?o.setFullYear(r.y,0,r.j):"W"in r||"U"in r?("w"in r||(r.w="W"in r?1:0),o.setFullYear(r.y,0,1),o.setFullYear(r.y,0,"W"in r?(r.w+6)%7+7*r.W-(o.getDay()+5)%7:r.w+7*r.U-(o.getDay()+6)%7)):o.setFullYear(r.y,r.m,r.d),o.setHours(r.H+(r.Z/100|0),r.M+r.Z%100,r.S,r.L),u?o._:o},t.toString=function(){return n},t}function e(n,t,e,r){for(var i,u,o,a=0,l=t.length,c=e.length;l>a;){if(r>=c)return-1;if(i=t.charCodeAt(a++),37===i){if(o=t.charAt(a++),u=C[o in ya?t.charAt(a++):o],!u||(r=u(n,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){_.lastIndex=0;var r=_.exec(t.slice(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){x.lastIndex=0;var r=x.exec(t.slice(e));return r?(n.w=b.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){N.lastIndex=0;var r=N.exec(t.slice(e));return r?(n.m=E.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.slice(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,A.c.toString(),t,r)}function l(n,t,r){return e(n,A.x.toString(),t,r)}function c(n,t,r){return e(n,A.X.toString(),t,r)}function f(n,t,e){var r=M.get(t.slice(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var s=n.dateTime,h=n.date,p=n.time,g=n.periods,v=n.days,d=n.shortDays,y=n.months,m=n.shortMonths;t.utc=function(n){function e(n){try{va=Hn;var t=new va;return t._=n,r(t)}finally{va=Date}}var r=t(n);return e.parse=function(n){try{va=Hn;var t=r.parse(n);return t&&t._}finally{va=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ct;var M=ao.map(),x=Vn(v),b=Xn(v),_=Vn(d),w=Xn(d),S=Vn(y),k=Xn(y),N=Vn(m),E=Xn(m);g.forEach(function(n,t){M.set(n.toLowerCase(),t)});var A={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return m[n.getMonth()]},B:function(n){return y[n.getMonth()]},c:t(s),d:function(n,t){return Zn(n.getDate(),t,2)},e:function(n,t){return Zn(n.getDate(),t,2)},H:function(n,t){return Zn(n.getHours(),t,2)},I:function(n,t){return Zn(n.getHours()%12||12,t,2)},j:function(n,t){return Zn(1+ga.dayOfYear(n),t,3)},L:function(n,t){return Zn(n.getMilliseconds(),t,3)},m:function(n,t){return Zn(n.getMonth()+1,t,2)},M:function(n,t){return Zn(n.getMinutes(),t,2)},p:function(n){return g[+(n.getHours()>=12)]},S:function(n,t){return Zn(n.getSeconds(),t,2)},U:function(n,t){return Zn(ga.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Zn(ga.mondayOfYear(n),t,2)},x:t(h),X:t(p),y:function(n,t){return Zn(n.getFullYear()%100,t,2)},Y:function(n,t){return Zn(n.getFullYear()%1e4,t,4)},Z:at,"%":function(){return"%"}},C={a:r,A:i,b:u,B:o,c:a,d:tt,e:tt,H:rt,I:rt,j:et,L:ot,m:nt,M:it,p:f,S:ut,U:Bn,w:$n,W:Wn,x:l,X:c,y:Gn,Y:Jn,Z:Kn,"%":lt};return t}function Zn(n,t,e){var r=0>n?"-":"",i=(r?-n:n)+"",u=i.length;return r+(e>u?new Array(e-u+1).join(t)+i:i)}function Vn(n){return new RegExp("^(?:"+n.map(ao.requote).join("|")+")","i")}function Xn(n){for(var t=new c,e=-1,r=n.length;++e<r;)t.set(n[e].toLowerCase(),e);return t}function $n(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+1));return r?(n.w=+r[0],e+r[0].length):-1}function Bn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e));return r?(n.U=+r[0],e+r[0].length):-1}function Wn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e));return r?(n.W=+r[0],e+r[0].length):-1}function Jn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+4));return r?(n.y=+r[0],e+r[0].length):-1}function Gn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.y=Qn(+r[0]),e+r[0].length):-1}function Kn(n,t,e){return/^[+-]\d{4}$/.test(t=t.slice(e,e+5))?(n.Z=-t,e+5):-1}function Qn(n){return n+(n>68?1900:2e3)}function nt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function tt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function et(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function rt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function it(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function ut(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ot(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function at(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=xo(t)/60|0,i=xo(t)%60;return e+Zn(r,"0",2)+Zn(i,"0",2)}function lt(n,t,e){Ma.lastIndex=0;var r=Ma.exec(t.slice(e,e+1));return r?e+r[0].length:-1}function ct(n){for(var t=n.length,e=-1;++e<t;)n[e][0]=this(n[e][0]);return function(t){for(var e=0,r=n[e];!r[1](t);)r=n[++e];return r[0](t)}}function ft(){}function st(n,t,e){var r=e.s=n+t,i=r-n,u=r-i;e.t=n-u+(t-i)}function ht(n,t){n&&wa.hasOwnProperty(n.type)&&wa[n.type](n,t)}function pt(n,t,e){var r,i=-1,u=n.length-e;for(t.lineStart();++i<u;)r=n[i],t.point(r[0],r[1],r[2]);t.lineEnd()}function gt(n,t){var e=-1,r=n.length;for(t.polygonStart();++e<r;)pt(n[e],t,1);t.polygonEnd()}function vt(){function n(n,t){n*=Yo,t=t*Yo/2+Fo/4;var e=n-r,o=e>=0?1:-1,a=o*e,l=Math.cos(t),c=Math.sin(t),f=u*c,s=i*l+f*Math.cos(a),h=f*o*Math.sin(a);ka.add(Math.atan2(h,s)),r=n,i=l,u=c}var t,e,r,i,u;Na.point=function(o,a){Na.point=n,r=(t=o)*Yo,i=Math.cos(a=(e=a)*Yo/2+Fo/4),u=Math.sin(a)},Na.lineEnd=function(){n(t,e)}}function dt(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function yt(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function mt(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function Mt(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function xt(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function bt(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function _t(n){return[Math.atan2(n[1],n[0]),tn(n[2])]}function wt(n,t){return xo(n[0]-t[0])<Uo&&xo(n[1]-t[1])<Uo}function St(n,t){n*=Yo;var e=Math.cos(t*=Yo);kt(e*Math.cos(n),e*Math.sin(n),Math.sin(t))}function kt(n,t,e){++Ea,Ca+=(n-Ca)/Ea,za+=(t-za)/Ea,La+=(e-La)/Ea}function Nt(){function n(n,i){n*=Yo;var u=Math.cos(i*=Yo),o=u*Math.cos(n),a=u*Math.sin(n),l=Math.sin(i),c=Math.atan2(Math.sqrt((c=e*l-r*a)*c+(c=r*o-t*l)*c+(c=t*a-e*o)*c),t*o+e*a+r*l);Aa+=c,qa+=c*(t+(t=o)),Ta+=c*(e+(e=a)),Ra+=c*(r+(r=l)),kt(t,e,r)}var t,e,r;ja.point=function(i,u){i*=Yo;var o=Math.cos(u*=Yo);t=o*Math.cos(i),e=o*Math.sin(i),r=Math.sin(u),ja.point=n,kt(t,e,r)}}function Et(){ja.point=St}function At(){function n(n,t){n*=Yo;var e=Math.cos(t*=Yo),o=e*Math.cos(n),a=e*Math.sin(n),l=Math.sin(t),c=i*l-u*a,f=u*o-r*l,s=r*a-i*o,h=Math.sqrt(c*c+f*f+s*s),p=r*o+i*a+u*l,g=h&&-nn(p)/h,v=Math.atan2(h,p);Da+=g*c,Pa+=g*f,Ua+=g*s,Aa+=v,qa+=v*(r+(r=o)),Ta+=v*(i+(i=a)),Ra+=v*(u+(u=l)),kt(r,i,u)}var t,e,r,i,u;ja.point=function(o,a){t=o,e=a,ja.point=n,o*=Yo;var l=Math.cos(a*=Yo);r=l*Math.cos(o),i=l*Math.sin(o),u=Math.sin(a),kt(r,i,u)},ja.lineEnd=function(){n(t,e),ja.lineEnd=Et,ja.point=St}}function Ct(n,t){function e(e,r){return e=n(e,r),t(e[0],e[1])}return n.invert&&t.invert&&(e.invert=function(e,r){return e=t.invert(e,r),e&&n.invert(e[0],e[1])}),e}function zt(){return!0}function Lt(n,t,e,r,i){var u=[],o=[];if(n.forEach(function(n){if(!((t=n.length-1)<=0)){var t,e=n[0],r=n[t];if(wt(e,r)){i.lineStart();for(var a=0;t>a;++a)i.point((e=n[a])[0],e[1]);return void i.lineEnd()}var l=new Tt(e,n,null,!0),c=new Tt(e,null,l,!1);l.o=c,u.push(l),o.push(c),l=new Tt(r,n,null,!1),c=new Tt(r,null,l,!0),l.o=c,u.push(l),o.push(c)}}),o.sort(t),qt(u),qt(o),u.length){for(var a=0,l=e,c=o.length;c>a;++a)o[a].e=l=!l;for(var f,s,h=u[0];;){for(var p=h,g=!0;p.v;)if((p=p.n)===h)return;f=p.z,i.lineStart();do{if(p.v=p.o.v=!0,p.e){if(g)for(var a=0,c=f.length;c>a;++a)i.point((s=f[a])[0],s[1]);else r(p.x,p.n.x,1,i);p=p.n}else{if(g){f=p.p.z;for(var a=f.length-1;a>=0;--a)i.point((s=f[a])[0],s[1])}else r(p.x,p.p.x,-1,i);p=p.p}p=p.o,f=p.z,g=!g}while(!p.v);i.lineEnd()}}}function qt(n){if(t=n.length){for(var t,e,r=0,i=n[0];++r<t;)i.n=e=n[r],e.p=i,i=e;i.n=e=n[0],e.p=i}}function Tt(n,t,e,r){this.x=n,this.z=t,this.o=e,this.e=r,this.v=!1,this.n=this.p=null}function Rt(n,t,e,r){return function(i,u){function o(t,e){var r=i(t,e);n(t=r[0],e=r[1])&&u.point(t,e)}function a(n,t){var e=i(n,t);d.point(e[0],e[1])}function l(){m.point=a,d.lineStart()}function c(){m.point=o,d.lineEnd()}function f(n,t){v.push([n,t]);var e=i(n,t);x.point(e[0],e[1])}function s(){x.lineStart(),v=[]}function h(){f(v[0][0],v[0][1]),x.lineEnd();var n,t=x.clean(),e=M.buffer(),r=e.length;if(v.pop(),g.push(v),v=null,r)if(1&t){n=e[0];var i,r=n.length-1,o=-1;if(r>0){for(b||(u.polygonStart(),b=!0),u.lineStart();++o<r;)u.point((i=n[o])[0],i[1]);u.lineEnd()}}else r>1&&2&t&&e.push(e.pop().concat(e.shift())),p.push(e.filter(Dt))}var p,g,v,d=t(u),y=i.invert(r[0],r[1]),m={point:o,lineStart:l,lineEnd:c,polygonStart:function(){m.point=f,m.lineStart=s,m.lineEnd=h,p=[],g=[]},polygonEnd:function(){m.point=o,m.lineStart=l,m.lineEnd=c,p=ao.merge(p);var n=Ot(y,g);p.length?(b||(u.polygonStart(),b=!0),Lt(p,Ut,n,e,u)):n&&(b||(u.polygonStart(),b=!0),u.lineStart(),e(null,null,1,u),u.lineEnd()),b&&(u.polygonEnd(),b=!1),p=g=null},sphere:function(){u.polygonStart(),u.lineStart(),e(null,null,1,u),u.lineEnd(),u.polygonEnd()}},M=Pt(),x=t(M),b=!1;return m}}function Dt(n){return n.length>1}function Pt(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:b,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Ut(n,t){return((n=n.x)[0]<0?n[1]-Io-Uo:Io-n[1])-((t=t.x)[0]<0?t[1]-Io-Uo:Io-t[1])}function jt(n){var t,e=NaN,r=NaN,i=NaN;return{lineStart:function(){n.lineStart(),t=1},point:function(u,o){var a=u>0?Fo:-Fo,l=xo(u-e);xo(l-Fo)<Uo?(n.point(e,r=(r+o)/2>0?Io:-Io),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(u,r),t=0):i!==a&&l>=Fo&&(xo(e-i)<Uo&&(e-=i*Uo),xo(u-a)<Uo&&(u-=a*Uo),r=Ft(e,r,u,o),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(a,r),t=0),n.point(e=u,r=o),i=a},lineEnd:function(){n.lineEnd(),e=r=NaN},clean:function(){return 2-t}}}function Ft(n,t,e,r){var i,u,o=Math.sin(n-e);return xo(o)>Uo?Math.atan((Math.sin(t)*(u=Math.cos(r))*Math.sin(e)-Math.sin(r)*(i=Math.cos(t))*Math.sin(n))/(i*u*o)):(t+r)/2}function Ht(n,t,e,r){var i;if(null==n)i=e*Io,r.point(-Fo,i),r.point(0,i),r.point(Fo,i),r.point(Fo,0),r.point(Fo,-i),r.point(0,-i),r.point(-Fo,-i),r.point(-Fo,0),r.point(-Fo,i);else if(xo(n[0]-t[0])>Uo){var u=n[0]<t[0]?Fo:-Fo;i=e*u/2,r.point(-u,i),r.point(0,i),r.point(u,i)}else r.point(t[0],t[1])}function Ot(n,t){var e=n[0],r=n[1],i=[Math.sin(e),-Math.cos(e),0],u=0,o=0;ka.reset();for(var a=0,l=t.length;l>a;++a){var c=t[a],f=c.length;if(f)for(var s=c[0],h=s[0],p=s[1]/2+Fo/4,g=Math.sin(p),v=Math.cos(p),d=1;;){d===f&&(d=0),n=c[d];var y=n[0],m=n[1]/2+Fo/4,M=Math.sin(m),x=Math.cos(m),b=y-h,_=b>=0?1:-1,w=_*b,S=w>Fo,k=g*M;if(ka.add(Math.atan2(k*_*Math.sin(w),v*x+k*Math.cos(w))),u+=S?b+_*Ho:b,S^h>=e^y>=e){var N=mt(dt(s),dt(n));bt(N);var E=mt(i,N);bt(E);var A=(S^b>=0?-1:1)*tn(E[2]);(r>A||r===A&&(N[0]||N[1]))&&(o+=S^b>=0?1:-1)}if(!d++)break;h=y,g=M,v=x,s=n}}return(-Uo>u||Uo>u&&-Uo>ka)^1&o}function It(n){function t(n,t){return Math.cos(n)*Math.cos(t)>u}function e(n){var e,u,l,c,f;return{lineStart:function(){c=l=!1,f=1},point:function(s,h){var p,g=[s,h],v=t(s,h),d=o?v?0:i(s,h):v?i(s+(0>s?Fo:-Fo),h):0;if(!e&&(c=l=v)&&n.lineStart(),v!==l&&(p=r(e,g),(wt(e,p)||wt(g,p))&&(g[0]+=Uo,g[1]+=Uo,v=t(g[0],g[1]))),v!==l)f=0,v?(n.lineStart(),p=r(g,e),n.point(p[0],p[1])):(p=r(e,g),n.point(p[0],p[1]),n.lineEnd()),e=p;else if(a&&e&&o^v){var y;d&u||!(y=r(g,e,!0))||(f=0,o?(n.lineStart(),n.point(y[0][0],y[0][1]),n.point(y[1][0],y[1][1]),n.lineEnd()):(n.point(y[1][0],y[1][1]),n.lineEnd(),n.lineStart(),n.point(y[0][0],y[0][1])))}!v||e&&wt(e,g)||n.point(g[0],g[1]),e=g,l=v,u=d},lineEnd:function(){l&&n.lineEnd(),e=null},clean:function(){return f|(c&&l)<<1}}}function r(n,t,e){var r=dt(n),i=dt(t),o=[1,0,0],a=mt(r,i),l=yt(a,a),c=a[0],f=l-c*c;if(!f)return!e&&n;var s=u*l/f,h=-u*c/f,p=mt(o,a),g=xt(o,s),v=xt(a,h);Mt(g,v);var d=p,y=yt(g,d),m=yt(d,d),M=y*y-m*(yt(g,g)-1);if(!(0>M)){var x=Math.sqrt(M),b=xt(d,(-y-x)/m);if(Mt(b,g),b=_t(b),!e)return b;var _,w=n[0],S=t[0],k=n[1],N=t[1];w>S&&(_=w,w=S,S=_);var E=S-w,A=xo(E-Fo)<Uo,C=A||Uo>E;if(!A&&k>N&&(_=k,k=N,N=_),C?A?k+N>0^b[1]<(xo(b[0]-w)<Uo?k:N):k<=b[1]&&b[1]<=N:E>Fo^(w<=b[0]&&b[0]<=S)){var z=xt(d,(-y+x)/m);return Mt(z,g),[b,_t(z)]}}}function i(t,e){var r=o?n:Fo-n,i=0;return-r>t?i|=1:t>r&&(i|=2),-r>e?i|=4:e>r&&(i|=8),i}var u=Math.cos(n),o=u>0,a=xo(u)>Uo,l=ve(n,6*Yo);return Rt(t,e,l,o?[0,-n]:[-Fo,n-Fo])}function Yt(n,t,e,r){return function(i){var u,o=i.a,a=i.b,l=o.x,c=o.y,f=a.x,s=a.y,h=0,p=1,g=f-l,v=s-c;if(u=n-l,g||!(u>0)){if(u/=g,0>g){if(h>u)return;p>u&&(p=u)}else if(g>0){if(u>p)return;u>h&&(h=u)}if(u=e-l,g||!(0>u)){if(u/=g,0>g){if(u>p)return;u>h&&(h=u)}else if(g>0){if(h>u)return;p>u&&(p=u)}if(u=t-c,v||!(u>0)){if(u/=v,0>v){if(h>u)return;p>u&&(p=u)}else if(v>0){if(u>p)return;u>h&&(h=u)}if(u=r-c,v||!(0>u)){if(u/=v,0>v){if(u>p)return;u>h&&(h=u)}else if(v>0){if(h>u)return;p>u&&(p=u)}return h>0&&(i.a={x:l+h*g,y:c+h*v}),1>p&&(i.b={x:l+p*g,y:c+p*v}),i}}}}}}function Zt(n,t,e,r){function i(r,i){return xo(r[0]-n)<Uo?i>0?0:3:xo(r[0]-e)<Uo?i>0?2:1:xo(r[1]-t)<Uo?i>0?1:0:i>0?3:2}function u(n,t){return o(n.x,t.x)}function o(n,t){var e=i(n,1),r=i(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function l(n){for(var t=0,e=d.length,r=n[1],i=0;e>i;++i)for(var u,o=1,a=d[i],l=a.length,c=a[0];l>o;++o)u=a[o],c[1]<=r?u[1]>r&&Q(c,u,n)>0&&++t:u[1]<=r&&Q(c,u,n)<0&&--t,c=u;return 0!==t}function c(u,a,l,c){var f=0,s=0;if(null==u||(f=i(u,l))!==(s=i(a,l))||o(u,a)<0^l>0){do c.point(0===f||3===f?n:e,f>1?r:t);while((f=(f+l+4)%4)!==s)}else c.point(a[0],a[1])}function f(i,u){return i>=n&&e>=i&&u>=t&&r>=u}function s(n,t){f(n,t)&&a.point(n,t)}function h(){C.point=g,d&&d.push(y=[]),S=!0,w=!1,b=_=NaN}function p(){v&&(g(m,M),x&&w&&E.rejoin(),v.push(E.buffer())),C.point=s,w&&a.lineEnd()}function g(n,t){n=Math.max(-Ha,Math.min(Ha,n)),t=Math.max(-Ha,Math.min(Ha,t));var e=f(n,t);if(d&&y.push([n,t]),S)m=n,M=t,x=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:b,y:_},b:{x:n,y:t}};A(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}b=n,_=t,w=e}var v,d,y,m,M,x,b,_,w,S,k,N=a,E=Pt(),A=Yt(n,t,e,r),C={point:s,lineStart:h,lineEnd:p,polygonStart:function(){a=E,v=[],d=[],k=!0},polygonEnd:function(){a=N,v=ao.merge(v);var t=l([n,r]),e=k&&t,i=v.length;(e||i)&&(a.polygonStart(),e&&(a.lineStart(),c(null,null,1,a),a.lineEnd()),i&&Lt(v,u,t,c,a),a.polygonEnd()),v=d=y=null}};return C}}function Vt(n){var t=0,e=Fo/3,r=ae(n),i=r(t,e);return i.parallels=function(n){return arguments.length?r(t=n[0]*Fo/180,e=n[1]*Fo/180):[t/Fo*180,e/Fo*180]},i}function Xt(n,t){function e(n,t){var e=Math.sqrt(u-2*i*Math.sin(t))/i;return[e*Math.sin(n*=i),o-e*Math.cos(n)]}var r=Math.sin(n),i=(r+Math.sin(t))/2,u=1+r*(2*i-r),o=Math.sqrt(u)/i;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/i,tn((u-(n*n+e*e)*i*i)/(2*i))]},e}function $t(){function n(n,t){Ia+=i*n-r*t,r=n,i=t}var t,e,r,i;$a.point=function(u,o){$a.point=n,t=r=u,e=i=o},$a.lineEnd=function(){n(t,e)}}function Bt(n,t){Ya>n&&(Ya=n),n>Va&&(Va=n),Za>t&&(Za=t),t>Xa&&(Xa=t)}function Wt(){function n(n,t){o.push("M",n,",",t,u)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function i(){o.push("Z")}var u=Jt(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return u=Jt(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Jt(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function Gt(n,t){Ca+=n,za+=t,++La}function Kt(){function n(n,r){var i=n-t,u=r-e,o=Math.sqrt(i*i+u*u);qa+=o*(t+n)/2,Ta+=o*(e+r)/2,Ra+=o,Gt(t=n,e=r)}var t,e;Wa.point=function(r,i){Wa.point=n,Gt(t=r,e=i)}}function Qt(){Wa.point=Gt}function ne(){function n(n,t){var e=n-r,u=t-i,o=Math.sqrt(e*e+u*u);qa+=o*(r+n)/2,Ta+=o*(i+t)/2,Ra+=o,o=i*n-r*t,Da+=o*(r+n),Pa+=o*(i+t),Ua+=3*o,Gt(r=n,i=t)}var t,e,r,i;Wa.point=function(u,o){Wa.point=n,Gt(t=r=u,e=i=o)},Wa.lineEnd=function(){n(t,e)}}function te(n){function t(t,e){n.moveTo(t+o,e),n.arc(t,e,o,0,Ho)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function i(){a.point=t}function u(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:i,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=i,a.point=t},pointRadius:function(n){return o=n,a},result:b};return a}function ee(n){function t(n){return(a?r:e)(n)}function e(t){return ue(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){M=NaN,S.point=u,t.lineStart()}function u(e,r){var u=dt([e,r]),o=n(e,r);i(M,x,m,b,_,w,M=o[0],x=o[1],m=e,b=u[0],_=u[1],w=u[2],a,t),t.point(M,x)}function o(){S.point=e,t.lineEnd()}function l(){
			r(),S.point=c,S.lineEnd=f}function c(n,t){u(s=n,h=t),p=M,g=x,v=b,d=_,y=w,S.point=u}function f(){i(M,x,m,b,_,w,p,g,s,v,d,y,a,t),S.lineEnd=o,o()}var s,h,p,g,v,d,y,m,M,x,b,_,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=l},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function i(t,e,r,a,l,c,f,s,h,p,g,v,d,y){var m=f-t,M=s-e,x=m*m+M*M;if(x>4*u&&d--){var b=a+p,_=l+g,w=c+v,S=Math.sqrt(b*b+_*_+w*w),k=Math.asin(w/=S),N=xo(xo(w)-1)<Uo||xo(r-h)<Uo?(r+h)/2:Math.atan2(_,b),E=n(N,k),A=E[0],C=E[1],z=A-t,L=C-e,q=M*z-m*L;(q*q/x>u||xo((m*z+M*L)/x-.5)>.3||o>a*p+l*g+c*v)&&(i(t,e,r,a,l,c,A,C,N,b/=S,_/=S,w,d,y),y.point(A,C),i(A,C,N,b,_,w,f,s,h,p,g,v,d,y))}}var u=.5,o=Math.cos(30*Yo),a=16;return t.precision=function(n){return arguments.length?(a=(u=n*n)>0&&16,t):Math.sqrt(u)},t}function re(n){var t=ee(function(t,e){return n([t*Zo,e*Zo])});return function(n){return le(t(n))}}function ie(n){this.stream=n}function ue(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function oe(n){return ae(function(){return n})()}function ae(n){function t(n){return n=a(n[0]*Yo,n[1]*Yo),[n[0]*h+l,c-n[1]*h]}function e(n){return n=a.invert((n[0]-l)/h,(c-n[1])/h),n&&[n[0]*Zo,n[1]*Zo]}function r(){a=Ct(o=se(y,M,x),u);var n=u(v,d);return l=p-n[0]*h,c=g+n[1]*h,i()}function i(){return f&&(f.valid=!1,f=null),t}var u,o,a,l,c,f,s=ee(function(n,t){return n=u(n,t),[n[0]*h+l,c-n[1]*h]}),h=150,p=480,g=250,v=0,d=0,y=0,M=0,x=0,b=Fa,_=m,w=null,S=null;return t.stream=function(n){return f&&(f.valid=!1),f=le(b(o,s(_(n)))),f.valid=!0,f},t.clipAngle=function(n){return arguments.length?(b=null==n?(w=n,Fa):It((w=+n)*Yo),i()):w},t.clipExtent=function(n){return arguments.length?(S=n,_=n?Zt(n[0][0],n[0][1],n[1][0],n[1][1]):m,i()):S},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(p=+n[0],g=+n[1],r()):[p,g]},t.center=function(n){return arguments.length?(v=n[0]%360*Yo,d=n[1]%360*Yo,r()):[v*Zo,d*Zo]},t.rotate=function(n){return arguments.length?(y=n[0]%360*Yo,M=n[1]%360*Yo,x=n.length>2?n[2]%360*Yo:0,r()):[y*Zo,M*Zo,x*Zo]},ao.rebind(t,s,"precision"),function(){return u=n.apply(this,arguments),t.invert=u.invert&&e,r()}}function le(n){return ue(n,function(t,e){n.point(t*Yo,e*Yo)})}function ce(n,t){return[n,t]}function fe(n,t){return[n>Fo?n-Ho:-Fo>n?n+Ho:n,t]}function se(n,t,e){return n?t||e?Ct(pe(n),ge(t,e)):pe(n):t||e?ge(t,e):fe}function he(n){return function(t,e){return t+=n,[t>Fo?t-Ho:-Fo>t?t+Ho:t,e]}}function pe(n){var t=he(n);return t.invert=he(-n),t}function ge(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*r+a*i;return[Math.atan2(l*u-f*o,a*r-c*i),tn(f*u+l*o)]}var r=Math.cos(n),i=Math.sin(n),u=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*u-l*o;return[Math.atan2(l*u+c*o,a*r+f*i),tn(f*r-a*i)]},e}function ve(n,t){var e=Math.cos(n),r=Math.sin(n);return function(i,u,o,a){var l=o*t;null!=i?(i=de(e,i),u=de(e,u),(o>0?u>i:i>u)&&(i+=o*Ho)):(i=n+o*Ho,u=n-.5*l);for(var c,f=i;o>0?f>u:u>f;f-=l)a.point((c=_t([e,-r*Math.cos(f),-r*Math.sin(f)]))[0],c[1])}}function de(n,t){var e=dt(t);e[0]-=n,bt(e);var r=nn(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Uo)%(2*Math.PI)}function ye(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function me(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function Me(n){return n.source}function xe(n){return n.target}function be(n,t,e,r){var i=Math.cos(t),u=Math.sin(t),o=Math.cos(r),a=Math.sin(r),l=i*Math.cos(n),c=i*Math.sin(n),f=o*Math.cos(e),s=o*Math.sin(e),h=2*Math.asin(Math.sqrt(on(r-t)+i*o*on(e-n))),p=1/Math.sin(h),g=h?function(n){var t=Math.sin(n*=h)*p,e=Math.sin(h-n)*p,r=e*l+t*f,i=e*c+t*s,o=e*u+t*a;return[Math.atan2(i,r)*Zo,Math.atan2(o,Math.sqrt(r*r+i*i))*Zo]}:function(){return[n*Zo,t*Zo]};return g.distance=h,g}function _e(){function n(n,i){var u=Math.sin(i*=Yo),o=Math.cos(i),a=xo((n*=Yo)-t),l=Math.cos(a);Ja+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*u-e*o*l)*a),e*u+r*o*l),t=n,e=u,r=o}var t,e,r;Ga.point=function(i,u){t=i*Yo,e=Math.sin(u*=Yo),r=Math.cos(u),Ga.point=n},Ga.lineEnd=function(){Ga.point=Ga.lineEnd=b}}function we(n,t){function e(t,e){var r=Math.cos(t),i=Math.cos(e),u=n(r*i);return[u*i*Math.sin(t),u*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),i=t(r),u=Math.sin(i),o=Math.cos(i);return[Math.atan2(n*u,r*o),Math.asin(r&&e*u/r)]},e}function Se(n,t){function e(n,t){o>0?-Io+Uo>t&&(t=-Io+Uo):t>Io-Uo&&(t=Io-Uo);var e=o/Math.pow(i(t),u);return[e*Math.sin(u*n),o-e*Math.cos(u*n)]}var r=Math.cos(n),i=function(n){return Math.tan(Fo/4+n/2)},u=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(i(t)/i(n)),o=r*Math.pow(i(n),u)/u;return u?(e.invert=function(n,t){var e=o-t,r=K(u)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/u,2*Math.atan(Math.pow(o/r,1/u))-Io]},e):Ne}function ke(n,t){function e(n,t){var e=u-t;return[e*Math.sin(i*n),u-e*Math.cos(i*n)]}var r=Math.cos(n),i=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),u=r/i+n;return xo(i)<Uo?ce:(e.invert=function(n,t){var e=u-t;return[Math.atan2(n,e)/i,u-K(i)*Math.sqrt(n*n+e*e)]},e)}function Ne(n,t){return[n,Math.log(Math.tan(Fo/4+t/2))]}function Ee(n){var t,e=oe(n),r=e.scale,i=e.translate,u=e.clipExtent;return e.scale=function(){var n=r.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.translate=function(){var n=i.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.clipExtent=function(n){var o=u.apply(e,arguments);if(o===e){if(t=null==n){var a=Fo*r(),l=i();u([[l[0]-a,l[1]-a],[l[0]+a,l[1]+a]])}}else t&&(o=null);return o},e.clipExtent(null)}function Ae(n,t){return[Math.log(Math.tan(Fo/4+t/2)),-n]}function Ce(n){return n[0]}function ze(n){return n[1]}function Le(n){for(var t=n.length,e=[0,1],r=2,i=2;t>i;i++){for(;r>1&&Q(n[e[r-2]],n[e[r-1]],n[i])<=0;)--r;e[r++]=i}return e.slice(0,r)}function qe(n,t){return n[0]-t[0]||n[1]-t[1]}function Te(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Re(n,t,e,r){var i=n[0],u=e[0],o=t[0]-i,a=r[0]-u,l=n[1],c=e[1],f=t[1]-l,s=r[1]-c,h=(a*(l-c)-s*(i-u))/(s*o-a*f);return[i+h*o,l+h*f]}function De(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Pe(){rr(this),this.edge=this.site=this.circle=null}function Ue(n){var t=cl.pop()||new Pe;return t.site=n,t}function je(n){Be(n),ol.remove(n),cl.push(n),rr(n)}function Fe(n){var t=n.circle,e=t.x,r=t.cy,i={x:e,y:r},u=n.P,o=n.N,a=[n];je(n);for(var l=u;l.circle&&xo(e-l.circle.x)<Uo&&xo(r-l.circle.cy)<Uo;)u=l.P,a.unshift(l),je(l),l=u;a.unshift(l),Be(l);for(var c=o;c.circle&&xo(e-c.circle.x)<Uo&&xo(r-c.circle.cy)<Uo;)o=c.N,a.push(c),je(c),c=o;a.push(c),Be(c);var f,s=a.length;for(f=1;s>f;++f)c=a[f],l=a[f-1],nr(c.edge,l.site,c.site,i);l=a[0],c=a[s-1],c.edge=Ke(l.site,c.site,null,i),$e(l),$e(c)}function He(n){for(var t,e,r,i,u=n.x,o=n.y,a=ol._;a;)if(r=Oe(a,o)-u,r>Uo)a=a.L;else{if(i=u-Ie(a,o),!(i>Uo)){r>-Uo?(t=a.P,e=a):i>-Uo?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var l=Ue(n);if(ol.insert(t,l),t||e){if(t===e)return Be(t),e=Ue(t.site),ol.insert(l,e),l.edge=e.edge=Ke(t.site,l.site),$e(t),void $e(e);if(!e)return void(l.edge=Ke(t.site,l.site));Be(t),Be(e);var c=t.site,f=c.x,s=c.y,h=n.x-f,p=n.y-s,g=e.site,v=g.x-f,d=g.y-s,y=2*(h*d-p*v),m=h*h+p*p,M=v*v+d*d,x={x:(d*m-p*M)/y+f,y:(h*M-v*m)/y+s};nr(e.edge,c,g,x),l.edge=Ke(c,n,null,x),e.edge=Ke(n,g,null,x),$e(t),$e(e)}}function Oe(n,t){var e=n.site,r=e.x,i=e.y,u=i-t;if(!u)return r;var o=n.P;if(!o)return-(1/0);e=o.site;var a=e.x,l=e.y,c=l-t;if(!c)return a;var f=a-r,s=1/u-1/c,h=f/c;return s?(-h+Math.sqrt(h*h-2*s*(f*f/(-2*c)-l+c/2+i-u/2)))/s+r:(r+a)/2}function Ie(n,t){var e=n.N;if(e)return Oe(e,t);var r=n.site;return r.y===t?r.x:1/0}function Ye(n){this.site=n,this.edges=[]}function Ze(n){for(var t,e,r,i,u,o,a,l,c,f,s=n[0][0],h=n[1][0],p=n[0][1],g=n[1][1],v=ul,d=v.length;d--;)if(u=v[d],u&&u.prepare())for(a=u.edges,l=a.length,o=0;l>o;)f=a[o].end(),r=f.x,i=f.y,c=a[++o%l].start(),t=c.x,e=c.y,(xo(r-t)>Uo||xo(i-e)>Uo)&&(a.splice(o,0,new tr(Qe(u.site,f,xo(r-s)<Uo&&g-i>Uo?{x:s,y:xo(t-s)<Uo?e:g}:xo(i-g)<Uo&&h-r>Uo?{x:xo(e-g)<Uo?t:h,y:g}:xo(r-h)<Uo&&i-p>Uo?{x:h,y:xo(t-h)<Uo?e:p}:xo(i-p)<Uo&&r-s>Uo?{x:xo(e-p)<Uo?t:s,y:p}:null),u.site,null)),++l)}function Ve(n,t){return t.angle-n.angle}function Xe(){rr(this),this.x=this.y=this.arc=this.site=this.cy=null}function $e(n){var t=n.P,e=n.N;if(t&&e){var r=t.site,i=n.site,u=e.site;if(r!==u){var o=i.x,a=i.y,l=r.x-o,c=r.y-a,f=u.x-o,s=u.y-a,h=2*(l*s-c*f);if(!(h>=-jo)){var p=l*l+c*c,g=f*f+s*s,v=(s*p-c*g)/h,d=(l*g-f*p)/h,s=d+a,y=fl.pop()||new Xe;y.arc=n,y.site=i,y.x=v+o,y.y=s+Math.sqrt(v*v+d*d),y.cy=s,n.circle=y;for(var m=null,M=ll._;M;)if(y.y<M.y||y.y===M.y&&y.x<=M.x){if(!M.L){m=M.P;break}M=M.L}else{if(!M.R){m=M;break}M=M.R}ll.insert(m,y),m||(al=y)}}}}function Be(n){var t=n.circle;t&&(t.P||(al=t.N),ll.remove(t),fl.push(t),rr(t),n.circle=null)}function We(n){for(var t,e=il,r=Yt(n[0][0],n[0][1],n[1][0],n[1][1]),i=e.length;i--;)t=e[i],(!Je(t,n)||!r(t)||xo(t.a.x-t.b.x)<Uo&&xo(t.a.y-t.b.y)<Uo)&&(t.a=t.b=null,e.splice(i,1))}function Je(n,t){var e=n.b;if(e)return!0;var r,i,u=n.a,o=t[0][0],a=t[1][0],l=t[0][1],c=t[1][1],f=n.l,s=n.r,h=f.x,p=f.y,g=s.x,v=s.y,d=(h+g)/2,y=(p+v)/2;if(v===p){if(o>d||d>=a)return;if(h>g){if(u){if(u.y>=c)return}else u={x:d,y:l};e={x:d,y:c}}else{if(u){if(u.y<l)return}else u={x:d,y:c};e={x:d,y:l}}}else if(r=(h-g)/(v-p),i=y-r*d,-1>r||r>1)if(h>g){if(u){if(u.y>=c)return}else u={x:(l-i)/r,y:l};e={x:(c-i)/r,y:c}}else{if(u){if(u.y<l)return}else u={x:(c-i)/r,y:c};e={x:(l-i)/r,y:l}}else if(v>p){if(u){if(u.x>=a)return}else u={x:o,y:r*o+i};e={x:a,y:r*a+i}}else{if(u){if(u.x<o)return}else u={x:a,y:r*a+i};e={x:o,y:r*o+i}}return n.a=u,n.b=e,!0}function Ge(n,t){this.l=n,this.r=t,this.a=this.b=null}function Ke(n,t,e,r){var i=new Ge(n,t);return il.push(i),e&&nr(i,n,t,e),r&&nr(i,t,n,r),ul[n.i].edges.push(new tr(i,n,t)),ul[t.i].edges.push(new tr(i,t,n)),i}function Qe(n,t,e){var r=new Ge(n,null);return r.a=t,r.b=e,il.push(r),r}function nr(n,t,e,r){n.a||n.b?n.l===e?n.b=r:n.a=r:(n.a=r,n.l=t,n.r=e)}function tr(n,t,e){var r=n.a,i=n.b;this.edge=n,this.site=t,this.angle=e?Math.atan2(e.y-t.y,e.x-t.x):n.l===t?Math.atan2(i.x-r.x,r.y-i.y):Math.atan2(r.x-i.x,i.y-r.y)}function er(){this._=null}function rr(n){n.U=n.C=n.L=n.R=n.P=n.N=null}function ir(n,t){var e=t,r=t.R,i=e.U;i?i.L===e?i.L=r:i.R=r:n._=r,r.U=i,e.U=r,e.R=r.L,e.R&&(e.R.U=e),r.L=e}function ur(n,t){var e=t,r=t.L,i=e.U;i?i.L===e?i.L=r:i.R=r:n._=r,r.U=i,e.U=r,e.L=r.R,e.L&&(e.L.U=e),r.R=e}function or(n){for(;n.L;)n=n.L;return n}function ar(n,t){var e,r,i,u=n.sort(lr).pop();for(il=[],ul=new Array(n.length),ol=new er,ll=new er;;)if(i=al,u&&(!i||u.y<i.y||u.y===i.y&&u.x<i.x))u.x===e&&u.y===r||(ul[u.i]=new Ye(u),He(u),e=u.x,r=u.y),u=n.pop();else{if(!i)break;Fe(i.arc)}t&&(We(t),Ze(t));var o={cells:ul,edges:il};return ol=ll=il=ul=null,o}function lr(n,t){return t.y-n.y||t.x-n.x}function cr(n,t,e){return(n.x-e.x)*(t.y-n.y)-(n.x-t.x)*(e.y-n.y)}function fr(n){return n.x}function sr(n){return n.y}function hr(){return{leaf:!0,nodes:[],point:null,x:null,y:null}}function pr(n,t,e,r,i,u){if(!n(t,e,r,i,u)){var o=.5*(e+i),a=.5*(r+u),l=t.nodes;l[0]&&pr(n,l[0],e,r,o,a),l[1]&&pr(n,l[1],o,r,i,a),l[2]&&pr(n,l[2],e,a,o,u),l[3]&&pr(n,l[3],o,a,i,u)}}function gr(n,t,e,r,i,u,o){var a,l=1/0;return function c(n,f,s,h,p){if(!(f>u||s>o||r>h||i>p)){if(g=n.point){var g,v=t-n.x,d=e-n.y,y=v*v+d*d;if(l>y){var m=Math.sqrt(l=y);r=t-m,i=e-m,u=t+m,o=e+m,a=g}}for(var M=n.nodes,x=.5*(f+h),b=.5*(s+p),_=t>=x,w=e>=b,S=w<<1|_,k=S+4;k>S;++S)if(n=M[3&S])switch(3&S){case 0:c(n,f,s,x,b);break;case 1:c(n,x,s,h,b);break;case 2:c(n,f,b,x,p);break;case 3:c(n,x,b,h,p)}}}(n,r,i,u,o),a}function vr(n,t){n=ao.rgb(n),t=ao.rgb(t);var e=n.r,r=n.g,i=n.b,u=t.r-e,o=t.g-r,a=t.b-i;return function(n){return"#"+bn(Math.round(e+u*n))+bn(Math.round(r+o*n))+bn(Math.round(i+a*n))}}function dr(n,t){var e,r={},i={};for(e in n)e in t?r[e]=Mr(n[e],t[e]):i[e]=n[e];for(e in t)e in n||(i[e]=t[e]);return function(n){for(e in r)i[e]=r[e](n);return i}}function yr(n,t){return n=+n,t=+t,function(e){return n*(1-e)+t*e}}function mr(n,t){var e,r,i,u=hl.lastIndex=pl.lastIndex=0,o=-1,a=[],l=[];for(n+="",t+="";(e=hl.exec(n))&&(r=pl.exec(t));)(i=r.index)>u&&(i=t.slice(u,i),a[o]?a[o]+=i:a[++o]=i),(e=e[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,l.push({i:o,x:yr(e,r)})),u=pl.lastIndex;return u<t.length&&(i=t.slice(u),a[o]?a[o]+=i:a[++o]=i),a.length<2?l[0]?(t=l[0].x,function(n){return t(n)+""}):function(){return t}:(t=l.length,function(n){for(var e,r=0;t>r;++r)a[(e=l[r]).i]=e.x(n);return a.join("")})}function Mr(n,t){for(var e,r=ao.interpolators.length;--r>=0&&!(e=ao.interpolators[r](n,t)););return e}function xr(n,t){var e,r=[],i=[],u=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(Mr(n[e],t[e]));for(;u>e;++e)i[e]=n[e];for(;o>e;++e)i[e]=t[e];return function(n){for(e=0;a>e;++e)i[e]=r[e](n);return i}}function br(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function _r(n){return function(t){return 1-n(1-t)}}function wr(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function Sr(n){return n*n}function kr(n){return n*n*n}function Nr(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function Er(n){return function(t){return Math.pow(t,n)}}function Ar(n){return 1-Math.cos(n*Io)}function Cr(n){return Math.pow(2,10*(n-1))}function zr(n){return 1-Math.sqrt(1-n*n)}function Lr(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/Ho*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*Ho/t)}}function qr(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function Tr(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Rr(n,t){n=ao.hcl(n),t=ao.hcl(t);var e=n.h,r=n.c,i=n.l,u=t.h-e,o=t.c-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return sn(e+u*n,r+o*n,i+a*n)+""}}function Dr(n,t){n=ao.hsl(n),t=ao.hsl(t);var e=n.h,r=n.s,i=n.l,u=t.h-e,o=t.s-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return cn(e+u*n,r+o*n,i+a*n)+""}}function Pr(n,t){n=ao.lab(n),t=ao.lab(t);var e=n.l,r=n.a,i=n.b,u=t.l-e,o=t.a-r,a=t.b-i;return function(n){return pn(e+u*n,r+o*n,i+a*n)+""}}function Ur(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function jr(n){var t=[n.a,n.b],e=[n.c,n.d],r=Hr(t),i=Fr(t,e),u=Hr(Or(e,t,-i))||0;t[0]*e[1]<e[0]*t[1]&&(t[0]*=-1,t[1]*=-1,r*=-1,i*=-1),this.rotate=(r?Math.atan2(t[1],t[0]):Math.atan2(-e[0],e[1]))*Zo,this.translate=[n.e,n.f],this.scale=[r,u],this.skew=u?Math.atan2(i,u)*Zo:0}function Fr(n,t){return n[0]*t[0]+n[1]*t[1]}function Hr(n){var t=Math.sqrt(Fr(n,n));return t&&(n[0]/=t,n[1]/=t),t}function Or(n,t,e){return n[0]+=e*t[0],n[1]+=e*t[1],n}function Ir(n){return n.length?n.pop()+",":""}function Yr(n,t,e,r){if(n[0]!==t[0]||n[1]!==t[1]){var i=e.push("translate(",null,",",null,")");r.push({i:i-4,x:yr(n[0],t[0])},{i:i-2,x:yr(n[1],t[1])})}else(t[0]||t[1])&&e.push("translate("+t+")")}function Zr(n,t,e,r){n!==t?(n-t>180?t+=360:t-n>180&&(n+=360),r.push({i:e.push(Ir(e)+"rotate(",null,")")-2,x:yr(n,t)})):t&&e.push(Ir(e)+"rotate("+t+")")}function Vr(n,t,e,r){n!==t?r.push({i:e.push(Ir(e)+"skewX(",null,")")-2,x:yr(n,t)}):t&&e.push(Ir(e)+"skewX("+t+")")}function Xr(n,t,e,r){if(n[0]!==t[0]||n[1]!==t[1]){var i=e.push(Ir(e)+"scale(",null,",",null,")");r.push({i:i-4,x:yr(n[0],t[0])},{i:i-2,x:yr(n[1],t[1])})}else 1===t[0]&&1===t[1]||e.push(Ir(e)+"scale("+t+")")}function $r(n,t){var e=[],r=[];return n=ao.transform(n),t=ao.transform(t),Yr(n.translate,t.translate,e,r),Zr(n.rotate,t.rotate,e,r),Vr(n.skew,t.skew,e,r),Xr(n.scale,t.scale,e,r),n=t=null,function(n){for(var t,i=-1,u=r.length;++i<u;)e[(t=r[i]).i]=t.x(n);return e.join("")}}function Br(n,t){return t=(t-=n=+n)||1/t,function(e){return(e-n)/t}}function Wr(n,t){return t=(t-=n=+n)||1/t,function(e){return Math.max(0,Math.min(1,(e-n)/t))}}function Jr(n){for(var t=n.source,e=n.target,r=Kr(t,e),i=[t];t!==r;)t=t.parent,i.push(t);for(var u=i.length;e!==r;)i.splice(u,0,e),e=e.parent;return i}function Gr(n){for(var t=[],e=n.parent;null!=e;)t.push(n),n=e,e=e.parent;return t.push(n),t}function Kr(n,t){if(n===t)return n;for(var e=Gr(n),r=Gr(t),i=e.pop(),u=r.pop(),o=null;i===u;)o=i,i=e.pop(),u=r.pop();return o}function Qr(n){n.fixed|=2}function ni(n){n.fixed&=-7}function ti(n){n.fixed|=4,n.px=n.x,n.py=n.y}function ei(n){n.fixed&=-5}function ri(n,t,e){var r=0,i=0;if(n.charge=0,!n.leaf)for(var u,o=n.nodes,a=o.length,l=-1;++l<a;)u=o[l],null!=u&&(ri(u,t,e),n.charge+=u.charge,r+=u.charge*u.cx,i+=u.charge*u.cy);if(n.point){n.leaf||(n.point.x+=Math.random()-.5,n.point.y+=Math.random()-.5);var c=t*e[n.point.index];n.charge+=n.pointCharge=c,r+=c*n.point.x,i+=c*n.point.y}n.cx=r/n.charge,n.cy=i/n.charge}function ii(n,t){return ao.rebind(n,t,"sort","children","value"),n.nodes=n,n.links=fi,n}function ui(n,t){for(var e=[n];null!=(n=e.pop());)if(t(n),(i=n.children)&&(r=i.length))for(var r,i;--r>=0;)e.push(i[r])}function oi(n,t){for(var e=[n],r=[];null!=(n=e.pop());)if(r.push(n),(u=n.children)&&(i=u.length))for(var i,u,o=-1;++o<i;)e.push(u[o]);for(;null!=(n=r.pop());)t(n)}function ai(n){return n.children}function li(n){return n.value}function ci(n,t){return t.value-n.value}function fi(n){return ao.merge(n.map(function(n){return(n.children||[]).map(function(t){return{source:n,target:t}})}))}function si(n){return n.x}function hi(n){return n.y}function pi(n,t,e){n.y0=t,n.y=e}function gi(n){return ao.range(n.length)}function vi(n){for(var t=-1,e=n[0].length,r=[];++t<e;)r[t]=0;return r}function di(n){for(var t,e=1,r=0,i=n[0][1],u=n.length;u>e;++e)(t=n[e][1])>i&&(r=e,i=t);return r}function yi(n){return n.reduce(mi,0)}function mi(n,t){return n+t[1]}function Mi(n,t){return xi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function xi(n,t){for(var e=-1,r=+n[0],i=(n[1]-r)/t,u=[];++e<=t;)u[e]=i*e+r;return u}function bi(n){return[ao.min(n),ao.max(n)]}function _i(n,t){return n.value-t.value}function wi(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function Si(n,t){n._pack_next=t,t._pack_prev=n}function ki(n,t){var e=t.x-n.x,r=t.y-n.y,i=n.r+t.r;return.999*i*i>e*e+r*r}function Ni(n){function t(n){f=Math.min(n.x-n.r,f),s=Math.max(n.x+n.r,s),h=Math.min(n.y-n.r,h),p=Math.max(n.y+n.r,p)}if((e=n.children)&&(c=e.length)){var e,r,i,u,o,a,l,c,f=1/0,s=-(1/0),h=1/0,p=-(1/0);if(e.forEach(Ei),r=e[0],r.x=-r.r,r.y=0,t(r),c>1&&(i=e[1],i.x=i.r,i.y=0,t(i),c>2))for(u=e[2],zi(r,i,u),t(u),wi(r,u),r._pack_prev=u,wi(u,i),i=r._pack_next,o=3;c>o;o++){zi(r,i,u=e[o]);var g=0,v=1,d=1;for(a=i._pack_next;a!==i;a=a._pack_next,v++)if(ki(a,u)){g=1;break}if(1==g)for(l=r._pack_prev;l!==a._pack_prev&&!ki(l,u);l=l._pack_prev,d++);g?(d>v||v==d&&i.r<r.r?Si(r,i=a):Si(r=l,i),o--):(wi(r,u),i=u,t(u))}var y=(f+s)/2,m=(h+p)/2,M=0;for(o=0;c>o;o++)u=e[o],u.x-=y,u.y-=m,M=Math.max(M,u.r+Math.sqrt(u.x*u.x+u.y*u.y));n.r=M,e.forEach(Ai)}}function Ei(n){n._pack_next=n._pack_prev=n}function Ai(n){delete n._pack_next,delete n._pack_prev}function Ci(n,t,e,r){var i=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,i)for(var u=-1,o=i.length;++u<o;)Ci(i[u],t,e,r)}function zi(n,t,e){var r=n.r+e.r,i=t.x-n.x,u=t.y-n.y;if(r&&(i||u)){var o=t.r+e.r,a=i*i+u*u;o*=o,r*=r;var l=.5+(r-o)/(2*a),c=Math.sqrt(Math.max(0,2*o*(r+a)-(r-=a)*r-o*o))/(2*a);e.x=n.x+l*i+c*u,e.y=n.y+l*u-c*i}else e.x=n.x+r,e.y=n.y}function Li(n,t){return n.parent==t.parent?1:2}function qi(n){var t=n.children;return t.length?t[0]:n.t}function Ti(n){var t,e=n.children;return(t=e.length)?e[t-1]:n.t}function Ri(n,t,e){var r=e/(t.i-n.i);t.c-=r,t.s+=e,n.c+=r,t.z+=e,t.m+=e}function Di(n){for(var t,e=0,r=0,i=n.children,u=i.length;--u>=0;)t=i[u],t.z+=e,t.m+=e,e+=t.s+(r+=t.c)}function Pi(n,t,e){return n.a.parent===t.parent?n.a:e}function Ui(n){return 1+ao.max(n,function(n){return n.y})}function ji(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Fi(n){var t=n.children;return t&&t.length?Fi(t[0]):n}function Hi(n){var t,e=n.children;return e&&(t=e.length)?Hi(e[t-1]):n}function Oi(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Ii(n,t){var e=n.x+t[3],r=n.y+t[0],i=n.dx-t[1]-t[3],u=n.dy-t[0]-t[2];return 0>i&&(e+=i/2,i=0),0>u&&(r+=u/2,u=0),{x:e,y:r,dx:i,dy:u}}function Yi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Zi(n){return n.rangeExtent?n.rangeExtent():Yi(n.range())}function Vi(n,t,e,r){var i=e(n[0],n[1]),u=r(t[0],t[1]);return function(n){return u(i(n))}}function Xi(n,t){var e,r=0,i=n.length-1,u=n[r],o=n[i];return u>o&&(e=r,r=i,i=e,e=u,u=o,o=e),n[r]=t.floor(u),n[i]=t.ceil(o),n}function $i(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:Sl}function Bi(n,t,e,r){var i=[],u=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]<n[0]&&(n=n.slice().reverse(),t=t.slice().reverse());++o<=a;)i.push(e(n[o-1],n[o])),u.push(r(t[o-1],t[o]));return function(t){var e=ao.bisect(n,t,1,a)-1;return u[e](i[e](t))}}function Wi(n,t,e,r){function i(){var i=Math.min(n.length,t.length)>2?Bi:Vi,l=r?Wr:Br;return o=i(n,t,l,e),a=i(t,n,l,Mr),u}function u(n){return o(n)}var o,a;return u.invert=function(n){return a(n)},u.domain=function(t){return arguments.length?(n=t.map(Number),i()):n},u.range=function(n){return arguments.length?(t=n,i()):t},u.rangeRound=function(n){return u.range(n).interpolate(Ur)},u.clamp=function(n){return arguments.length?(r=n,i()):r},u.interpolate=function(n){return arguments.length?(e=n,i()):e},u.ticks=function(t){return Qi(n,t)},u.tickFormat=function(t,e){return nu(n,t,e)},u.nice=function(t){return Gi(n,t),i()},u.copy=function(){return Wi(n,t,e,r)},i()}function Ji(n,t){return ao.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Gi(n,t){return Xi(n,$i(Ki(n,t)[2])),Xi(n,$i(Ki(n,t)[2])),n}function Ki(n,t){null==t&&(t=10);var e=Yi(n),r=e[1]-e[0],i=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),u=t/r*i;return.15>=u?i*=10:.35>=u?i*=5:.75>=u&&(i*=2),e[0]=Math.ceil(e[0]/i)*i,e[1]=Math.floor(e[1]/i)*i+.5*i,e[2]=i,e}function Qi(n,t){return ao.range.apply(ao,Ki(n,t))}function nu(n,t,e){var r=Ki(n,t);if(e){var i=ha.exec(e);if(i.shift(),"s"===i[8]){var u=ao.formatPrefix(Math.max(xo(r[0]),xo(r[1])));return i[7]||(i[7]="."+tu(u.scale(r[2]))),i[8]="f",e=ao.format(i.join("")),function(n){return e(u.scale(n))+u.symbol}}i[7]||(i[7]="."+eu(i[8],r)),e=i.join("")}else e=",."+tu(r[2])+"f";return ao.format(e)}function tu(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function eu(n,t){var e=tu(t[2]);return n in kl?Math.abs(e-tu(Math.max(xo(t[0]),xo(t[1]))))+ +("e"!==n):e-2*("%"===n)}function ru(n,t,e,r){function i(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function u(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(i(t))}return o.invert=function(t){return u(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(i)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(i)),o):t},o.nice=function(){var t=Xi(r.map(i),e?Math:El);return n.domain(t),r=t.map(u),o},o.ticks=function(){var n=Yi(r),o=[],a=n[0],l=n[1],c=Math.floor(i(a)),f=Math.ceil(i(l)),s=t%1?2:t;if(isFinite(f-c)){if(e){for(;f>c;c++)for(var h=1;s>h;h++)o.push(u(c)*h);o.push(u(c))}else for(o.push(u(c));c++<f;)for(var h=s-1;h>0;h--)o.push(u(c)*h);for(c=0;o[c]<a;c++);for(f=o.length;o[f-1]>l;f--);o=o.slice(c,f)}return o},o.tickFormat=function(n,e){if(!arguments.length)return Nl;arguments.length<2?e=Nl:"function"!=typeof e&&(e=ao.format(e));var r=Math.max(1,t*n/o.ticks().length);return function(n){var o=n/u(Math.round(i(n)));return t-.5>o*t&&(o*=t),r>=o?e(n):""}},o.copy=function(){return ru(n.copy(),t,e,r)},Ji(o,n)}function iu(n,t,e){function r(t){return n(i(t))}var i=uu(t),u=uu(1/t);return r.invert=function(t){return u(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(i)),r):e},r.ticks=function(n){return Qi(e,n)},r.tickFormat=function(n,t){return nu(e,n,t)},r.nice=function(n){return r.domain(Gi(e,n))},r.exponent=function(o){return arguments.length?(i=uu(t=o),u=uu(1/t),n.domain(e.map(i)),r):t},r.copy=function(){return iu(n.copy(),t,e)},Ji(r,n)}function uu(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function ou(n,t){function e(e){return u[((i.get(e)||("range"===t.t?i.set(e,n.push(e)):NaN))-1)%u.length]}function r(t,e){return ao.range(n.length).map(function(n){return t+e*n})}var i,u,o;return e.domain=function(r){if(!arguments.length)return n;n=[],i=new c;for(var u,o=-1,a=r.length;++o<a;)i.has(u=r[o])||i.set(u,n.push(u));return e[t.t].apply(e,t.a)},e.range=function(n){return arguments.length?(u=n,o=0,t={t:"range",a:arguments},e):u},e.rangePoints=function(i,a){arguments.length<2&&(a=0);var l=i[0],c=i[1],f=n.length<2?(l=(l+c)/2,0):(c-l)/(n.length-1+a);return u=r(l+f*a/2,f),o=0,t={t:"rangePoints",a:arguments},e},e.rangeRoundPoints=function(i,a){arguments.length<2&&(a=0);var l=i[0],c=i[1],f=n.length<2?(l=c=Math.round((l+c)/2),0):(c-l)/(n.length-1+a)|0;return u=r(l+Math.round(f*a/2+(c-l-(n.length-1+a)*f)/2),f),o=0,t={t:"rangeRoundPoints",a:arguments},e},e.rangeBands=function(i,a,l){arguments.length<2&&(a=0),arguments.length<3&&(l=a);var c=i[1]<i[0],f=i[c-0],s=i[1-c],h=(s-f)/(n.length-a+2*l);return u=r(f+h*l,h),c&&u.reverse(),o=h*(1-a),t={t:"rangeBands",a:arguments},e},e.rangeRoundBands=function(i,a,l){arguments.length<2&&(a=0),arguments.length<3&&(l=a);var c=i[1]<i[0],f=i[c-0],s=i[1-c],h=Math.floor((s-f)/(n.length-a+2*l));return u=r(f+Math.round((s-f-(n.length-a)*h)/2),h),c&&u.reverse(),o=Math.round(h*(1-a)),t={t:"rangeRoundBands",a:arguments},e},e.rangeBand=function(){return o},e.rangeExtent=function(){return Yi(t.a[0])},e.copy=function(){return ou(n,t)},e.domain(n)}function au(n,t){function u(){var e=0,r=t.length;for(a=[];++e<r;)a[e-1]=ao.quantile(n,e/r);return o}function o(n){return isNaN(n=+n)?void 0:t[ao.bisect(a,n)]}var a;return o.domain=function(t){return arguments.length?(n=t.map(r).filter(i).sort(e),u()):n},o.range=function(n){return arguments.length?(t=n,u()):t},o.quantiles=function(){return a},o.invertExtent=function(e){return e=t.indexOf(e),0>e?[NaN,NaN]:[e>0?a[e-1]:n[0],e<a.length?a[e]:n[n.length-1]]},o.copy=function(){return au(n,t)},u()}function lu(n,t,e){function r(t){return e[Math.max(0,Math.min(o,Math.floor(u*(t-n))))]}function i(){return u=e.length/(t-n),o=e.length-1,r}var u,o;return r.domain=function(e){return arguments.length?(n=+e[0],t=+e[e.length-1],i()):[n,t]},r.range=function(n){return arguments.length?(e=n,i()):e},r.invertExtent=function(t){return t=e.indexOf(t),t=0>t?NaN:t/u+n,[t,t+1/u]},r.copy=function(){return lu(n,t,e)},i()}function cu(n,t){function e(e){return e>=e?t[ao.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return cu(n,t)},e}function fu(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Qi(n,t)},t.tickFormat=function(t,e){return nu(n,t,e)},t.copy=function(){return fu(n)},t}function su(){return 0}function hu(n){return n.innerRadius}function pu(n){return n.outerRadius}function gu(n){return n.startAngle}function vu(n){return n.endAngle}function du(n){return n&&n.padAngle}function yu(n,t,e,r){return(n-e)*t-(t-r)*n>0?0:1}function mu(n,t,e,r,i){var u=n[0]-t[0],o=n[1]-t[1],a=(i?r:-r)/Math.sqrt(u*u+o*o),l=a*o,c=-a*u,f=n[0]+l,s=n[1]+c,h=t[0]+l,p=t[1]+c,g=(f+h)/2,v=(s+p)/2,d=h-f,y=p-s,m=d*d+y*y,M=e-r,x=f*p-h*s,b=(0>y?-1:1)*Math.sqrt(Math.max(0,M*M*m-x*x)),_=(x*y-d*b)/m,w=(-x*d-y*b)/m,S=(x*y+d*b)/m,k=(-x*d+y*b)/m,N=_-g,E=w-v,A=S-g,C=k-v;return N*N+E*E>A*A+C*C&&(_=S,w=k),[[_-l,w-c],[_*e/M,w*e/M]]}function Mu(n){function t(t){function o(){c.push("M",u(n(f),a))}for(var l,c=[],f=[],s=-1,h=t.length,p=En(e),g=En(r);++s<h;)i.call(this,l=t[s],s)?f.push([+p.call(this,l,s),+g.call(this,l,s)]):f.length&&(o(),f=[]);return f.length&&o(),c.length?c.join(""):null}var e=Ce,r=ze,i=zt,u=xu,o=u.key,a=.7;return t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t.defined=function(n){return arguments.length?(i=n,t):i},t.interpolate=function(n){return arguments.length?(o="function"==typeof n?u=n:(u=Tl.get(n)||xu).key,t):o},t.tension=function(n){return arguments.length?(a=n,t):a},t}function xu(n){return n.length>1?n.join("L"):n+"Z"}function bu(n){return n.join("L")+"Z"}function _u(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("H",(r[0]+(r=n[t])[0])/2,"V",r[1]);return e>1&&i.push("H",r[0]),i.join("")}function wu(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("V",(r=n[t])[1],"H",r[0]);return i.join("")}function Su(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("H",(r=n[t])[0],"V",r[1]);return i.join("")}function ku(n,t){return n.length<4?xu(n):n[1]+Au(n.slice(1,-1),Cu(n,t))}function Nu(n,t){return n.length<3?bu(n):n[0]+Au((n.push(n[0]),n),Cu([n[n.length-2]].concat(n,[n[1]]),t))}function Eu(n,t){return n.length<3?xu(n):n[0]+Au(n,Cu(n,t))}function Au(n,t){if(t.length<1||n.length!=t.length&&n.length!=t.length+2)return xu(n);var e=n.length!=t.length,r="",i=n[0],u=n[1],o=t[0],a=o,l=1;if(e&&(r+="Q"+(u[0]-2*o[0]/3)+","+(u[1]-2*o[1]/3)+","+u[0]+","+u[1],i=n[1],l=2),t.length>1){a=t[1],u=n[l],l++,r+="C"+(i[0]+o[0])+","+(i[1]+o[1])+","+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1];for(var c=2;c<t.length;c++,l++)u=n[l],a=t[c],r+="S"+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1]}if(e){var f=n[l];r+="Q"+(u[0]+2*a[0]/3)+","+(u[1]+2*a[1]/3)+","+f[0]+","+f[1]}return r}function Cu(n,t){for(var e,r=[],i=(1-t)/2,u=n[0],o=n[1],a=1,l=n.length;++a<l;)e=u,u=o,o=n[a],r.push([i*(o[0]-e[0]),i*(o[1]-e[1])]);return r}function zu(n){if(n.length<3)return xu(n);var t=1,e=n.length,r=n[0],i=r[0],u=r[1],o=[i,i,i,(r=n[1])[0]],a=[u,u,u,r[1]],l=[i,",",u,"L",Ru(Pl,o),",",Ru(Pl,a)];for(n.push(n[e-1]);++t<=e;)r=n[t],o.shift(),o.push(r[0]),a.shift(),a.push(r[1]),Du(l,o,a);return n.pop(),l.push("L",r),l.join("")}function Lu(n){if(n.length<4)return xu(n);for(var t,e=[],r=-1,i=n.length,u=[0],o=[0];++r<3;)t=n[r],u.push(t[0]),o.push(t[1]);for(e.push(Ru(Pl,u)+","+Ru(Pl,o)),--r;++r<i;)t=n[r],u.shift(),u.push(t[0]),o.shift(),o.push(t[1]),Du(e,u,o);return e.join("")}function qu(n){for(var t,e,r=-1,i=n.length,u=i+4,o=[],a=[];++r<4;)e=n[r%i],o.push(e[0]),a.push(e[1]);for(t=[Ru(Pl,o),",",Ru(Pl,a)],--r;++r<u;)e=n[r%i],o.shift(),o.push(e[0]),a.shift(),a.push(e[1]),Du(t,o,a);return t.join("")}function Tu(n,t){var e=n.length-1;if(e)for(var r,i,u=n[0][0],o=n[0][1],a=n[e][0]-u,l=n[e][1]-o,c=-1;++c<=e;)r=n[c],i=c/e,r[0]=t*r[0]+(1-t)*(u+i*a),r[1]=t*r[1]+(1-t)*(o+i*l);return zu(n)}function Ru(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]+n[3]*t[3]}function Du(n,t,e){n.push("C",Ru(Rl,t),",",Ru(Rl,e),",",Ru(Dl,t),",",Ru(Dl,e),",",Ru(Pl,t),",",Ru(Pl,e))}function Pu(n,t){return(t[1]-n[1])/(t[0]-n[0])}function Uu(n){for(var t=0,e=n.length-1,r=[],i=n[0],u=n[1],o=r[0]=Pu(i,u);++t<e;)r[t]=(o+(o=Pu(i=u,u=n[t+1])))/2;return r[t]=o,r}function ju(n){for(var t,e,r,i,u=[],o=Uu(n),a=-1,l=n.length-1;++a<l;)t=Pu(n[a],n[a+1]),xo(t)<Uo?o[a]=o[a+1]=0:(e=o[a]/t,r=o[a+1]/t,i=e*e+r*r,i>9&&(i=3*t/Math.sqrt(i),o[a]=i*e,o[a+1]=i*r));for(a=-1;++a<=l;)i=(n[Math.min(l,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),u.push([i||0,o[a]*i||0]);return u}function Fu(n){return n.length<3?xu(n):n[0]+Au(n,ju(n))}function Hu(n){for(var t,e,r,i=-1,u=n.length;++i<u;)t=n[i],e=t[0],r=t[1]-Io,t[0]=e*Math.cos(r),t[1]=e*Math.sin(r);return n}function Ou(n){function t(t){function l(){v.push("M",a(n(y),s),f,c(n(d.reverse()),s),"Z")}for(var h,p,g,v=[],d=[],y=[],m=-1,M=t.length,x=En(e),b=En(i),_=e===r?function(){
			return p}:En(r),w=i===u?function(){return g}:En(u);++m<M;)o.call(this,h=t[m],m)?(d.push([p=+x.call(this,h,m),g=+b.call(this,h,m)]),y.push([+_.call(this,h,m),+w.call(this,h,m)])):d.length&&(l(),d=[],y=[]);return d.length&&l(),v.length?v.join(""):null}var e=Ce,r=Ce,i=0,u=ze,o=zt,a=xu,l=a.key,c=a,f="L",s=.7;return t.x=function(n){return arguments.length?(e=r=n,t):r},t.x0=function(n){return arguments.length?(e=n,t):e},t.x1=function(n){return arguments.length?(r=n,t):r},t.y=function(n){return arguments.length?(i=u=n,t):u},t.y0=function(n){return arguments.length?(i=n,t):i},t.y1=function(n){return arguments.length?(u=n,t):u},t.defined=function(n){return arguments.length?(o=n,t):o},t.interpolate=function(n){return arguments.length?(l="function"==typeof n?a=n:(a=Tl.get(n)||xu).key,c=a.reverse||a,f=a.closed?"M":"L",t):l},t.tension=function(n){return arguments.length?(s=n,t):s},t}function Iu(n){return n.radius}function Yu(n){return[n.x,n.y]}function Zu(n){return function(){var t=n.apply(this,arguments),e=t[0],r=t[1]-Io;return[e*Math.cos(r),e*Math.sin(r)]}}function Vu(){return 64}function Xu(){return"circle"}function $u(n){var t=Math.sqrt(n/Fo);return"M0,"+t+"A"+t+","+t+" 0 1,1 0,"+-t+"A"+t+","+t+" 0 1,1 0,"+t+"Z"}function Bu(n){return function(){var t,e,r;(t=this[n])&&(r=t[e=t.active])&&(r.timer.c=null,r.timer.t=NaN,--t.count?delete t[e]:delete this[n],t.active+=.5,r.event&&r.event.interrupt.call(this,this.__data__,r.index))}}function Wu(n,t,e){return ko(n,Yl),n.namespace=t,n.id=e,n}function Ju(n,t,e,r){var i=n.id,u=n.namespace;return Y(n,"function"==typeof e?function(n,o,a){n[u][i].tween.set(t,r(e.call(n,n.__data__,o,a)))}:(e=r(e),function(n){n[u][i].tween.set(t,e)}))}function Gu(n){return null==n&&(n=""),function(){this.textContent=n}}function Ku(n){return null==n?"__transition__":"__transition_"+n+"__"}function Qu(n,t,e,r,i){function u(n){var t=v.delay;return f.t=t+l,n>=t?o(n-t):void(f.c=o)}function o(e){var i=g.active,u=g[i];u&&(u.timer.c=null,u.timer.t=NaN,--g.count,delete g[i],u.event&&u.event.interrupt.call(n,n.__data__,u.index));for(var o in g)if(r>+o){var c=g[o];c.timer.c=null,c.timer.t=NaN,--g.count,delete g[o]}f.c=a,qn(function(){return f.c&&a(e||1)&&(f.c=null,f.t=NaN),1},0,l),g.active=r,v.event&&v.event.start.call(n,n.__data__,t),p=[],v.tween.forEach(function(e,r){(r=r.call(n,n.__data__,t))&&p.push(r)}),h=v.ease,s=v.duration}function a(i){for(var u=i/s,o=h(u),a=p.length;a>0;)p[--a].call(n,o);return u>=1?(v.event&&v.event.end.call(n,n.__data__,t),--g.count?delete g[r]:delete n[e],1):void 0}var l,f,s,h,p,g=n[e]||(n[e]={active:0,count:0}),v=g[r];v||(l=i.time,f=qn(u,0,l),v=g[r]={tween:new c,time:l,timer:f,delay:i.delay,duration:i.duration,ease:i.ease,index:t},i=null,++g.count)}function no(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate("+(isFinite(r)?r:e(n))+",0)"})}function to(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate(0,"+(isFinite(r)?r:e(n))+")"})}function eo(n){return n.toISOString()}function ro(n,t,e){function r(t){return n(t)}function i(n,e){var r=n[1]-n[0],i=r/e,u=ao.bisect(Kl,i);return u==Kl.length?[t.year,Ki(n.map(function(n){return n/31536e6}),e)[2]]:u?t[i/Kl[u-1]<Kl[u]/i?u-1:u]:[tc,Ki(n,e)[2]]}return r.invert=function(t){return io(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain(t),r):n.domain().map(io)},r.nice=function(n,t){function e(e){return!isNaN(e)&&!n.range(e,io(+e+1),t).length}var u=r.domain(),o=Yi(u),a=null==n?i(o,10):"number"==typeof n&&i(o,n);return a&&(n=a[0],t=a[1]),r.domain(Xi(u,t>1?{floor:function(t){for(;e(t=n.floor(t));)t=io(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=io(+t+1);return t}}:n))},r.ticks=function(n,t){var e=Yi(r.domain()),u=null==n?i(e,10):"number"==typeof n?i(e,n):!n.range&&[{range:n},t];return u&&(n=u[0],t=u[1]),n.range(e[0],io(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return ro(n.copy(),t,e)},Ji(r,n)}function io(n){return new Date(n)}function uo(n){return JSON.parse(n.responseText)}function oo(n){var t=fo.createRange();return t.selectNode(fo.body),t.createContextualFragment(n.responseText)}var ao={version:"3.5.17"},lo=[].slice,co=function(n){return lo.call(n)},fo=this.document;if(fo)try{co(fo.documentElement.childNodes)[0].nodeType}catch(so){co=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}if(Date.now||(Date.now=function(){return+new Date}),fo)try{fo.createElement("DIV").style.setProperty("opacity",0,"")}catch(ho){var po=this.Element.prototype,go=po.setAttribute,vo=po.setAttributeNS,yo=this.CSSStyleDeclaration.prototype,mo=yo.setProperty;po.setAttribute=function(n,t){go.call(this,n,t+"")},po.setAttributeNS=function(n,t,e){vo.call(this,n,t,e+"")},yo.setProperty=function(n,t,e){mo.call(this,n,t+"",e)}}ao.ascending=e,ao.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:NaN},ao.min=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i<u;)if(null!=(r=n[i])&&r>=r){e=r;break}for(;++i<u;)null!=(r=n[i])&&e>r&&(e=r)}else{for(;++i<u;)if(null!=(r=t.call(n,n[i],i))&&r>=r){e=r;break}for(;++i<u;)null!=(r=t.call(n,n[i],i))&&e>r&&(e=r)}return e},ao.max=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i<u;)if(null!=(r=n[i])&&r>=r){e=r;break}for(;++i<u;)null!=(r=n[i])&&r>e&&(e=r)}else{for(;++i<u;)if(null!=(r=t.call(n,n[i],i))&&r>=r){e=r;break}for(;++i<u;)null!=(r=t.call(n,n[i],i))&&r>e&&(e=r)}return e},ao.extent=function(n,t){var e,r,i,u=-1,o=n.length;if(1===arguments.length){for(;++u<o;)if(null!=(r=n[u])&&r>=r){e=i=r;break}for(;++u<o;)null!=(r=n[u])&&(e>r&&(e=r),r>i&&(i=r))}else{for(;++u<o;)if(null!=(r=t.call(n,n[u],u))&&r>=r){e=i=r;break}for(;++u<o;)null!=(r=t.call(n,n[u],u))&&(e>r&&(e=r),r>i&&(i=r))}return[e,i]},ao.sum=function(n,t){var e,r=0,u=n.length,o=-1;if(1===arguments.length)for(;++o<u;)i(e=+n[o])&&(r+=e);else for(;++o<u;)i(e=+t.call(n,n[o],o))&&(r+=e);return r},ao.mean=function(n,t){var e,u=0,o=n.length,a=-1,l=o;if(1===arguments.length)for(;++a<o;)i(e=r(n[a]))?u+=e:--l;else for(;++a<o;)i(e=r(t.call(n,n[a],a)))?u+=e:--l;return l?u/l:void 0},ao.quantile=function(n,t){var e=(n.length-1)*t+1,r=Math.floor(e),i=+n[r-1],u=e-r;return u?i+u*(n[r]-i):i},ao.median=function(n,t){var u,o=[],a=n.length,l=-1;if(1===arguments.length)for(;++l<a;)i(u=r(n[l]))&&o.push(u);else for(;++l<a;)i(u=r(t.call(n,n[l],l)))&&o.push(u);return o.length?ao.quantile(o.sort(e),.5):void 0},ao.variance=function(n,t){var e,u,o=n.length,a=0,l=0,c=-1,f=0;if(1===arguments.length)for(;++c<o;)i(e=r(n[c]))&&(u=e-a,a+=u/++f,l+=u*(e-a));else for(;++c<o;)i(e=r(t.call(n,n[c],c)))&&(u=e-a,a+=u/++f,l+=u*(e-a));return f>1?l/(f-1):void 0},ao.deviation=function(){var n=ao.variance.apply(this,arguments);return n?Math.sqrt(n):n};var Mo=u(e);ao.bisectLeft=Mo.left,ao.bisect=ao.bisectRight=Mo.right,ao.bisector=function(n){return u(1===n.length?function(t,r){return e(n(t),r)}:n)},ao.shuffle=function(n,t,e){(u=arguments.length)<3&&(e=n.length,2>u&&(t=0));for(var r,i,u=e-t;u;)i=Math.random()*u--|0,r=n[u+t],n[u+t]=n[i+t],n[i+t]=r;return n},ao.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},ao.pairs=function(n){for(var t,e=0,r=n.length-1,i=n[0],u=new Array(0>r?0:r);r>e;)u[e]=[t=i,i=n[++e]];return u},ao.transpose=function(n){if(!(i=n.length))return[];for(var t=-1,e=ao.min(n,o),r=new Array(e);++t<e;)for(var i,u=-1,a=r[t]=new Array(i);++u<i;)a[u]=n[u][t];return r},ao.zip=function(){return ao.transpose(arguments)},ao.keys=function(n){var t=[];for(var e in n)t.push(e);return t},ao.values=function(n){var t=[];for(var e in n)t.push(n[e]);return t},ao.entries=function(n){var t=[];for(var e in n)t.push({key:e,value:n[e]});return t},ao.merge=function(n){for(var t,e,r,i=n.length,u=-1,o=0;++u<i;)o+=n[u].length;for(e=new Array(o);--i>=0;)for(r=n[i],t=r.length;--t>=0;)e[--o]=r[t];return e};var xo=Math.abs;ao.range=function(n,t,e){if(arguments.length<3&&(e=1,arguments.length<2&&(t=n,n=0)),(t-n)/e===1/0)throw new Error("infinite range");var r,i=[],u=a(xo(e)),o=-1;if(n*=u,t*=u,e*=u,0>e)for(;(r=n+e*++o)>t;)i.push(r/u);else for(;(r=n+e*++o)<t;)i.push(r/u);return i},ao.map=function(n,t){var e=new c;if(n instanceof c)n.forEach(function(n,t){e.set(n,t)});else if(Array.isArray(n)){var r,i=-1,u=n.length;if(1===arguments.length)for(;++i<u;)e.set(i,n[i]);else for(;++i<u;)e.set(t.call(n,r=n[i],i),r)}else for(var o in n)e.set(o,n[o]);return e};var bo="__proto__",_o="\x00";l(c,{has:h,get:function(n){return this._[f(n)]},set:function(n,t){return this._[f(n)]=t},remove:p,keys:g,values:function(){var n=[];for(var t in this._)n.push(this._[t]);return n},entries:function(){var n=[];for(var t in this._)n.push({key:s(t),value:this._[t]});return n},size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,s(t),this._[t])}}),ao.nest=function(){function n(t,o,a){if(a>=u.length)return r?r.call(i,o):e?o.sort(e):o;for(var l,f,s,h,p=-1,g=o.length,v=u[a++],d=new c;++p<g;)(h=d.get(l=v(f=o[p])))?h.push(f):d.set(l,[f]);return t?(f=t(),s=function(e,r){f.set(e,n(t,r,a))}):(f={},s=function(e,r){f[e]=n(t,r,a)}),d.forEach(s),f}function t(n,e){if(e>=u.length)return n;var r=[],i=o[e++];return n.forEach(function(n,i){r.push({key:n,values:t(i,e)})}),i?r.sort(function(n,t){return i(n.key,t.key)}):r}var e,r,i={},u=[],o=[];return i.map=function(t,e){return n(e,t,0)},i.entries=function(e){return t(n(ao.map,e,0),0)},i.key=function(n){return u.push(n),i},i.sortKeys=function(n){return o[u.length-1]=n,i},i.sortValues=function(n){return e=n,i},i.rollup=function(n){return r=n,i},i},ao.set=function(n){var t=new y;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},l(y,{has:h,add:function(n){return this._[f(n+="")]=!0,n},remove:p,values:g,size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,s(t))}}),ao.behavior={},ao.rebind=function(n,t){for(var e,r=1,i=arguments.length;++r<i;)n[e=arguments[r]]=M(n,t,t[e]);return n};var wo=["webkit","ms","moz","Moz","o","O"];ao.dispatch=function(){for(var n=new _,t=-1,e=arguments.length;++t<e;)n[arguments[t]]=w(n);return n},_.prototype.on=function(n,t){var e=n.indexOf("."),r="";if(e>=0&&(r=n.slice(e+1),n=n.slice(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},ao.event=null,ao.requote=function(n){return n.replace(So,"\\$&")};var So=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ko={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},No=function(n,t){return t.querySelector(n)},Eo=function(n,t){return t.querySelectorAll(n)},Ao=function(n,t){var e=n.matches||n[x(n,"matchesSelector")];return(Ao=function(n,t){return e.call(n,t)})(n,t)};"function"==typeof Sizzle&&(No=function(n,t){return Sizzle(n,t)[0]||null},Eo=Sizzle,Ao=Sizzle.matchesSelector),ao.selection=function(){return ao.select(fo.documentElement)};var Co=ao.selection.prototype=[];Co.select=function(n){var t,e,r,i,u=[];n=A(n);for(var o=-1,a=this.length;++o<a;){u.push(t=[]),t.parentNode=(r=this[o]).parentNode;for(var l=-1,c=r.length;++l<c;)(i=r[l])?(t.push(e=n.call(i,i.__data__,l,o)),e&&"__data__"in i&&(e.__data__=i.__data__)):t.push(null)}return E(u)},Co.selectAll=function(n){var t,e,r=[];n=C(n);for(var i=-1,u=this.length;++i<u;)for(var o=this[i],a=-1,l=o.length;++a<l;)(e=o[a])&&(r.push(t=co(n.call(e,e.__data__,a,i))),t.parentNode=e);return E(r)};var zo="http://www.w3.org/1999/xhtml",Lo={svg:"http://www.w3.org/2000/svg",xhtml:zo,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};ao.ns={prefix:Lo,qualify:function(n){var t=n.indexOf(":"),e=n;return t>=0&&"xmlns"!==(e=n.slice(0,t))&&(n=n.slice(t+1)),Lo.hasOwnProperty(e)?{space:Lo[e],local:n}:n}},Co.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=ao.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(z(t,n[t]));return this}return this.each(z(n,t))},Co.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=T(n)).length,i=-1;if(t=e.classList){for(;++i<r;)if(!t.contains(n[i]))return!1}else for(t=e.getAttribute("class");++i<r;)if(!q(n[i]).test(t))return!1;return!0}for(t in n)this.each(R(t,n[t]));return this}return this.each(R(n,t))},Co.style=function(n,e,r){var i=arguments.length;if(3>i){if("string"!=typeof n){2>i&&(e="");for(r in n)this.each(P(r,n[r],e));return this}if(2>i){var u=this.node();return t(u).getComputedStyle(u,null).getPropertyValue(n)}r=""}return this.each(P(n,e,r))},Co.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(U(t,n[t]));return this}return this.each(U(n,t))},Co.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},Co.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},Co.append=function(n){return n=j(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},Co.insert=function(n,t){return n=j(n),t=A(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},Co.remove=function(){return this.each(F)},Co.data=function(n,t){function e(n,e){var r,i,u,o=n.length,s=e.length,h=Math.min(o,s),p=new Array(s),g=new Array(s),v=new Array(o);if(t){var d,y=new c,m=new Array(o);for(r=-1;++r<o;)(i=n[r])&&(y.has(d=t.call(i,i.__data__,r))?v[r]=i:y.set(d,i),m[r]=d);for(r=-1;++r<s;)(i=y.get(d=t.call(e,u=e[r],r)))?i!==!0&&(p[r]=i,i.__data__=u):g[r]=H(u),y.set(d,!0);for(r=-1;++r<o;)r in m&&y.get(m[r])!==!0&&(v[r]=n[r])}else{for(r=-1;++r<h;)i=n[r],u=e[r],i?(i.__data__=u,p[r]=i):g[r]=H(u);for(;s>r;++r)g[r]=H(e[r]);for(;o>r;++r)v[r]=n[r]}g.update=p,g.parentNode=p.parentNode=v.parentNode=n.parentNode,a.push(g),l.push(p),f.push(v)}var r,i,u=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(r=this[0]).length);++u<o;)(i=r[u])&&(n[u]=i.__data__);return n}var a=Z([]),l=E([]),f=E([]);if("function"==typeof n)for(;++u<o;)e(r=this[u],n.call(r,r.parentNode.__data__,u));else for(;++u<o;)e(r=this[u],n);return l.enter=function(){return a},l.exit=function(){return f},l},Co.datum=function(n){return arguments.length?this.property("__data__",n):this.property("__data__")},Co.filter=function(n){var t,e,r,i=[];"function"!=typeof n&&(n=O(n));for(var u=0,o=this.length;o>u;u++){i.push(t=[]),t.parentNode=(e=this[u]).parentNode;for(var a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return E(i)},Co.order=function(){for(var n=-1,t=this.length;++n<t;)for(var e,r=this[n],i=r.length-1,u=r[i];--i>=0;)(e=r[i])&&(u&&u!==e.nextSibling&&u.parentNode.insertBefore(e,u),u=e);return this},Co.sort=function(n){n=I.apply(this,arguments);for(var t=-1,e=this.length;++t<e;)this[t].sort(n);return this.order()},Co.each=function(n){return Y(this,function(t,e,r){n.call(t,t.__data__,e,r)})},Co.call=function(n){var t=co(arguments);return n.apply(t[0]=this,t),this},Co.empty=function(){return!this.node()},Co.node=function(){for(var n=0,t=this.length;t>n;n++)for(var e=this[n],r=0,i=e.length;i>r;r++){var u=e[r];if(u)return u}return null},Co.size=function(){var n=0;return Y(this,function(){++n}),n};var qo=[];ao.selection.enter=Z,ao.selection.enter.prototype=qo,qo.append=Co.append,qo.empty=Co.empty,qo.node=Co.node,qo.call=Co.call,qo.size=Co.size,qo.select=function(n){for(var t,e,r,i,u,o=[],a=-1,l=this.length;++a<l;){r=(i=this[a]).update,o.push(t=[]),t.parentNode=i.parentNode;for(var c=-1,f=i.length;++c<f;)(u=i[c])?(t.push(r[c]=e=n.call(i.parentNode,u.__data__,c,a)),e.__data__=u.__data__):t.push(null)}return E(o)},qo.insert=function(n,t){return arguments.length<2&&(t=V(this)),Co.insert.call(this,n,t)},ao.select=function(t){var e;return"string"==typeof t?(e=[No(t,fo)],e.parentNode=fo.documentElement):(e=[t],e.parentNode=n(t)),E([e])},ao.selectAll=function(n){var t;return"string"==typeof n?(t=co(Eo(n,fo)),t.parentNode=fo.documentElement):(t=co(n),t.parentNode=null),E([t])},Co.on=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(X(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(X(n,t,e))};var To=ao.map({mouseenter:"mouseover",mouseleave:"mouseout"});fo&&To.forEach(function(n){"on"+n in fo&&To.remove(n)});var Ro,Do=0;ao.mouse=function(n){return J(n,k())};var Po=this.navigator&&/WebKit/.test(this.navigator.userAgent)?-1:0;ao.touch=function(n,t,e){if(arguments.length<3&&(e=t,t=k().changedTouches),t)for(var r,i=0,u=t.length;u>i;++i)if((r=t[i]).identifier===e)return J(n,r)},ao.behavior.drag=function(){function n(){this.on("mousedown.drag",u).on("touchstart.drag",o)}function e(n,t,e,u,o){return function(){function a(){var n,e,r=t(h,v);r&&(n=r[0]-M[0],e=r[1]-M[1],g|=n|e,M=r,p({type:"drag",x:r[0]+c[0],y:r[1]+c[1],dx:n,dy:e}))}function l(){t(h,v)&&(y.on(u+d,null).on(o+d,null),m(g),p({type:"dragend"}))}var c,f=this,s=ao.event.target.correspondingElement||ao.event.target,h=f.parentNode,p=r.of(f,arguments),g=0,v=n(),d=".drag"+(null==v?"":"-"+v),y=ao.select(e(s)).on(u+d,a).on(o+d,l),m=W(s),M=t(h,v);i?(c=i.apply(f,arguments),c=[c.x-M[0],c.y-M[1]]):c=[0,0],p({type:"dragstart"})}}var r=N(n,"drag","dragstart","dragend"),i=null,u=e(b,ao.mouse,t,"mousemove","mouseup"),o=e(G,ao.touch,m,"touchmove","touchend");return n.origin=function(t){return arguments.length?(i=t,n):i},ao.rebind(n,r,"on")},ao.touches=function(n,t){return arguments.length<2&&(t=k().touches),t?co(t).map(function(t){var e=J(n,t);return e.identifier=t.identifier,e}):[]};var Uo=1e-6,jo=Uo*Uo,Fo=Math.PI,Ho=2*Fo,Oo=Ho-Uo,Io=Fo/2,Yo=Fo/180,Zo=180/Fo,Vo=Math.SQRT2,Xo=2,$o=4;ao.interpolateZoom=function(n,t){var e,r,i=n[0],u=n[1],o=n[2],a=t[0],l=t[1],c=t[2],f=a-i,s=l-u,h=f*f+s*s;if(jo>h)r=Math.log(c/o)/Vo,e=function(n){return[i+n*f,u+n*s,o*Math.exp(Vo*n*r)]};else{var p=Math.sqrt(h),g=(c*c-o*o+$o*h)/(2*o*Xo*p),v=(c*c-o*o-$o*h)/(2*c*Xo*p),d=Math.log(Math.sqrt(g*g+1)-g),y=Math.log(Math.sqrt(v*v+1)-v);r=(y-d)/Vo,e=function(n){var t=n*r,e=rn(d),a=o/(Xo*p)*(e*un(Vo*t+d)-en(d));return[i+a*f,u+a*s,o*e/rn(Vo*t+d)]}}return e.duration=1e3*r,e},ao.behavior.zoom=function(){function n(n){n.on(L,s).on(Wo+".zoom",p).on("dblclick.zoom",g).on(R,h)}function e(n){return[(n[0]-k.x)/k.k,(n[1]-k.y)/k.k]}function r(n){return[n[0]*k.k+k.x,n[1]*k.k+k.y]}function i(n){k.k=Math.max(A[0],Math.min(A[1],n))}function u(n,t){t=r(t),k.x+=n[0]-t[0],k.y+=n[1]-t[1]}function o(t,e,r,o){t.__chart__={x:k.x,y:k.y,k:k.k},i(Math.pow(2,o)),u(d=e,r),t=ao.select(t),C>0&&(t=t.transition().duration(C)),t.call(n.event)}function a(){b&&b.domain(x.range().map(function(n){return(n-k.x)/k.k}).map(x.invert)),w&&w.domain(_.range().map(function(n){return(n-k.y)/k.k}).map(_.invert))}function l(n){z++||n({type:"zoomstart"})}function c(n){a(),n({type:"zoom",scale:k.k,translate:[k.x,k.y]})}function f(n){--z||(n({type:"zoomend"}),d=null)}function s(){function n(){a=1,u(ao.mouse(i),h),c(o)}function r(){s.on(q,null).on(T,null),p(a),f(o)}var i=this,o=D.of(i,arguments),a=0,s=ao.select(t(i)).on(q,n).on(T,r),h=e(ao.mouse(i)),p=W(i);Il.call(i),l(o)}function h(){function n(){var n=ao.touches(g);return p=k.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=e(n))}),n}function t(){var t=ao.event.target;ao.select(t).on(x,r).on(b,a),_.push(t);for(var e=ao.event.changedTouches,i=0,u=e.length;u>i;++i)d[e[i].identifier]=null;var l=n(),c=Date.now();if(1===l.length){if(500>c-M){var f=l[0];o(g,f,d[f.identifier],Math.floor(Math.log(k.k)/Math.LN2)+1),S()}M=c}else if(l.length>1){var f=l[0],s=l[1],h=f[0]-s[0],p=f[1]-s[1];y=h*h+p*p}}function r(){var n,t,e,r,o=ao.touches(g);Il.call(g);for(var a=0,l=o.length;l>a;++a,r=null)if(e=o[a],r=d[e.identifier]){if(t)break;n=e,t=r}if(r){var f=(f=e[0]-n[0])*f+(f=e[1]-n[1])*f,s=y&&Math.sqrt(f/y);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+r[0])/2,(t[1]+r[1])/2],i(s*p)}M=null,u(n,t),c(v)}function a(){if(ao.event.touches.length){for(var t=ao.event.changedTouches,e=0,r=t.length;r>e;++e)delete d[t[e].identifier];for(var i in d)return void n()}ao.selectAll(_).on(m,null),w.on(L,s).on(R,h),N(),f(v)}var p,g=this,v=D.of(g,arguments),d={},y=0,m=".zoom-"+ao.event.changedTouches[0].identifier,x="touchmove"+m,b="touchend"+m,_=[],w=ao.select(g),N=W(g);t(),l(v),w.on(L,null).on(R,t)}function p(){var n=D.of(this,arguments);m?clearTimeout(m):(Il.call(this),v=e(d=y||ao.mouse(this)),l(n)),m=setTimeout(function(){m=null,f(n)},50),S(),i(Math.pow(2,.002*Bo())*k.k),u(d,v),c(n)}function g(){var n=ao.mouse(this),t=Math.log(k.k)/Math.LN2;o(this,n,e(n),ao.event.shiftKey?Math.ceil(t)-1:Math.floor(t)+1)}var v,d,y,m,M,x,b,_,w,k={x:0,y:0,k:1},E=[960,500],A=Jo,C=250,z=0,L="mousedown.zoom",q="mousemove.zoom",T="mouseup.zoom",R="touchstart.zoom",D=N(n,"zoomstart","zoom","zoomend");return Wo||(Wo="onwheel"in fo?(Bo=function(){return-ao.event.deltaY*(ao.event.deltaMode?120:1)},"wheel"):"onmousewheel"in fo?(Bo=function(){return ao.event.wheelDelta},"mousewheel"):(Bo=function(){return-ao.event.detail},"MozMousePixelScroll")),n.event=function(n){n.each(function(){var n=D.of(this,arguments),t=k;Hl?ao.select(this).transition().each("start.zoom",function(){k=this.__chart__||{x:0,y:0,k:1},l(n)}).tween("zoom:zoom",function(){var e=E[0],r=E[1],i=d?d[0]:e/2,u=d?d[1]:r/2,o=ao.interpolateZoom([(i-k.x)/k.k,(u-k.y)/k.k,e/k.k],[(i-t.x)/t.k,(u-t.y)/t.k,e/t.k]);return function(t){var r=o(t),a=e/r[2];this.__chart__=k={x:i-r[0]*a,y:u-r[1]*a,k:a},c(n)}}).each("interrupt.zoom",function(){f(n)}).each("end.zoom",function(){f(n)}):(this.__chart__=k,l(n),c(n),f(n))})},n.translate=function(t){return arguments.length?(k={x:+t[0],y:+t[1],k:k.k},a(),n):[k.x,k.y]},n.scale=function(t){return arguments.length?(k={x:k.x,y:k.y,k:null},i(+t),a(),n):k.k},n.scaleExtent=function(t){return arguments.length?(A=null==t?Jo:[+t[0],+t[1]],n):A},n.center=function(t){return arguments.length?(y=t&&[+t[0],+t[1]],n):y},n.size=function(t){return arguments.length?(E=t&&[+t[0],+t[1]],n):E},n.duration=function(t){return arguments.length?(C=+t,n):C},n.x=function(t){return arguments.length?(b=t,x=t.copy(),k={x:0,y:0,k:1},n):b},n.y=function(t){return arguments.length?(w=t,_=t.copy(),k={x:0,y:0,k:1},n):w},ao.rebind(n,D,"on")};var Bo,Wo,Jo=[0,1/0];ao.color=an,an.prototype.toString=function(){return this.rgb()+""},ao.hsl=ln;var Go=ln.prototype=new an;Go.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,this.l/n)},Go.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,n*this.l)},Go.rgb=function(){return cn(this.h,this.s,this.l)},ao.hcl=fn;var Ko=fn.prototype=new an;Ko.brighter=function(n){return new fn(this.h,this.c,Math.min(100,this.l+Qo*(arguments.length?n:1)))},Ko.darker=function(n){return new fn(this.h,this.c,Math.max(0,this.l-Qo*(arguments.length?n:1)))},Ko.rgb=function(){return sn(this.h,this.c,this.l).rgb()},ao.lab=hn;var Qo=18,na=.95047,ta=1,ea=1.08883,ra=hn.prototype=new an;ra.brighter=function(n){return new hn(Math.min(100,this.l+Qo*(arguments.length?n:1)),this.a,this.b)},ra.darker=function(n){return new hn(Math.max(0,this.l-Qo*(arguments.length?n:1)),this.a,this.b)},ra.rgb=function(){return pn(this.l,this.a,this.b)},ao.rgb=mn;var ia=mn.prototype=new an;ia.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,i=30;return t||e||r?(t&&i>t&&(t=i),e&&i>e&&(e=i),r&&i>r&&(r=i),new mn(Math.min(255,t/n),Math.min(255,e/n),Math.min(255,r/n))):new mn(i,i,i)},ia.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new mn(n*this.r,n*this.g,n*this.b)},ia.hsl=function(){return wn(this.r,this.g,this.b)},ia.toString=function(){return"#"+bn(this.r)+bn(this.g)+bn(this.b)};var ua=ao.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});ua.forEach(function(n,t){ua.set(n,Mn(t))}),ao.functor=En,ao.xhr=An(m),ao.dsv=function(n,t){function e(n,e,u){arguments.length<3&&(u=e,e=null);var o=Cn(n,t,null==e?r:i(e),u);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:i(n)):e},o}function r(n){return e.parse(n.responseText)}function i(n){return function(t){return e.parse(t.responseText,n)}}function u(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),l=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var i=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(i(n),e)}:i})},e.parseRows=function(n,t){function e(){if(f>=c)return o;if(i)return i=!1,u;var t=f;if(34===n.charCodeAt(t)){for(var e=t;e++<c;)if(34===n.charCodeAt(e)){if(34!==n.charCodeAt(e+1))break;++e}f=e+2;var r=n.charCodeAt(e+1);return 13===r?(i=!0,10===n.charCodeAt(e+2)&&++f):10===r&&(i=!0),n.slice(t+1,e).replace(/""/g,'"')}for(;c>f;){var r=n.charCodeAt(f++),a=1;if(10===r)i=!0;else if(13===r)i=!0,10===n.charCodeAt(f)&&(++f,++a);else if(r!==l)continue;return n.slice(t,f-a)}return n.slice(t)}for(var r,i,u={},o={},a=[],c=n.length,f=0,s=0;(r=e())!==o;){for(var h=[];r!==u&&r!==o;)h.push(r),r=e();t&&null==(h=t(h,s++))||a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new y,i=[];return t.forEach(function(n){for(var t in n)r.has(t)||i.push(r.add(t))}),[i.map(o).join(n)].concat(t.map(function(t){return i.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(u).join("\n")},e},ao.csv=ao.dsv(",","text/csv"),ao.tsv=ao.dsv("	","text/tab-separated-values");var oa,aa,la,ca,fa=this[x(this,"requestAnimationFrame")]||function(n){setTimeout(n,17)};ao.timer=function(){qn.apply(this,arguments)},ao.timer.flush=function(){Rn(),Dn()},ao.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var sa=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Un);ao.formatPrefix=function(n,t){var e=0;return(n=+n)&&(0>n&&(n*=-1),t&&(n=ao.round(n,Pn(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((e-1)/3)))),sa[8+e/3]};var ha=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,pa=ao.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=ao.round(n,Pn(n,t))).toFixed(Math.max(0,Math.min(20,Pn(n*(1+1e-15),t))))}}),ga=ao.time={},va=Date;Hn.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){da.setUTCDate.apply(this._,arguments)},setDay:function(){da.setUTCDay.apply(this._,arguments)},setFullYear:function(){da.setUTCFullYear.apply(this._,arguments)},setHours:function(){da.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){da.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){da.setUTCMinutes.apply(this._,arguments)},setMonth:function(){da.setUTCMonth.apply(this._,arguments)},setSeconds:function(){da.setUTCSeconds.apply(this._,arguments)},setTime:function(){da.setTime.apply(this._,arguments)}};var da=Date.prototype;ga.year=On(function(n){return n=ga.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),ga.years=ga.year.range,ga.years.utc=ga.year.utc.range,ga.day=On(function(n){var t=new va(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),ga.days=ga.day.range,ga.days.utc=ga.day.utc.range,ga.dayOfYear=function(n){var t=ga.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=ga[n]=On(function(n){return(n=ga.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});ga[n+"s"]=e.range,ga[n+"s"].utc=e.utc.range,ga[n+"OfYear"]=function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)}}),ga.week=ga.sunday,ga.weeks=ga.sunday.range,ga.weeks.utc=ga.sunday.utc.range,ga.weekOfYear=ga.sundayOfYear;var ya={"-":"",_:" ",0:"0"},ma=/^\s*\d+/,Ma=/^%/;ao.locale=function(n){return{numberFormat:jn(n),timeFormat:Yn(n)}};var xa=ao.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],
			shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});ao.format=xa.numberFormat,ao.geo={},ft.prototype={s:0,t:0,add:function(n){st(n,this.t,ba),st(ba.s,this.s,this),this.s?this.t+=ba.t:this.s=ba.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var ba=new ft;ao.geo.stream=function(n,t){n&&_a.hasOwnProperty(n.type)?_a[n.type](n,t):ht(n,t)};var _a={Feature:function(n,t){ht(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,i=e.length;++r<i;)ht(e[r].geometry,t)}},wa={Sphere:function(n,t){t.sphere()},Point:function(n,t){n=n.coordinates,t.point(n[0],n[1],n[2])},MultiPoint:function(n,t){for(var e=n.coordinates,r=-1,i=e.length;++r<i;)n=e[r],t.point(n[0],n[1],n[2])},LineString:function(n,t){pt(n.coordinates,t,0)},MultiLineString:function(n,t){for(var e=n.coordinates,r=-1,i=e.length;++r<i;)pt(e[r],t,0)},Polygon:function(n,t){gt(n.coordinates,t)},MultiPolygon:function(n,t){for(var e=n.coordinates,r=-1,i=e.length;++r<i;)gt(e[r],t)},GeometryCollection:function(n,t){for(var e=n.geometries,r=-1,i=e.length;++r<i;)ht(e[r],t)}};ao.geo.area=function(n){return Sa=0,ao.geo.stream(n,Na),Sa};var Sa,ka=new ft,Na={sphere:function(){Sa+=4*Fo},point:b,lineStart:b,lineEnd:b,polygonStart:function(){ka.reset(),Na.lineStart=vt},polygonEnd:function(){var n=2*ka;Sa+=0>n?4*Fo+n:n,Na.lineStart=Na.lineEnd=Na.point=b}};ao.geo.bounds=function(){function n(n,t){M.push(x=[f=n,h=n]),s>t&&(s=t),t>p&&(p=t)}function t(t,e){var r=dt([t*Yo,e*Yo]);if(y){var i=mt(y,r),u=[i[1],-i[0],0],o=mt(u,i);bt(o),o=_t(o);var l=t-g,c=l>0?1:-1,v=o[0]*Zo*c,d=xo(l)>180;if(d^(v>c*g&&c*t>v)){var m=o[1]*Zo;m>p&&(p=m)}else if(v=(v+360)%360-180,d^(v>c*g&&c*t>v)){var m=-o[1]*Zo;s>m&&(s=m)}else s>e&&(s=e),e>p&&(p=e);d?g>t?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t):h>=f?(f>t&&(f=t),t>h&&(h=t)):t>g?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t)}else n(t,e);y=r,g=t}function e(){b.point=t}function r(){x[0]=f,x[1]=h,b.point=n,y=null}function i(n,e){if(y){var r=n-g;m+=xo(r)>180?r+(r>0?360:-360):r}else v=n,d=e;Na.point(n,e),t(n,e)}function u(){Na.lineStart()}function o(){i(v,d),Na.lineEnd(),xo(m)>Uo&&(f=-(h=180)),x[0]=f,x[1]=h,y=null}function a(n,t){return(t-=n)<0?t+360:t}function l(n,t){return n[0]-t[0]}function c(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:n<t[0]||t[1]<n}var f,s,h,p,g,v,d,y,m,M,x,b={point:n,lineStart:e,lineEnd:r,polygonStart:function(){b.point=i,b.lineStart=u,b.lineEnd=o,m=0,Na.polygonStart()},polygonEnd:function(){Na.polygonEnd(),b.point=n,b.lineStart=e,b.lineEnd=r,0>ka?(f=-(h=180),s=-(p=90)):m>Uo?p=90:-Uo>m&&(s=-90),x[0]=f,x[1]=h}};return function(n){p=h=-(f=s=1/0),M=[],ao.geo.stream(n,b);var t=M.length;if(t){M.sort(l);for(var e,r=1,i=M[0],u=[i];t>r;++r)e=M[r],c(e[0],i)||c(e[1],i)?(a(i[0],e[1])>a(i[0],i[1])&&(i[1]=e[1]),a(e[0],i[1])>a(i[0],i[1])&&(i[0]=e[0])):u.push(i=e);for(var o,e,g=-(1/0),t=u.length-1,r=0,i=u[t];t>=r;i=e,++r)e=u[r],(o=a(i[1],e[0]))>g&&(g=o,f=e[0],h=i[1])}return M=x=null,f===1/0||s===1/0?[[NaN,NaN],[NaN,NaN]]:[[f,s],[h,p]]}}(),ao.geo.centroid=function(n){Ea=Aa=Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,ja);var t=Da,e=Pa,r=Ua,i=t*t+e*e+r*r;return jo>i&&(t=qa,e=Ta,r=Ra,Uo>Aa&&(t=Ca,e=za,r=La),i=t*t+e*e+r*r,jo>i)?[NaN,NaN]:[Math.atan2(e,t)*Zo,tn(r/Math.sqrt(i))*Zo]};var Ea,Aa,Ca,za,La,qa,Ta,Ra,Da,Pa,Ua,ja={sphere:b,point:St,lineStart:Nt,lineEnd:Et,polygonStart:function(){ja.lineStart=At},polygonEnd:function(){ja.lineStart=Nt}},Fa=Rt(zt,jt,Ht,[-Fo,-Fo/2]),Ha=1e9;ao.geo.clipExtent=function(){var n,t,e,r,i,u,o={stream:function(n){return i&&(i.valid=!1),i=u(n),i.valid=!0,i},extent:function(a){return arguments.length?(u=Zt(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),i&&(i.valid=!1,i=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(ao.geo.conicEqualArea=function(){return Vt(Xt)}).raw=Xt,ao.geo.albers=function(){return ao.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},ao.geo.albersUsa=function(){function n(n){var u=n[0],o=n[1];return t=null,e(u,o),t||(r(u,o),t)||i(u,o),t}var t,e,r,i,u=ao.geo.albers(),o=ao.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=ao.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),l={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=u.scale(),e=u.translate(),r=(n[0]-e[0])/t,i=(n[1]-e[1])/t;return(i>=.12&&.234>i&&r>=-.425&&-.214>r?o:i>=.166&&.234>i&&r>=-.214&&-.115>r?a:u).invert(n)},n.stream=function(n){var t=u.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,i){t.point(n,i),e.point(n,i),r.point(n,i)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(u.precision(t),o.precision(t),a.precision(t),n):u.precision()},n.scale=function(t){return arguments.length?(u.scale(t),o.scale(.35*t),a.scale(t),n.translate(u.translate())):u.scale()},n.translate=function(t){if(!arguments.length)return u.translate();var c=u.scale(),f=+t[0],s=+t[1];return e=u.translate(t).clipExtent([[f-.455*c,s-.238*c],[f+.455*c,s+.238*c]]).stream(l).point,r=o.translate([f-.307*c,s+.201*c]).clipExtent([[f-.425*c+Uo,s+.12*c+Uo],[f-.214*c-Uo,s+.234*c-Uo]]).stream(l).point,i=a.translate([f-.205*c,s+.212*c]).clipExtent([[f-.214*c+Uo,s+.166*c+Uo],[f-.115*c-Uo,s+.234*c-Uo]]).stream(l).point,n},n.scale(1070)};var Oa,Ia,Ya,Za,Va,Xa,$a={point:b,lineStart:b,lineEnd:b,polygonStart:function(){Ia=0,$a.lineStart=$t},polygonEnd:function(){$a.lineStart=$a.lineEnd=$a.point=b,Oa+=xo(Ia/2)}},Ba={point:Bt,lineStart:b,lineEnd:b,polygonStart:b,polygonEnd:b},Wa={point:Gt,lineStart:Kt,lineEnd:Qt,polygonStart:function(){Wa.lineStart=ne},polygonEnd:function(){Wa.point=Gt,Wa.lineStart=Kt,Wa.lineEnd=Qt}};ao.geo.path=function(){function n(n){return n&&("function"==typeof a&&u.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=i(u)),ao.geo.stream(n,o)),u.result()}function t(){return o=null,n}var e,r,i,u,o,a=4.5;return n.area=function(n){return Oa=0,ao.geo.stream(n,i($a)),Oa},n.centroid=function(n){return Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,i(Wa)),Ua?[Da/Ua,Pa/Ua]:Ra?[qa/Ra,Ta/Ra]:La?[Ca/La,za/La]:[NaN,NaN]},n.bounds=function(n){return Va=Xa=-(Ya=Za=1/0),ao.geo.stream(n,i(Ba)),[[Ya,Za],[Va,Xa]]},n.projection=function(n){return arguments.length?(i=(e=n)?n.stream||re(n):m,t()):e},n.context=function(n){return arguments.length?(u=null==(r=n)?new Wt:new te(n),"function"!=typeof a&&u.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(u.pointRadius(+t),+t),n):a},n.projection(ao.geo.albersUsa()).context(null)},ao.geo.transform=function(n){return{stream:function(t){var e=new ie(t);for(var r in n)e[r]=n[r];return e}}},ie.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},ao.geo.projection=oe,ao.geo.projectionMutator=ae,(ao.geo.equirectangular=function(){return oe(ce)}).raw=ce.invert=ce,ao.geo.rotation=function(n){function t(t){return t=n(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t}return n=se(n[0]%360*Yo,n[1]*Yo,n.length>2?n[2]*Yo:0),t.invert=function(t){return t=n.invert(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t},t},fe.invert=ce,ao.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=se(-n[0]*Yo,-n[1]*Yo,0).invert,i=[];return e(null,null,1,{point:function(n,e){i.push(n=t(n,e)),n[0]*=Zo,n[1]*=Zo}}),{type:"Polygon",coordinates:[i]}}var t,e,r=[0,0],i=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=ve((t=+r)*Yo,i*Yo),n):t},n.precision=function(r){return arguments.length?(e=ve(t*Yo,(i=+r)*Yo),n):i},n.angle(90)},ao.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Yo,i=n[1]*Yo,u=t[1]*Yo,o=Math.sin(r),a=Math.cos(r),l=Math.sin(i),c=Math.cos(i),f=Math.sin(u),s=Math.cos(u);return Math.atan2(Math.sqrt((e=s*o)*e+(e=c*f-l*s*a)*e),l*f+c*s*a)},ao.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return ao.range(Math.ceil(u/d)*d,i,d).map(h).concat(ao.range(Math.ceil(c/y)*y,l,y).map(p)).concat(ao.range(Math.ceil(r/g)*g,e,g).filter(function(n){return xo(n%d)>Uo}).map(f)).concat(ao.range(Math.ceil(a/v)*v,o,v).filter(function(n){return xo(n%y)>Uo}).map(s))}var e,r,i,u,o,a,l,c,f,s,h,p,g=10,v=g,d=90,y=360,m=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(u).concat(p(l).slice(1),h(i).reverse().slice(1),p(c).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(u=+t[0][0],i=+t[1][0],c=+t[0][1],l=+t[1][1],u>i&&(t=u,u=i,i=t),c>l&&(t=c,c=l,l=t),n.precision(m)):[[u,c],[i,l]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(m)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],y=+t[1],n):[d,y]},n.minorStep=function(t){return arguments.length?(g=+t[0],v=+t[1],n):[g,v]},n.precision=function(t){return arguments.length?(m=+t,f=ye(a,o,90),s=me(r,e,m),h=ye(c,l,90),p=me(u,i,m),n):m},n.majorExtent([[-180,-90+Uo],[180,90-Uo]]).minorExtent([[-180,-80-Uo],[180,80+Uo]])},ao.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||i.apply(this,arguments)]}}var t,e,r=Me,i=xe;return n.distance=function(){return ao.geo.distance(t||r.apply(this,arguments),e||i.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(i=t,e="function"==typeof t?null:t,n):i},n.precision=function(){return arguments.length?n:0},n},ao.geo.interpolate=function(n,t){return be(n[0]*Yo,n[1]*Yo,t[0]*Yo,t[1]*Yo)},ao.geo.length=function(n){return Ja=0,ao.geo.stream(n,Ga),Ja};var Ja,Ga={sphere:b,point:b,lineStart:_e,lineEnd:b,polygonStart:b,polygonEnd:b},Ka=we(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(ao.geo.azimuthalEqualArea=function(){return oe(Ka)}).raw=Ka;var Qa=we(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},m);(ao.geo.azimuthalEquidistant=function(){return oe(Qa)}).raw=Qa,(ao.geo.conicConformal=function(){return Vt(Se)}).raw=Se,(ao.geo.conicEquidistant=function(){return Vt(ke)}).raw=ke;var nl=we(function(n){return 1/n},Math.atan);(ao.geo.gnomonic=function(){return oe(nl)}).raw=nl,Ne.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Io]},(ao.geo.mercator=function(){return Ee(Ne)}).raw=Ne;var tl=we(function(){return 1},Math.asin);(ao.geo.orthographic=function(){return oe(tl)}).raw=tl;var el=we(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(ao.geo.stereographic=function(){return oe(el)}).raw=el,Ae.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Io]},(ao.geo.transverseMercator=function(){var n=Ee(Ae),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[n[1],-n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},e([0,0,90])}).raw=Ae,ao.geom={},ao.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,i=En(e),u=En(r),o=n.length,a=[],l=[];for(t=0;o>t;t++)a.push([+i.call(this,n[t],t),+u.call(this,n[t],t),t]);for(a.sort(qe),t=0;o>t;t++)l.push([a[t][0],-a[t][1]]);var c=Le(a),f=Le(l),s=f[0]===c[0],h=f[f.length-1]===c[c.length-1],p=[];for(t=c.length-1;t>=0;--t)p.push(n[a[c[t]][2]]);for(t=+s;t<f.length-h;++t)p.push(n[a[f[t]][2]]);return p}var e=Ce,r=ze;return arguments.length?t(n):(t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t)},ao.geom.polygon=function(n){return ko(n,rl),n};var rl=ao.geom.polygon.prototype=[];rl.area=function(){for(var n,t=-1,e=this.length,r=this[e-1],i=0;++t<e;)n=r,r=this[t],i+=n[1]*r[0]-n[0]*r[1];return.5*i},rl.centroid=function(n){var t,e,r=-1,i=this.length,u=0,o=0,a=this[i-1];for(arguments.length||(n=-1/(6*this.area()));++r<i;)t=a,a=this[r],e=t[0]*a[1]-a[0]*t[1],u+=(t[0]+a[0])*e,o+=(t[1]+a[1])*e;return[u*n,o*n]},rl.clip=function(n){for(var t,e,r,i,u,o,a=De(n),l=-1,c=this.length-De(this),f=this[c-1];++l<c;){for(t=n.slice(),n.length=0,i=this[l],u=t[(r=t.length-a)-1],e=-1;++e<r;)o=t[e],Te(o,f,i)?(Te(u,f,i)||n.push(Re(u,o,f,i)),n.push(o)):Te(u,f,i)&&n.push(Re(u,o,f,i)),u=o;a&&n.push(n[0]),f=i}return n};var il,ul,ol,al,ll,cl=[],fl=[];Ye.prototype.prepare=function(){for(var n,t=this.edges,e=t.length;e--;)n=t[e].edge,n.b&&n.a||t.splice(e,1);return t.sort(Ve),t.length},tr.prototype={start:function(){return this.edge.l===this.site?this.edge.a:this.edge.b},end:function(){return this.edge.l===this.site?this.edge.b:this.edge.a}},er.prototype={insert:function(n,t){var e,r,i;if(n){if(t.P=n,t.N=n.N,n.N&&(n.N.P=t),n.N=t,n.R){for(n=n.R;n.L;)n=n.L;n.L=t}else n.R=t;e=n}else this._?(n=or(this._),t.P=null,t.N=n,n.P=n.L=t,e=n):(t.P=t.N=null,this._=t,e=null);for(t.L=t.R=null,t.U=e,t.C=!0,n=t;e&&e.C;)r=e.U,e===r.L?(i=r.R,i&&i.C?(e.C=i.C=!1,r.C=!0,n=r):(n===e.R&&(ir(this,e),n=e,e=n.U),e.C=!1,r.C=!0,ur(this,r))):(i=r.L,i&&i.C?(e.C=i.C=!1,r.C=!0,n=r):(n===e.L&&(ur(this,e),n=e,e=n.U),e.C=!1,r.C=!0,ir(this,r))),e=n.U;this._.C=!1},remove:function(n){n.N&&(n.N.P=n.P),n.P&&(n.P.N=n.N),n.N=n.P=null;var t,e,r,i=n.U,u=n.L,o=n.R;if(e=u?o?or(o):u:o,i?i.L===n?i.L=e:i.R=e:this._=e,u&&o?(r=e.C,e.C=n.C,e.L=u,u.U=e,e!==o?(i=e.U,e.U=n.U,n=e.R,i.L=n,e.R=o,o.U=e):(e.U=i,i=e,n=e.R)):(r=n.C,n=e),n&&(n.U=i),!r){if(n&&n.C)return void(n.C=!1);do{if(n===this._)break;if(n===i.L){if(t=i.R,t.C&&(t.C=!1,i.C=!0,ir(this,i),t=i.R),t.L&&t.L.C||t.R&&t.R.C){t.R&&t.R.C||(t.L.C=!1,t.C=!0,ur(this,t),t=i.R),t.C=i.C,i.C=t.R.C=!1,ir(this,i),n=this._;break}}else if(t=i.L,t.C&&(t.C=!1,i.C=!0,ur(this,i),t=i.L),t.L&&t.L.C||t.R&&t.R.C){t.L&&t.L.C||(t.R.C=!1,t.C=!0,ir(this,t),t=i.L),t.C=i.C,i.C=t.L.C=!1,ur(this,i),n=this._;break}t.C=!0,n=i,i=i.U}while(!n.C);n&&(n.C=!1)}}},ao.geom.voronoi=function(n){function t(n){var t=new Array(n.length),r=a[0][0],i=a[0][1],u=a[1][0],o=a[1][1];return ar(e(n),a).cells.forEach(function(e,a){var l=e.edges,c=e.site,f=t[a]=l.length?l.map(function(n){var t=n.start();return[t.x,t.y]}):c.x>=r&&c.x<=u&&c.y>=i&&c.y<=o?[[r,o],[u,o],[u,i],[r,i]]:[];f.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(u(n,t)/Uo)*Uo,y:Math.round(o(n,t)/Uo)*Uo,i:t}})}var r=Ce,i=ze,u=r,o=i,a=sl;return n?t(n):(t.links=function(n){return ar(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return ar(e(n)).cells.forEach(function(e,r){for(var i,u,o=e.site,a=e.edges.sort(Ve),l=-1,c=a.length,f=a[c-1].edge,s=f.l===o?f.r:f.l;++l<c;)i=f,u=s,f=a[l].edge,s=f.l===o?f.r:f.l,r<u.i&&r<s.i&&cr(o,u,s)<0&&t.push([n[r],n[u.i],n[s.i]])}),t},t.x=function(n){return arguments.length?(u=En(r=n),t):r},t.y=function(n){return arguments.length?(o=En(i=n),t):i},t.clipExtent=function(n){return arguments.length?(a=null==n?sl:n,t):a===sl?null:a},t.size=function(n){return arguments.length?t.clipExtent(n&&[[0,0],n]):a===sl?null:a&&a[1]},t)};var sl=[[-1e6,-1e6],[1e6,1e6]];ao.geom.delaunay=function(n){return ao.geom.voronoi().triangles(n)},ao.geom.quadtree=function(n,t,e,r,i){function u(n){function u(n,t,e,r,i,u,o,a){if(!isNaN(e)&&!isNaN(r))if(n.leaf){var l=n.x,f=n.y;if(null!=l)if(xo(l-e)+xo(f-r)<.01)c(n,t,e,r,i,u,o,a);else{var s=n.point;n.x=n.y=n.point=null,c(n,s,l,f,i,u,o,a),c(n,t,e,r,i,u,o,a)}else n.x=e,n.y=r,n.point=t}else c(n,t,e,r,i,u,o,a)}function c(n,t,e,r,i,o,a,l){var c=.5*(i+a),f=.5*(o+l),s=e>=c,h=r>=f,p=h<<1|s;n.leaf=!1,n=n.nodes[p]||(n.nodes[p]=hr()),s?i=c:a=c,h?o=f:l=f,u(n,t,e,r,i,o,a,l)}var f,s,h,p,g,v,d,y,m,M=En(a),x=En(l);if(null!=t)v=t,d=e,y=r,m=i;else if(y=m=-(v=d=1/0),s=[],h=[],g=n.length,o)for(p=0;g>p;++p)f=n[p],f.x<v&&(v=f.x),f.y<d&&(d=f.y),f.x>y&&(y=f.x),f.y>m&&(m=f.y),s.push(f.x),h.push(f.y);else for(p=0;g>p;++p){var b=+M(f=n[p],p),_=+x(f,p);v>b&&(v=b),d>_&&(d=_),b>y&&(y=b),_>m&&(m=_),s.push(b),h.push(_)}var w=y-v,S=m-d;w>S?m=d+w:y=v+S;var k=hr();if(k.add=function(n){u(k,n,+M(n,++p),+x(n,p),v,d,y,m)},k.visit=function(n){pr(n,k,v,d,y,m)},k.find=function(n){return gr(k,n[0],n[1],v,d,y,m)},p=-1,null==t){for(;++p<g;)u(k,n[p],s[p],h[p],v,d,y,m);--p}else n.forEach(k.add);return s=h=n=f=null,k}var o,a=Ce,l=ze;return(o=arguments.length)?(a=fr,l=sr,3===o&&(i=e,r=t,e=t=0),u(n)):(u.x=function(n){return arguments.length?(a=n,u):a},u.y=function(n){return arguments.length?(l=n,u):l},u.extent=function(n){return arguments.length?(null==n?t=e=r=i=null:(t=+n[0][0],e=+n[0][1],r=+n[1][0],i=+n[1][1]),u):null==t?null:[[t,e],[r,i]]},u.size=function(n){return arguments.length?(null==n?t=e=r=i=null:(t=e=0,r=+n[0],i=+n[1]),u):null==t?null:[r-t,i-e]},u)},ao.interpolateRgb=vr,ao.interpolateObject=dr,ao.interpolateNumber=yr,ao.interpolateString=mr;var hl=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,pl=new RegExp(hl.source,"g");ao.interpolate=Mr,ao.interpolators=[function(n,t){var e=typeof t;return("string"===e?ua.has(t.toLowerCase())||/^(#|rgb\(|hsl\()/i.test(t)?vr:mr:t instanceof an?vr:Array.isArray(t)?xr:"object"===e&&isNaN(t)?dr:yr)(n,t)}],ao.interpolateArray=xr;var gl=function(){return m},vl=ao.map({linear:gl,poly:Er,quad:function(){return Sr},cubic:function(){return kr},sin:function(){return Ar},exp:function(){return Cr},circle:function(){return zr},elastic:Lr,back:qr,bounce:function(){return Tr}}),dl=ao.map({"in":m,out:_r,"in-out":wr,"out-in":function(n){return wr(_r(n))}});ao.ease=function(n){var t=n.indexOf("-"),e=t>=0?n.slice(0,t):n,r=t>=0?n.slice(t+1):"in";return e=vl.get(e)||gl,r=dl.get(r)||m,br(r(e.apply(null,lo.call(arguments,1))))},ao.interpolateHcl=Rr,ao.interpolateHsl=Dr,ao.interpolateLab=Pr,ao.interpolateRound=Ur,ao.transform=function(n){var t=fo.createElementNS(ao.ns.prefix.svg,"g");return(ao.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new jr(e?e.matrix:yl)})(n)},jr.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var yl={a:1,b:0,c:0,d:1,e:0,f:0};ao.interpolateTransform=$r,ao.layout={},ao.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++e<r;)t.push(Jr(n[e]));return t}},ao.layout.chord=function(){function n(){var n,c,s,h,p,g={},v=[],d=ao.range(u),y=[];for(e=[],r=[],n=0,h=-1;++h<u;){for(c=0,p=-1;++p<u;)c+=i[h][p];v.push(c),y.push(ao.range(u)),n+=c}for(o&&d.sort(function(n,t){return o(v[n],v[t])}),a&&y.forEach(function(n,t){n.sort(function(n,e){return a(i[t][n],i[t][e])})}),n=(Ho-f*u)/n,c=0,h=-1;++h<u;){for(s=c,p=-1;++p<u;){var m=d[h],M=y[m][p],x=i[m][M],b=c,_=c+=x*n;g[m+"-"+M]={index:m,subindex:M,startAngle:b,endAngle:_,value:x}}r[m]={index:m,startAngle:s,endAngle:c,value:v[m]},c+=f}for(h=-1;++h<u;)for(p=h-1;++p<u;){var w=g[h+"-"+p],S=g[p+"-"+h];(w.value||S.value)&&e.push(w.value<S.value?{source:S,target:w}:{source:w,target:S})}l&&t()}function t(){e.sort(function(n,t){return l((n.source.value+n.target.value)/2,(t.source.value+t.target.value)/2)})}var e,r,i,u,o,a,l,c={},f=0;return c.matrix=function(n){return arguments.length?(u=(i=n)&&i.length,e=r=null,c):i},c.padding=function(n){return arguments.length?(f=n,e=r=null,c):f},c.sortGroups=function(n){return arguments.length?(o=n,e=r=null,c):o},c.sortSubgroups=function(n){return arguments.length?(a=n,e=null,c):a},c.sortChords=function(n){return arguments.length?(l=n,e&&t(),c):l},c.chords=function(){return e||n(),e},c.groups=function(){return r||n(),r},c},ao.layout.force=function(){function n(n){return function(t,e,r,i){if(t.point!==n){var u=t.cx-n.x,o=t.cy-n.y,a=i-e,l=u*u+o*o;if(l>a*a/y){if(v>l){var c=t.charge/l;n.px-=u*c,n.py-=o*c}return!0}if(t.point&&l&&v>l){var c=t.pointCharge/l;n.px-=u*c,n.py-=o*c}}return!t.charge}}function t(n){n.px=ao.event.x,n.py=ao.event.y,l.resume()}var e,r,i,u,o,a,l={},c=ao.dispatch("start","tick","end"),f=[1,1],s=.9,h=ml,p=Ml,g=-30,v=xl,d=.1,y=.64,M=[],x=[];return l.tick=function(){if((i*=.99)<.005)return e=null,c.end({type:"end",alpha:i=0}),!0;var t,r,l,h,p,v,y,m,b,_=M.length,w=x.length;for(r=0;w>r;++r)l=x[r],h=l.source,p=l.target,m=p.x-h.x,b=p.y-h.y,(v=m*m+b*b)&&(v=i*o[r]*((v=Math.sqrt(v))-u[r])/v,m*=v,b*=v,p.x-=m*(y=h.weight+p.weight?h.weight/(h.weight+p.weight):.5),p.y-=b*y,h.x+=m*(y=1-y),h.y+=b*y);if((y=i*d)&&(m=f[0]/2,b=f[1]/2,r=-1,y))for(;++r<_;)l=M[r],l.x+=(m-l.x)*y,l.y+=(b-l.y)*y;if(g)for(ri(t=ao.geom.quadtree(M),i,a),r=-1;++r<_;)(l=M[r]).fixed||t.visit(n(l));for(r=-1;++r<_;)l=M[r],l.fixed?(l.x=l.px,l.y=l.py):(l.x-=(l.px-(l.px=l.x))*s,l.y-=(l.py-(l.py=l.y))*s);c.tick({type:"tick",alpha:i})},l.nodes=function(n){return arguments.length?(M=n,l):M},l.links=function(n){return arguments.length?(x=n,l):x},l.size=function(n){return arguments.length?(f=n,l):f},l.linkDistance=function(n){return arguments.length?(h="function"==typeof n?n:+n,l):h},l.distance=l.linkDistance,l.linkStrength=function(n){return arguments.length?(p="function"==typeof n?n:+n,l):p},l.friction=function(n){return arguments.length?(s=+n,l):s},l.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,l):g},l.chargeDistance=function(n){return arguments.length?(v=n*n,l):Math.sqrt(v)},l.gravity=function(n){return arguments.length?(d=+n,l):d},l.theta=function(n){return arguments.length?(y=n*n,l):Math.sqrt(y)},l.alpha=function(n){return arguments.length?(n=+n,i?n>0?i=n:(e.c=null,e.t=NaN,e=null,c.end({type:"end",alpha:i=0})):n>0&&(c.start({type:"start",alpha:i=n}),e=qn(l.tick)),l):i},l.start=function(){function n(n,r){if(!e){for(e=new Array(i),l=0;i>l;++l)e[l]=[];for(l=0;c>l;++l){var u=x[l];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var o,a=e[t],l=-1,f=a.length;++l<f;)if(!isNaN(o=a[l][n]))return o;return Math.random()*r}var t,e,r,i=M.length,c=x.length,s=f[0],v=f[1];for(t=0;i>t;++t)(r=M[t]).index=t,r.weight=0;for(t=0;c>t;++t)r=x[t],"number"==typeof r.source&&(r.source=M[r.source]),"number"==typeof r.target&&(r.target=M[r.target]),++r.source.weight,++r.target.weight;for(t=0;i>t;++t)r=M[t],isNaN(r.x)&&(r.x=n("x",s)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof h)for(t=0;c>t;++t)u[t]=+h.call(this,x[t],t);else for(t=0;c>t;++t)u[t]=h;if(o=[],"function"==typeof p)for(t=0;c>t;++t)o[t]=+p.call(this,x[t],t);else for(t=0;c>t;++t)o[t]=p;if(a=[],"function"==typeof g)for(t=0;i>t;++t)a[t]=+g.call(this,M[t],t);else for(t=0;i>t;++t)a[t]=g;return l.resume()},l.resume=function(){return l.alpha(.1)},l.stop=function(){return l.alpha(0)},l.drag=function(){return r||(r=ao.behavior.drag().origin(m).on("dragstart.force",Qr).on("drag.force",t).on("dragend.force",ni)),arguments.length?void this.on("mouseover.force",ti).on("mouseout.force",ei).call(r):r},ao.rebind(l,c,"on")};var ml=20,Ml=1,xl=1/0;ao.layout.hierarchy=function(){function n(i){var u,o=[i],a=[];for(i.depth=0;null!=(u=o.pop());)if(a.push(u),(c=e.call(n,u,u.depth))&&(l=c.length)){for(var l,c,f;--l>=0;)o.push(f=c[l]),f.parent=u,f.depth=u.depth+1;r&&(u.value=0),u.children=c}else r&&(u.value=+r.call(n,u,u.depth)||0),delete u.children;return oi(i,function(n){var e,i;t&&(e=n.children)&&e.sort(t),r&&(i=n.parent)&&(i.value+=n.value)}),a}var t=ci,e=ai,r=li;return n.sort=function(e){return arguments.length?(t=e,n):t},n.children=function(t){return arguments.length?(e=t,n):e},n.value=function(t){return arguments.length?(r=t,n):r},n.revalue=function(t){return r&&(ui(t,function(n){n.children&&(n.value=0)}),oi(t,function(t){var e;t.children||(t.value=+r.call(n,t,t.depth)||0),(e=t.parent)&&(e.value+=t.value)})),t},n},ao.layout.partition=function(){function n(t,e,r,i){var u=t.children;if(t.x=e,t.y=t.depth*i,t.dx=r,t.dy=i,u&&(o=u.length)){var o,a,l,c=-1;for(r=t.value?r/t.value:0;++c<o;)n(a=u[c],e,l=a.value*r,i),e+=l}}function t(n){var e=n.children,r=0;if(e&&(i=e.length))for(var i,u=-1;++u<i;)r=Math.max(r,t(e[u]));return 1+r}function e(e,u){var o=r.call(this,e,u);return n(o[0],0,i[0],i[1]/t(o[0])),o}var r=ao.layout.hierarchy(),i=[1,1];return e.size=function(n){return arguments.length?(i=n,e):i},ii(e,r)},ao.layout.pie=function(){function n(o){var a,l=o.length,c=o.map(function(e,r){return+t.call(n,e,r)}),f=+("function"==typeof r?r.apply(this,arguments):r),s=("function"==typeof i?i.apply(this,arguments):i)-f,h=Math.min(Math.abs(s)/l,+("function"==typeof u?u.apply(this,arguments):u)),p=h*(0>s?-1:1),g=ao.sum(c),v=g?(s-l*p)/g:0,d=ao.range(l),y=[];return null!=e&&d.sort(e===bl?function(n,t){return c[t]-c[n]}:function(n,t){return e(o[n],o[t])}),d.forEach(function(n){y[n]={data:o[n],value:a=c[n],startAngle:f,endAngle:f+=a*v+p,padAngle:h}}),y}var t=Number,e=bl,r=0,i=Ho,u=0;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(i=t,n):i},n.padAngle=function(t){return arguments.length?(u=t,n):u},n};var bl={};ao.layout.stack=function(){function n(a,l){if(!(h=a.length))return a;var c=a.map(function(e,r){return t.call(n,e,r)}),f=c.map(function(t){return t.map(function(t,e){return[u.call(n,t,e),o.call(n,t,e)]})}),s=e.call(n,f,l);c=ao.permute(c,s),f=ao.permute(f,s);var h,p,g,v,d=r.call(n,f,l),y=c[0].length;for(g=0;y>g;++g)for(i.call(n,c[0][g],v=d[g],f[0][g][1]),p=1;h>p;++p)i.call(n,c[p][g],v+=f[p-1][g][1],f[p][g][1]);return a}var t=m,e=gi,r=vi,i=pi,u=si,o=hi;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:_l.get(t)||gi,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:wl.get(t)||vi,n):r},n.x=function(t){return arguments.length?(u=t,n):u},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(i=t,n):i},n};var _l=ao.map({"inside-out":function(n){var t,e,r=n.length,i=n.map(di),u=n.map(yi),o=ao.range(r).sort(function(n,t){return i[n]-i[t]}),a=0,l=0,c=[],f=[];for(t=0;r>t;++t)e=o[t],l>a?(a+=u[e],c.push(e)):(l+=u[e],f.push(e));return f.reverse().concat(c)},reverse:function(n){return ao.range(n.length).reverse()},"default":gi}),wl=ao.map({silhouette:function(n){var t,e,r,i=n.length,u=n[0].length,o=[],a=0,l=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;u>e;++e)l[e]=(a-o[e])/2;return l},wiggle:function(n){var t,e,r,i,u,o,a,l,c,f=n.length,s=n[0],h=s.length,p=[];for(p[0]=l=c=0,e=1;h>e;++e){for(t=0,i=0;f>t;++t)i+=n[t][e][1];for(t=0,u=0,a=s[e][0]-s[e-1][0];f>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;u+=o*n[t][e][1]}p[e]=l-=i?u/i*a:0,c>l&&(c=l)}for(e=0;h>e;++e)p[e]-=c;return p},expand:function(n){var t,e,r,i=n.length,u=n[0].length,o=1/i,a=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];if(r)for(t=0;i>t;t++)n[t][e][1]/=r;else for(t=0;i>t;t++)n[t][e][1]=o}for(e=0;u>e;++e)a[e]=0;return a},zero:vi});ao.layout.histogram=function(){function n(n,u){for(var o,a,l=[],c=n.map(e,this),f=r.call(this,c,u),s=i.call(this,f,c,u),u=-1,h=c.length,p=s.length-1,g=t?1:1/h;++u<p;)o=l[u]=[],o.dx=s[u+1]-(o.x=s[u]),o.y=0;if(p>0)for(u=-1;++u<h;)a=c[u],a>=f[0]&&a<=f[1]&&(o=l[ao.bisect(s,a,1,p)-1],o.y+=g,o.push(n[u]));return l}var t=!0,e=Number,r=bi,i=Mi;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=En(t),n):r},n.bins=function(t){return arguments.length?(i="number"==typeof t?function(n){return xi(n,t)}:En(t),n):i},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},ao.layout.pack=function(){function n(n,u){var o=e.call(this,n,u),a=o[0],l=i[0],c=i[1],f=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,oi(a,function(n){n.r=+f(n.value)}),oi(a,Ni),r){var s=r*(t?1:Math.max(2*a.r/l,2*a.r/c))/2;oi(a,function(n){n.r+=s}),oi(a,Ni),oi(a,function(n){n.r-=s})}return Ci(a,l/2,c/2,t?1:1/Math.max(2*a.r/l,2*a.r/c)),o}var t,e=ao.layout.hierarchy().sort(_i),r=0,i=[1,1];return n.size=function(t){return arguments.length?(i=t,n):i},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},ii(n,e)},ao.layout.tree=function(){function n(n,i){var f=o.call(this,n,i),s=f[0],h=t(s);if(oi(h,e),h.parent.m=-h.z,ui(h,r),c)ui(s,u);else{var p=s,g=s,v=s;ui(s,function(n){n.x<p.x&&(p=n),n.x>g.x&&(g=n),n.depth>v.depth&&(v=n)});var d=a(p,g)/2-p.x,y=l[0]/(g.x+a(g,p)/2+d),m=l[1]/(v.depth||1);ui(s,function(n){n.x=(n.x+d)*y,n.y=n.depth*m})}return f}function t(n){for(var t,e={A:null,children:[n]},r=[e];null!=(t=r.pop());)for(var i,u=t.children,o=0,a=u.length;a>o;++o)r.push((u[o]=i={_:u[o],parent:t,children:(i=u[o].children)&&i.slice()||[],A:null,a:null,z:0,m:0,c:0,s:0,t:null,i:o}).a=i);return e.children[0]}function e(n){var t=n.children,e=n.parent.children,r=n.i?e[n.i-1]:null;if(t.length){Di(n);var u=(t[0].z+t[t.length-1].z)/2;r?(n.z=r.z+a(n._,r._),n.m=n.z-u):n.z=u}else r&&(n.z=r.z+a(n._,r._));n.parent.A=i(n,r,n.parent.A||e[0])}function r(n){n._.x=n.z+n.parent.m,n.m+=n.parent.m}function i(n,t,e){if(t){for(var r,i=n,u=n,o=t,l=i.parent.children[0],c=i.m,f=u.m,s=o.m,h=l.m;o=Ti(o),i=qi(i),o&&i;)l=qi(l),u=Ti(u),u.a=n,r=o.z+s-i.z-c+a(o._,i._),r>0&&(Ri(Pi(o,n,e),n,r),c+=r,f+=r),s+=o.m,c+=i.m,h+=l.m,f+=u.m;o&&!Ti(u)&&(u.t=o,u.m+=s-f),i&&!qi(l)&&(l.t=i,l.m+=c-h,e=n)}return e}function u(n){n.x*=l[0],n.y=n.depth*l[1]}var o=ao.layout.hierarchy().sort(null).value(null),a=Li,l=[1,1],c=null;return n.separation=function(t){return arguments.length?(a=t,n):a},n.size=function(t){return arguments.length?(c=null==(l=t)?u:null,n):c?null:l},n.nodeSize=function(t){return arguments.length?(c=null==(l=t)?null:u,n):c?l:null},ii(n,o)},ao.layout.cluster=function(){function n(n,u){var o,a=t.call(this,n,u),l=a[0],c=0;oi(l,function(n){var t=n.children;t&&t.length?(n.x=ji(t),n.y=Ui(t)):(n.x=o?c+=e(n,o):0,n.y=0,o=n)});var f=Fi(l),s=Hi(l),h=f.x-e(f,s)/2,p=s.x+e(s,f)/2;return oi(l,i?function(n){n.x=(n.x-l.x)*r[0],n.y=(l.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(p-h)*r[0],n.y=(1-(l.y?n.y/l.y:1))*r[1]}),a}var t=ao.layout.hierarchy().sort(null).value(null),e=Li,r=[1,1],i=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(i=null==(r=t),n):i?null:r},n.nodeSize=function(t){return arguments.length?(i=null!=(r=t),n):i?r:null},ii(n,t)},ao.layout.treemap=function(){function n(n,t){for(var e,r,i=-1,u=n.length;++i<u;)r=(e=n[i]).value*(0>t?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var u=e.children;if(u&&u.length){var o,a,l,c=s(e),f=[],h=u.slice(),g=1/0,v="slice"===p?c.dx:"dice"===p?c.dy:"slice-dice"===p?1&e.depth?c.dy:c.dx:Math.min(c.dx,c.dy);for(n(h,c.dx*c.dy/e.value),f.area=0;(l=h.length)>0;)f.push(o=h[l-1]),f.area+=o.area,"squarify"!==p||(a=r(f,v))<=g?(h.pop(),g=a):(f.area-=f.pop().area,i(f,v,c,!1),v=Math.min(c.dx,c.dy),f.length=f.area=0,g=1/0);f.length&&(i(f,v,c,!0),f.length=f.area=0),u.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var u,o=s(t),a=r.slice(),l=[];for(n(a,o.dx*o.dy/t.value),l.area=0;u=a.pop();)l.push(u),l.area+=u.area,null!=u.z&&(i(l,u.z?o.dx:o.dy,o,!a.length),l.length=l.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,i=0,u=1/0,o=-1,a=n.length;++o<a;)(e=n[o].area)&&(u>e&&(u=e),e>i&&(i=e));return r*=r,t*=t,r?Math.max(t*i*g/r,r/(t*u*g)):1/0}function i(n,t,e,r){var i,u=-1,o=n.length,a=e.x,c=e.y,f=t?l(n.area/t):0;
			if(t==e.dx){for((r||f>e.dy)&&(f=e.dy);++u<o;)i=n[u],i.x=a,i.y=c,i.dy=f,a+=i.dx=Math.min(e.x+e.dx-a,f?l(i.area/f):0);i.z=!0,i.dx+=e.x+e.dx-a,e.y+=f,e.dy-=f}else{for((r||f>e.dx)&&(f=e.dx);++u<o;)i=n[u],i.x=a,i.y=c,i.dx=f,c+=i.dy=Math.min(e.y+e.dy-c,f?l(i.area/f):0);i.z=!1,i.dy+=e.y+e.dy-c,e.x+=f,e.dx-=f}}function u(r){var i=o||a(r),u=i[0];return u.x=u.y=0,u.value?(u.dx=c[0],u.dy=c[1]):u.dx=u.dy=0,o&&a.revalue(u),n([u],u.dx*u.dy/u.value),(o?e:t)(u),h&&(o=i),i}var o,a=ao.layout.hierarchy(),l=Math.round,c=[1,1],f=null,s=Oi,h=!1,p="squarify",g=.5*(1+Math.sqrt(5));return u.size=function(n){return arguments.length?(c=n,u):c},u.padding=function(n){function t(t){var e=n.call(u,t,t.depth);return null==e?Oi(t):Ii(t,"number"==typeof e?[e,e,e,e]:e)}function e(t){return Ii(t,n)}if(!arguments.length)return f;var r;return s=null==(f=n)?Oi:"function"==(r=typeof n)?t:"number"===r?(n=[n,n,n,n],e):e,u},u.round=function(n){return arguments.length?(l=n?Math.round:Number,u):l!=Number},u.sticky=function(n){return arguments.length?(h=n,o=null,u):h},u.ratio=function(n){return arguments.length?(g=n,u):g},u.mode=function(n){return arguments.length?(p=n+"",u):p},ii(u,a)},ao.random={normal:function(n,t){var e=arguments.length;return 2>e&&(t=1),1>e&&(n=0),function(){var e,r,i;do e=2*Math.random()-1,r=2*Math.random()-1,i=e*e+r*r;while(!i||i>1);return n+t*e*Math.sqrt(-2*Math.log(i)/i)}},logNormal:function(){var n=ao.random.normal.apply(ao,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=ao.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},ao.scale={};var Sl={floor:m,ceil:m};ao.scale.linear=function(){return Wi([0,1],[0,1],Mr,!1)};var kl={s:1,g:1,p:1,r:1,e:1};ao.scale.log=function(){return ru(ao.scale.linear().domain([0,1]),10,!0,[1,10])};var Nl=ao.format(".0e"),El={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};ao.scale.pow=function(){return iu(ao.scale.linear(),1,[0,1])},ao.scale.sqrt=function(){return ao.scale.pow().exponent(.5)},ao.scale.ordinal=function(){return ou([],{t:"range",a:[[]]})},ao.scale.category10=function(){return ao.scale.ordinal().range(Al)},ao.scale.category20=function(){return ao.scale.ordinal().range(Cl)},ao.scale.category20b=function(){return ao.scale.ordinal().range(zl)},ao.scale.category20c=function(){return ao.scale.ordinal().range(Ll)};var Al=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(xn),Cl=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(xn),zl=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(xn),Ll=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(xn);ao.scale.quantile=function(){return au([],[])},ao.scale.quantize=function(){return lu(0,1,[0,1])},ao.scale.threshold=function(){return cu([.5],[0,1])},ao.scale.identity=function(){return fu([0,1])},ao.svg={},ao.svg.arc=function(){function n(){var n=Math.max(0,+e.apply(this,arguments)),c=Math.max(0,+r.apply(this,arguments)),f=o.apply(this,arguments)-Io,s=a.apply(this,arguments)-Io,h=Math.abs(s-f),p=f>s?0:1;if(n>c&&(g=c,c=n,n=g),h>=Oo)return t(c,p)+(n?t(n,1-p):"")+"Z";var g,v,d,y,m,M,x,b,_,w,S,k,N=0,E=0,A=[];if((y=(+l.apply(this,arguments)||0)/2)&&(d=u===ql?Math.sqrt(n*n+c*c):+u.apply(this,arguments),p||(E*=-1),c&&(E=tn(d/c*Math.sin(y))),n&&(N=tn(d/n*Math.sin(y)))),c){m=c*Math.cos(f+E),M=c*Math.sin(f+E),x=c*Math.cos(s-E),b=c*Math.sin(s-E);var C=Math.abs(s-f-2*E)<=Fo?0:1;if(E&&yu(m,M,x,b)===p^C){var z=(f+s)/2;m=c*Math.cos(z),M=c*Math.sin(z),x=b=null}}else m=M=0;if(n){_=n*Math.cos(s-N),w=n*Math.sin(s-N),S=n*Math.cos(f+N),k=n*Math.sin(f+N);var L=Math.abs(f-s+2*N)<=Fo?0:1;if(N&&yu(_,w,S,k)===1-p^L){var q=(f+s)/2;_=n*Math.cos(q),w=n*Math.sin(q),S=k=null}}else _=w=0;if(h>Uo&&(g=Math.min(Math.abs(c-n)/2,+i.apply(this,arguments)))>.001){v=c>n^p?0:1;var T=g,R=g;if(Fo>h){var D=null==S?[_,w]:null==x?[m,M]:Re([m,M],[S,k],[x,b],[_,w]),P=m-D[0],U=M-D[1],j=x-D[0],F=b-D[1],H=1/Math.sin(Math.acos((P*j+U*F)/(Math.sqrt(P*P+U*U)*Math.sqrt(j*j+F*F)))/2),O=Math.sqrt(D[0]*D[0]+D[1]*D[1]);R=Math.min(g,(n-O)/(H-1)),T=Math.min(g,(c-O)/(H+1))}if(null!=x){var I=mu(null==S?[_,w]:[S,k],[m,M],c,T,p),Y=mu([x,b],[_,w],c,T,p);g===T?A.push("M",I[0],"A",T,",",T," 0 0,",v," ",I[1],"A",c,",",c," 0 ",1-p^yu(I[1][0],I[1][1],Y[1][0],Y[1][1]),",",p," ",Y[1],"A",T,",",T," 0 0,",v," ",Y[0]):A.push("M",I[0],"A",T,",",T," 0 1,",v," ",Y[0])}else A.push("M",m,",",M);if(null!=S){var Z=mu([m,M],[S,k],n,-R,p),V=mu([_,w],null==x?[m,M]:[x,b],n,-R,p);g===R?A.push("L",V[0],"A",R,",",R," 0 0,",v," ",V[1],"A",n,",",n," 0 ",p^yu(V[1][0],V[1][1],Z[1][0],Z[1][1]),",",1-p," ",Z[1],"A",R,",",R," 0 0,",v," ",Z[0]):A.push("L",V[0],"A",R,",",R," 0 0,",v," ",Z[0])}else A.push("L",_,",",w)}else A.push("M",m,",",M),null!=x&&A.push("A",c,",",c," 0 ",C,",",p," ",x,",",b),A.push("L",_,",",w),null!=S&&A.push("A",n,",",n," 0 ",L,",",1-p," ",S,",",k);return A.push("Z"),A.join("")}function t(n,t){return"M0,"+n+"A"+n+","+n+" 0 1,"+t+" 0,"+-n+"A"+n+","+n+" 0 1,"+t+" 0,"+n}var e=hu,r=pu,i=su,u=ql,o=gu,a=vu,l=du;return n.innerRadius=function(t){return arguments.length?(e=En(t),n):e},n.outerRadius=function(t){return arguments.length?(r=En(t),n):r},n.cornerRadius=function(t){return arguments.length?(i=En(t),n):i},n.padRadius=function(t){return arguments.length?(u=t==ql?ql:En(t),n):u},n.startAngle=function(t){return arguments.length?(o=En(t),n):o},n.endAngle=function(t){return arguments.length?(a=En(t),n):a},n.padAngle=function(t){return arguments.length?(l=En(t),n):l},n.centroid=function(){var n=(+e.apply(this,arguments)+ +r.apply(this,arguments))/2,t=(+o.apply(this,arguments)+ +a.apply(this,arguments))/2-Io;return[Math.cos(t)*n,Math.sin(t)*n]},n};var ql="auto";ao.svg.line=function(){return Mu(m)};var Tl=ao.map({linear:xu,"linear-closed":bu,step:_u,"step-before":wu,"step-after":Su,basis:zu,"basis-open":Lu,"basis-closed":qu,bundle:Tu,cardinal:Eu,"cardinal-open":ku,"cardinal-closed":Nu,monotone:Fu});Tl.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Rl=[0,2/3,1/3,0],Dl=[0,1/3,2/3,0],Pl=[0,1/6,2/3,1/6];ao.svg.line.radial=function(){var n=Mu(Hu);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},wu.reverse=Su,Su.reverse=wu,ao.svg.area=function(){return Ou(m)},ao.svg.area.radial=function(){var n=Ou(Hu);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},ao.svg.chord=function(){function n(n,a){var l=t(this,u,n,a),c=t(this,o,n,a);return"M"+l.p0+r(l.r,l.p1,l.a1-l.a0)+(e(l,c)?i(l.r,l.p1,l.r,l.p0):i(l.r,l.p1,c.r,c.p0)+r(c.r,c.p1,c.a1-c.a0)+i(c.r,c.p1,l.r,l.p0))+"Z"}function t(n,t,e,r){var i=t.call(n,e,r),u=a.call(n,i,r),o=l.call(n,i,r)-Io,f=c.call(n,i,r)-Io;return{r:u,a0:o,a1:f,p0:[u*Math.cos(o),u*Math.sin(o)],p1:[u*Math.cos(f),u*Math.sin(f)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Fo)+",1 "+t}function i(n,t,e,r){return"Q 0,0 "+r}var u=Me,o=xe,a=Iu,l=gu,c=vu;return n.radius=function(t){return arguments.length?(a=En(t),n):a},n.source=function(t){return arguments.length?(u=En(t),n):u},n.target=function(t){return arguments.length?(o=En(t),n):o},n.startAngle=function(t){return arguments.length?(l=En(t),n):l},n.endAngle=function(t){return arguments.length?(c=En(t),n):c},n},ao.svg.diagonal=function(){function n(n,i){var u=t.call(this,n,i),o=e.call(this,n,i),a=(u.y+o.y)/2,l=[u,{x:u.x,y:a},{x:o.x,y:a},o];return l=l.map(r),"M"+l[0]+"C"+l[1]+" "+l[2]+" "+l[3]}var t=Me,e=xe,r=Yu;return n.source=function(e){return arguments.length?(t=En(e),n):t},n.target=function(t){return arguments.length?(e=En(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},ao.svg.diagonal.radial=function(){var n=ao.svg.diagonal(),t=Yu,e=n.projection;return n.projection=function(n){return arguments.length?e(Zu(t=n)):t},n},ao.svg.symbol=function(){function n(n,r){return(Ul.get(t.call(this,n,r))||$u)(e.call(this,n,r))}var t=Xu,e=Vu;return n.type=function(e){return arguments.length?(t=En(e),n):t},n.size=function(t){return arguments.length?(e=En(t),n):e},n};var Ul=ao.map({circle:$u,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Fl)),e=t*Fl;return"M0,"+-t+"L"+e+",0 0,"+t+" "+-e+",0Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});ao.svg.symbolTypes=Ul.keys();var jl=Math.sqrt(3),Fl=Math.tan(30*Yo);Co.transition=function(n){for(var t,e,r=Hl||++Zl,i=Ku(n),u=[],o=Ol||{time:Date.now(),ease:Nr,delay:0,duration:250},a=-1,l=this.length;++a<l;){u.push(t=[]);for(var c=this[a],f=-1,s=c.length;++f<s;)(e=c[f])&&Qu(e,f,i,r,o),t.push(e)}return Wu(u,i,r)},Co.interrupt=function(n){return this.each(null==n?Il:Bu(Ku(n)))};var Hl,Ol,Il=Bu(Ku()),Yl=[],Zl=0;Yl.call=Co.call,Yl.empty=Co.empty,Yl.node=Co.node,Yl.size=Co.size,ao.transition=function(n,t){return n&&n.transition?Hl?n.transition(t):n:ao.selection().transition(n)},ao.transition.prototype=Yl,Yl.select=function(n){var t,e,r,i=this.id,u=this.namespace,o=[];n=A(n);for(var a=-1,l=this.length;++a<l;){o.push(t=[]);for(var c=this[a],f=-1,s=c.length;++f<s;)(r=c[f])&&(e=n.call(r,r.__data__,f,a))?("__data__"in r&&(e.__data__=r.__data__),Qu(e,f,u,i,r[u][i]),t.push(e)):t.push(null)}return Wu(o,u,i)},Yl.selectAll=function(n){var t,e,r,i,u,o=this.id,a=this.namespace,l=[];n=C(n);for(var c=-1,f=this.length;++c<f;)for(var s=this[c],h=-1,p=s.length;++h<p;)if(r=s[h]){u=r[a][o],e=n.call(r,r.__data__,h,c),l.push(t=[]);for(var g=-1,v=e.length;++g<v;)(i=e[g])&&Qu(i,g,a,o,u),t.push(i)}return Wu(l,a,o)},Yl.filter=function(n){var t,e,r,i=[];"function"!=typeof n&&(n=O(n));for(var u=0,o=this.length;o>u;u++){i.push(t=[]);for(var e=this[u],a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return Wu(i,this.namespace,this.id)},Yl.tween=function(n,t){var e=this.id,r=this.namespace;return arguments.length<2?this.node()[r][e].tween.get(n):Y(this,null==t?function(t){t[r][e].tween.remove(n)}:function(i){i[r][e].tween.set(n,t)})},Yl.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function i(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function u(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?$r:Mr,a=ao.ns.qualify(n);return Ju(this,"attr."+n,t,a.local?u:i)},Yl.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(i));return r&&function(n){this.setAttribute(i,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(i.space,i.local));return r&&function(n){this.setAttributeNS(i.space,i.local,r(n))}}var i=ao.ns.qualify(n);return this.tween("attr."+n,i.local?r:e)},Yl.style=function(n,e,r){function i(){this.style.removeProperty(n)}function u(e){return null==e?i:(e+="",function(){var i,u=t(this).getComputedStyle(this,null).getPropertyValue(n);return u!==e&&(i=Mr(u,e),function(t){this.style.setProperty(n,i(t),r)})})}var o=arguments.length;if(3>o){if("string"!=typeof n){2>o&&(e="");for(r in n)this.style(r,n[r],e);return this}r=""}return Ju(this,"style."+n,e,u)},Yl.styleTween=function(n,e,r){function i(i,u){var o=e.call(this,i,u,t(this).getComputedStyle(this,null).getPropertyValue(n));return o&&function(t){this.style.setProperty(n,o(t),r)}}return arguments.length<3&&(r=""),this.tween("style."+n,i)},Yl.text=function(n){return Ju(this,"text",n,Gu)},Yl.remove=function(){var n=this.namespace;return this.each("end.transition",function(){var t;this[n].count<2&&(t=this.parentNode)&&t.removeChild(this)})},Yl.ease=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].ease:("function"!=typeof n&&(n=ao.ease.apply(ao,arguments)),Y(this,function(r){r[e][t].ease=n}))},Yl.delay=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].delay:Y(this,"function"==typeof n?function(r,i,u){r[e][t].delay=+n.call(r,r.__data__,i,u)}:(n=+n,function(r){r[e][t].delay=n}))},Yl.duration=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].duration:Y(this,"function"==typeof n?function(r,i,u){r[e][t].duration=Math.max(1,n.call(r,r.__data__,i,u))}:(n=Math.max(1,n),function(r){r[e][t].duration=n}))},Yl.each=function(n,t){var e=this.id,r=this.namespace;if(arguments.length<2){var i=Ol,u=Hl;try{Hl=e,Y(this,function(t,i,u){Ol=t[r][e],n.call(t,t.__data__,i,u)})}finally{Ol=i,Hl=u}}else Y(this,function(i){var u=i[r][e];(u.event||(u.event=ao.dispatch("start","end","interrupt"))).on(n,t)});return this},Yl.transition=function(){for(var n,t,e,r,i=this.id,u=++Zl,o=this.namespace,a=[],l=0,c=this.length;c>l;l++){a.push(n=[]);for(var t=this[l],f=0,s=t.length;s>f;f++)(e=t[f])&&(r=e[o][i],Qu(e,f,o,u,{time:r.time,ease:r.ease,delay:r.delay+r.duration,duration:r.duration})),n.push(e)}return Wu(a,o,u)},ao.svg.axis=function(){function n(n){n.each(function(){var n,c=ao.select(this),f=this.__chart__||e,s=this.__chart__=e.copy(),h=null==l?s.ticks?s.ticks.apply(s,a):s.domain():l,p=null==t?s.tickFormat?s.tickFormat.apply(s,a):m:t,g=c.selectAll(".tick").data(h,s),v=g.enter().insert("g",".domain").attr("class","tick").style("opacity",Uo),d=ao.transition(g.exit()).style("opacity",Uo).remove(),y=ao.transition(g.order()).style("opacity",1),M=Math.max(i,0)+o,x=Zi(s),b=c.selectAll(".domain").data([0]),_=(b.enter().append("path").attr("class","domain"),ao.transition(b));v.append("line"),v.append("text");var w,S,k,N,E=v.select("line"),A=y.select("line"),C=g.select("text").text(p),z=v.select("text"),L=y.select("text"),q="top"===r||"left"===r?-1:1;if("bottom"===r||"top"===r?(n=no,w="x",k="y",S="x2",N="y2",C.attr("dy",0>q?"0em":".71em").style("text-anchor","middle"),_.attr("d","M"+x[0]+","+q*u+"V0H"+x[1]+"V"+q*u)):(n=to,w="y",k="x",S="y2",N="x2",C.attr("dy",".32em").style("text-anchor",0>q?"end":"start"),_.attr("d","M"+q*u+","+x[0]+"H0V"+x[1]+"H"+q*u)),E.attr(N,q*i),z.attr(k,q*M),A.attr(S,0).attr(N,q*i),L.attr(w,0).attr(k,q*M),s.rangeBand){var T=s,R=T.rangeBand()/2;f=s=function(n){return T(n)+R}}else f.rangeBand?f=s:d.call(n,s,f);v.call(n,f,s),y.call(n,s,s)})}var t,e=ao.scale.linear(),r=Vl,i=6,u=6,o=3,a=[10],l=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Xl?t+"":Vl,n):r},n.ticks=function(){return arguments.length?(a=co(arguments),n):a},n.tickValues=function(t){return arguments.length?(l=t,n):l},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(i=+t,u=+arguments[e-1],n):i},n.innerTickSize=function(t){return arguments.length?(i=+t,n):i},n.outerTickSize=function(t){return arguments.length?(u=+t,n):u},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Vl="bottom",Xl={top:1,right:1,bottom:1,left:1};ao.svg.brush=function(){function n(t){t.each(function(){var t=ao.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=t.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),t.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=t.selectAll(".resize").data(v,m);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return $l[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var l,s=ao.transition(t),h=ao.transition(o);c&&(l=Zi(c),h.attr("x",l[0]).attr("width",l[1]-l[0]),r(s)),f&&(l=Zi(f),h.attr("y",l[0]).attr("height",l[1]-l[0]),i(s)),e(s)})}function e(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+s[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function r(n){n.select(".extent").attr("x",s[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",s[1]-s[0])}function i(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function u(){function u(){32==ao.event.keyCode&&(C||(M=null,L[0]-=s[1],L[1]-=h[1],C=2),S())}function v(){32==ao.event.keyCode&&2==C&&(L[0]+=s[1],L[1]+=h[1],C=0,S())}function d(){var n=ao.mouse(b),t=!1;x&&(n[0]+=x[0],n[1]+=x[1]),C||(ao.event.altKey?(M||(M=[(s[0]+s[1])/2,(h[0]+h[1])/2]),L[0]=s[+(n[0]<M[0])],L[1]=h[+(n[1]<M[1])]):M=null),E&&y(n,c,0)&&(r(k),t=!0),A&&y(n,f,1)&&(i(k),t=!0),t&&(e(k),w({type:"brush",mode:C?"move":"resize"}))}function y(n,t,e){var r,i,u=Zi(t),l=u[0],c=u[1],f=L[e],v=e?h:s,d=v[1]-v[0];return C&&(l-=f,c-=d+f),r=(e?g:p)?Math.max(l,Math.min(c,n[e])):n[e],C?i=(r+=f)+d:(M&&(f=Math.max(l,Math.min(c,2*M[e]-r))),r>f?(i=r,r=f):i=f),v[0]!=r||v[1]!=i?(e?a=null:o=null,v[0]=r,v[1]=i,!0):void 0}function m(){d(),k.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),ao.select("body").style("cursor",null),q.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),z(),w({type:"brushend"})}var M,x,b=this,_=ao.select(ao.event.target),w=l.of(b,arguments),k=ao.select(b),N=_.datum(),E=!/^(n|s)$/.test(N)&&c,A=!/^(e|w)$/.test(N)&&f,C=_.classed("extent"),z=W(b),L=ao.mouse(b),q=ao.select(t(b)).on("keydown.brush",u).on("keyup.brush",v);if(ao.event.changedTouches?q.on("touchmove.brush",d).on("touchend.brush",m):q.on("mousemove.brush",d).on("mouseup.brush",m),k.interrupt().selectAll("*").interrupt(),C)L[0]=s[0]-L[0],L[1]=h[0]-L[1];else if(N){var T=+/w$/.test(N),R=+/^n/.test(N);x=[s[1-T]-L[0],h[1-R]-L[1]],L[0]=s[T],L[1]=h[R]}else ao.event.altKey&&(M=L.slice());k.style("pointer-events","none").selectAll(".resize").style("display",null),ao.select("body").style("cursor",_.style("cursor")),w({type:"brushstart"}),d()}var o,a,l=N(n,"brushstart","brush","brushend"),c=null,f=null,s=[0,0],h=[0,0],p=!0,g=!0,v=Bl[0];return n.event=function(n){n.each(function(){var n=l.of(this,arguments),t={x:s,y:h,i:o,j:a},e=this.__chart__||t;this.__chart__=t,Hl?ao.select(this).transition().each("start.brush",function(){o=e.i,a=e.j,s=e.x,h=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=xr(s,t.x),r=xr(h,t.y);return o=a=null,function(i){s=t.x=e(i),h=t.y=r(i),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){o=t.i,a=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,v=Bl[!c<<1|!f],n):c},n.y=function(t){return arguments.length?(f=t,v=Bl[!c<<1|!f],n):f},n.clamp=function(t){return arguments.length?(c&&f?(p=!!t[0],g=!!t[1]):c?p=!!t:f&&(g=!!t),n):c&&f?[p,g]:c?p:f?g:null},n.extent=function(t){var e,r,i,u,l;return arguments.length?(c&&(e=t[0],r=t[1],f&&(e=e[0],r=r[0]),o=[e,r],c.invert&&(e=c(e),r=c(r)),e>r&&(l=e,e=r,r=l),e==s[0]&&r==s[1]||(s=[e,r])),f&&(i=t[0],u=t[1],c&&(i=i[1],u=u[1]),a=[i,u],f.invert&&(i=f(i),u=f(u)),i>u&&(l=i,i=u,u=l),i==h[0]&&u==h[1]||(h=[i,u])),n):(c&&(o?(e=o[0],r=o[1]):(e=s[0],r=s[1],c.invert&&(e=c.invert(e),r=c.invert(r)),e>r&&(l=e,e=r,r=l))),f&&(a?(i=a[0],u=a[1]):(i=h[0],u=h[1],f.invert&&(i=f.invert(i),u=f.invert(u)),i>u&&(l=i,i=u,u=l))),c&&f?[[e,i],[r,u]]:c?[e,r]:f&&[i,u])},n.clear=function(){return n.empty()||(s=[0,0],h=[0,0],o=a=null),n},n.empty=function(){return!!c&&s[0]==s[1]||!!f&&h[0]==h[1]},ao.rebind(n,l,"on")};var $l={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Bl=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Wl=ga.format=xa.timeFormat,Jl=Wl.utc,Gl=Jl("%Y-%m-%dT%H:%M:%S.%LZ");Wl.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?eo:Gl,eo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},eo.toString=Gl.toString,ga.second=On(function(n){return new va(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),ga.seconds=ga.second.range,ga.seconds.utc=ga.second.utc.range,ga.minute=On(function(n){return new va(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),ga.minutes=ga.minute.range,ga.minutes.utc=ga.minute.utc.range,ga.hour=On(function(n){var t=n.getTimezoneOffset()/60;return new va(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),ga.hours=ga.hour.range,ga.hours.utc=ga.hour.utc.range,ga.month=On(function(n){return n=ga.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),ga.months=ga.month.range,ga.months.utc=ga.month.utc.range;var Kl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Ql=[[ga.second,1],[ga.second,5],[ga.second,15],[ga.second,30],[ga.minute,1],[ga.minute,5],[ga.minute,15],[ga.minute,30],[ga.hour,1],[ga.hour,3],[ga.hour,6],[ga.hour,12],[ga.day,1],[ga.day,2],[ga.week,1],[ga.month,1],[ga.month,3],[ga.year,1]],nc=Wl.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",zt]]),tc={range:function(n,t,e){return ao.range(Math.ceil(n/e)*e,+t,e).map(io)},floor:m,ceil:m};Ql.year=ga.year,ga.scale=function(){return ro(ao.scale.linear(),Ql,nc)};var ec=Ql.map(function(n){return[n[0].utc,n[1]]}),rc=Jl.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",zt]]);ec.year=ga.year.utc,ga.scale.utc=function(){return ro(ao.scale.linear(),ec,rc)},ao.text=An(function(n){return n.responseText}),ao.json=function(n,t){return Cn(n,"application/json",uo,t)},ao.html=function(n,t){return Cn(n,"text/html",oo,t)},ao.xml=An(function(n){return n.responseXML}),"function"==typeof define&&define.amd?(this.d3=ao,define(ao)):"object"==typeof module&&module.exports?module.exports=ao:this.d3=ao}();},{}],
			2:[function(require,module,exports){
			"use strict";

			var d3 = require('d3');


			//aigner: functions for drawing expand- and collapse-symbols
			function drawExpandSymbol(aCircle, line1, line2)
			{        
				 aCircle = aCircle
					.attr("r", 10)
					.style("stroke", "black")
					.style("stroke-width", 2)
					.style("fill", "#99CC00")
					.style("fill-opacity", .6)
				line1 = line1
					.attr("x1", parseFloat(aCircle.attr("cx"))-5)
					.attr("y1", parseFloat(aCircle.attr("cy")))
					.attr("x2", parseFloat(aCircle.attr("cx"))+5)
					.attr("y2", parseFloat(aCircle.attr("cy")))
					.style("stroke", "black")
					.style("stroke-width", 2);
				line2 = line2
					.attr("x1", parseFloat(aCircle.attr("cx")))
					.attr("y1", parseFloat(aCircle.attr("cy"))-5)
					.attr("x2", parseFloat(aCircle.attr("cx")))
					.attr("y2", parseFloat(aCircle.attr("cy"))+5)
					.style("stroke", "black")
					.style("stroke-width", 2);
			}
			function drawRemoveSymbol(anXdsm, aCircle, aMinus)
			{        
				 var xOffset=150;
				 var yOffset=12;
				 aCircle = aCircle
					.attr("cx", anXdsm.svg.attr("width")-xOffset)
					.attr("cy", yOffset)
					.attr("r", 10)
					.classed("remCircle",true)
				aMinus = aMinus
					.attr("x1", anXdsm.svg.attr("width")-xOffset+5)
					.attr("y1", yOffset)
					.attr("x2", anXdsm.svg.attr("width")-xOffset-5)
					.attr("y2", yOffset)
					.classed("remMinus",true)
			}
			//d3-context-menu for right-click-option
			d3.contextMenu = function (menu, openCallback) {

				// create the div element that will hold the context menu
				d3.selectAll('.d3-context-menu').data([1])
					.enter()
					.append('div')
					.attr('class', 'd3-context-menu');

				// close menu
				d3.select('body').on('click.d3-context-menu', function() {
					d3.select('.d3-context-menu').style('display', 'none');
				});

				// this gets executed when a contextmenu event occurs
				return function(data, index) {	
					var elm = this;

					d3.selectAll('.d3-context-menu').html('');
					var list = d3.selectAll('.d3-context-menu').append('ul');
						list.selectAll('li').data(menu).enter()
						.append('li')
						.html(function(d) {
							return d.title;
						})
						.on('mousedown', function(d, i) {
							d.onMouseDown(elm, data, index);
						})
						.on('mouseup', function(d, i) {
							d.onMouseUp(elm, data, index);
							d3.select('.d3-context-menu').style('display', 'none');
						})
						.on('mouseenter',function(d,i){
							d.onMouseOver(elm,data,index);
							if(d.childrenItems.length>0 )
								 {
                                  var li = this
								  d3.select(this).selectAll("ul").remove(); 
								  d3.select(this)
									.append("ul")
                                    .style("top",String(li.offsetTop-5)+"px")
									.selectAll("li")
									   .data(d.childrenItems)
										.enter().append("li")
										  .text(function(d) { return d.title; })
									 .on("mouseenter", function(d,i){
											d.onMouseOver(elm,data,index);
										})
									 .on('click',  function(d, i) {
											d.onMouseClick(elm, d, index);
										})
									 .on('mouseleave',function(d,i){
										
										});
								 }
							 else
								 return false;
						})
						.on('mouseleave',function(d,i){
							d3.select(this).selectAll("ul").style('display', 'none')                  
						});
					
					  

					// the openCallback allows an action to fire before the menu is displayed
					// an example usage would be closing a tooltip
					if (openCallback) openCallback(data, index);

					// display context menu
					d3.select('.d3-context-menu')
						.style('left', (d3.event.pageX - 2) + 'px')
						.style('top', (d3.event.pageY - 2) + 'px')
						.style('display', 'block');
                    
                    //Prevent the default event, which is the left-click. 
                    //This means, the context-menu will only appear on right mouse clicks
					d3.event.preventDefault();
                    
                    //Place context-menu always on top of everything esle
                    d3.select(".d3-context-menu").style("z-index",Number.MAX_SAFE_INTEGER);
				};
			};


			//aigner: Function creates a table
			function tabulate(aTable,data,columns) {
				var thead = aTable.append('thead');
				var	tbody = aTable.append('tbody');  

				// create a row for each object in the data
				var rows = tbody.selectAll('tr')
				  .data(data)
				  .enter()
				  .append('tr');

				// create a cell in each row for each column
				var cells = rows.selectAll('td')
				  .data(function (row) {
					return columns.map(function (column) {
					  return {column: column, value: row[column]};
					});
				  })
				  .enter()
				  .append('td')
					.html(function (d) { return d.value; });

				return aTable;
			}

			//aigner: Move to front function
			d3.selection.prototype.moveToFront = function() {  
			  return this.each(function(){
				this.parentNode.appendChild(this);
			  });
			};
			//aigner: Move to back function
			d3.selection.prototype.moveToBack = function() {  
				return this.each(function() { 
					var firstChild = this.parentNode.firstChild; 
					if (firstChild) { 
						this.parentNode.insertBefore(this, firstChild); 
					} 
				});
			};


			//aigner: biHisankey functions (Source: http://bl.ocks.org/Neilos/584b9a5d44d5fe00f779)
			d3.biHiSankey = function () {
			  "use strict";

			  var biHiSankey = {},
				nodeWidth = 24,
				nodeSpacing = 8,
				linkSpacing = 5,
				arrowheadScaleFactor = 0, // Specifies the proportion of a link's stroke width to be allowed for the marker at the end of the link.
				size = [1, 1], // default to one pixel by one pixel
				nodes = [],
				nodeMap = {},
				parentNodes = [],
				leafNodes = [],
				links = [],
				xScaleFactor = 1,
				yScaleFactor = 1,
				defaultLinkCurvature = 0.5;

			  function center(node) {
				return node.y + node.height / 2;
			  }

			  function value(link) {
				return link.value;
			  }

			  function initializeNodeArrayProperties(node) {
				node.sourceLinks = [];
				node.rightLinks = [];
				node.targetLinks = [];
				node.leftLinks = [];
				node.connectedNodes = [];
				node.children = [];
				node.ancestors = [];
			  }
			  // generates the nodeMap {"1": <node1>, "2": <node2>}
			  // and initializes the array properties of each node
			  function initializeNodeMap() {
				nodes.forEach(function (node) {
				  nodeMap[node.id] = node;
				  initializeNodeArrayProperties(node);
				});
			  }

			  function computeLeafNodes() {
				leafNodes = nodes.filter(function (node) {
				  return !node.children.length;
				});
			  }

			  function computeParentNodes() {
				parentNodes = nodes.filter(function (node) {
				  return node.children.length;
				});
			  }

			  function addAncestorsToChildren(node) {
				node.children.forEach(function (child) {
				  child.ancestors = child.ancestors.concat(this.ancestors.concat([this]));
				  addAncestorsToChildren(child);
				}, node);
			  }

			  // generate hierarchical connections between parent and child nodes
			  function computeNodeHierarchy() {
				var parent,
					rootNodes = [];

				nodes.forEach(function (node) {
				  parent = null;
				  if (parent) {
					node.parent = parent;
					parent.children.push(node);
				  } else {
					node.parent = null;
					rootNodes.push(node);
				  }
				});

				computeLeafNodes();
				computeParentNodes();

				rootNodes.forEach(function (rNode) {
				  addAncestorsToChildren(rNode);
				});
			  }

			  // Populate the sourceLinks and targetLinks for each node.
			  function computeNodeLinks() {
				var sourceNode, targetNode;
				links.forEach(function (link) {
				  sourceNode = nodeMap[link.source] || link.source;
				  targetNode = nodeMap[link.target] || link.target;
				  link.id = link.source + '-' + link.target;
				  link.source = sourceNode;
				  link.target = targetNode;
				  sourceNode.sourceLinks.push(link);
				  targetNode.targetLinks.push(link);
				});
			  }

			  function visible(linkCollection) {
				return linkCollection.filter(function (link) {
				  return link.source.state === "collapsed" && link.target.state === "collapsed";
				});
			  }

			  // When child nodes are collapsed into their parents (or higher ancestors)
			  // the links between the child nodes should be represented by links
			  // between the containing ancestors. This function adds those extra links.
			  function computeAncestorLinks() {
				// Leaf nodes are never parents of other nodes
				// Duplicate source and target links between a leaf node and another leaf node
				// and add to the leaf nodes' parents
				leafNodes.forEach(function (leafNode) {
				  leafNode.sourceLinks.forEach(function (sourceLink) {
					var ancestorTargets,
					target = sourceLink.target;
					if (leafNodes.indexOf(target) >= 0) {
					  ancestorTargets = target.ancestors.filter(function (tAncestor) {
						return leafNode.ancestors.indexOf(tAncestor) < 0;
					  });
					  ancestorTargets.forEach(function (ancestorTarget) {
						var ancestorLink = { source: leafNode,
											target: ancestorTarget,
											value: sourceLink.value,
											id: leafNode.id + "-" + ancestorTarget.id };

						leafNode.sourceLinks.push(ancestorLink);
						ancestorTarget.targetLinks.push(ancestorLink);
						links.push(ancestorLink);
					  });
					}
				  });

				  leafNode.targetLinks.forEach(function (targetLink) {
					var ancestorSources, source = targetLink.source;
					if (leafNodes.indexOf(source) >= 0) {
					  ancestorSources = source.ancestors.filter(function (sAncestor) {
						return leafNode.ancestors.indexOf(sAncestor) < 0;
					  });
					  ancestorSources.forEach(function (ancestorSource) {
						var ancestorLink = { source: ancestorSource,
											target: leafNode,
											value: targetLink.value,
											id: ancestorSource.id + "-" + leafNode.id };
						ancestorSource.sourceLinks.push(ancestorLink);
						leafNode.targetLinks.push(ancestorLink);
						links.push(ancestorLink);
					  });
					}
				  });
				});

				// Add links between parents (for when both parents are in collapsed state)
				parentNodes.forEach(function (parentNode) {
				  parentNode.sourceLinks.forEach(function (sourceLink) {
					var ancestorTargets, target = sourceLink.target;
					if (leafNodes.indexOf(target) >= 0) {
					  ancestorTargets = target.ancestors.filter(function (tAncestor) {
						return parentNode.ancestors.indexOf(tAncestor) < 0;
					  });
					  ancestorTargets.forEach(function (ancestorTarget) {
						var ancestorLink = { source: parentNode,
											target: ancestorTarget,
											value: sourceLink.value,
											id: parentNode.id + "-" + ancestorTarget.id };

						parentNode.sourceLinks.push(ancestorLink);
						ancestorTarget.targetLinks.push(ancestorLink);
						links.push(ancestorLink);
					  });
					}
				  });
				});
			  }

			  // To reduce clutter in the diagram merge links that are from the
			  // same source to the same target by creating a new link
			  // with a value equal to the sum of the values of the merged links
			  function mergeLinks() {
				var linkGroups = d3.nest()
				  .key(function (link) { return link.source.id + "->" + link.target.id; })
				  .entries(links)
				  .map(function (object) { return object.values; });

				links = linkGroups.map(function (linkGroup) {
				  return linkGroup.reduce(function (previousLink, currentLink) {
					return {
					  "source": previousLink.source,
					  "target": previousLink.target,
					  "id": d3.min([previousLink.id, currentLink.id]),
					  "value": previousLink.value + currentLink.value
					};
				  });
				});
			  }

			  function nodeHeight(sideLinks) {
				var spacing = Math.max(sideLinks.length - 1, 0) * linkSpacing,
					scaledValueSum = d3.sum(sideLinks, value) * yScaleFactor;
				return scaledValueSum + spacing;
			  }

			  // Compute the value of each node by summing the associated links.
			  // Compute the number of spaces between the links
			  // Compute the number of source links for later decrementing
			  function computeNodeValues() {
				nodes.forEach(function (node) {
				  node.value = Math.max(
					d3.sum(node.leftLinks, value),
					d3.sum(node.rightLinks, value)
				  );
				  node.inputs = d3.sum(visible(node.targetLinks), value);
				  node.outputs = d3.sum(visible(node.sourceLinks), value);
				  node.height = Math.max(nodeHeight(visible(node.leftLinks)), nodeHeight(visible(node.rightLinks)));
				  node.linkSpaceCount = Math.max(Math.max(node.leftLinks.length, node.rightLinks.length) - 1, 0);
				});
			  }

			  function computeConnectedNodes() {
				var sourceNode, targetNode;
				links.forEach(function (link) {
				  sourceNode = link.source;
				  targetNode = link.target;
				  if (sourceNode.connectedNodes.indexOf(targetNode) < 0) {
					sourceNode.connectedNodes.push(targetNode);
				  }
				  if (targetNode.connectedNodes.indexOf(sourceNode) < 0) {
					targetNode.connectedNodes.push(sourceNode);
				  }
				});
			  }

			  function sourceAndTargetNodesWithSameX() {
				var nodeArray = [];
				links.filter(function (link) {
				  return link.target.x === link.source.x;
				}).forEach(function (link) {
				  if (nodeArray.indexOf(link.target) < 0) {
					nodeArray.push(link.target);
				  }
				});
				return nodeArray;
			  }

			  function compressInXDirection() {
				var connectedNodesXPositions,
					nodesByXPosition = d3.nest()
					  .key(function (node) { return node.x; })
					  .sortKeys(d3.ascending)
					  .entries(nodes)
					  .map(function (object) { return object.values; });

				nodesByXPosition.forEach(function (xnodes) {
				  xnodes.forEach(function (node) {
					connectedNodesXPositions = node.connectedNodes.map(function (connectedNode) {
					  return connectedNode.x;
					});
					// keep decrementing the x value of the node
					// unless it would have the same x value as one of its source or target nodes
					// or node.x is already 0
					while (node.x > 0 && connectedNodesXPositions.indexOf(node.x - 1) < 0) {
					  node.x -= 1;
					}
				  });
				});
			  }

			  function scaleNodeXPositions() {
				var minX = d3.min(nodes, function (node) { return node.x; }),
					maxX = d3.max(nodes, function (node) { return node.x; }) - minX;
				xScaleFactor = (size[0] - nodeWidth) / maxX;

				nodes.forEach(function (node) {
				  node.x *= xScaleFactor;
				});
			  }

			  function computeNodeXPositions() {
				var remainingNodes = nodes,
					nextNodes,
					x = 0,
					addToNextNodes = function (link) {
					  if (nextNodes.indexOf(link.target) < 0 && link.target.x === this.x) {
						nextNodes.push(link.target);
					  }
					},
					setValues = function (node) {
					  node.x = x;
					  node.width = nodeWidth;
					  node.sourceLinks.forEach(addToNextNodes, node);
					};

				while (remainingNodes.length) {
				  nextNodes = [];
				  remainingNodes.forEach(setValues);
				  if (nextNodes.length) {
					remainingNodes = nextNodes;
				  } else {
					remainingNodes = sourceAndTargetNodesWithSameX();
				  }
				  x += 1;
				}

				compressInXDirection();
				scaleNodeXPositions();
			  }

			  function computeLeftAndRightLinks() {
				var source, target;
				nodes.forEach(function (node) {
				  node.rightLinks = [];
				  node.leftLinks = [];
				});
				links.forEach(function (link) {
				  source = link.source;
				  target = link.target;
				  if (source.x < target.x) {
					source.rightLinks.push(link);
					target.leftLinks.push(link);
					link.direction = 1;
				  } else {
					source.leftLinks.push(link);
					target.rightLinks.push(link);
					link.direction = -1;
				  }
				});
			  }

			  function adjustTop(adjustment) {
				nodes.forEach(function (node) {
				  node.y -= adjustment;
				});
			  }

			  function computeNodeYPositions(iterations) {
				var minY,
					alpha,
					nodesByXPosition = d3.nest()
					  .key(function (node) { return node.x; })
					  .sortKeys(d3.ascending)
					  .entries(nodes)
					  .map(function (object) { return object.values; });

				function calculateYScaleFactor() {
				  var linkSpacesCount, nodeValueSum, discretionaryY;
				  yScaleFactor = d3.min(nodesByXPosition, function (nodes) {
					linkSpacesCount = d3.sum(nodes, function (node) {
					  return node.linkSpaceCount;
					});
					nodeValueSum = d3.sum(nodes, function (node) {
					  return node.value;
					});
					discretionaryY = (size[1]
									- (nodes.length - 1) * nodeSpacing
									- linkSpacesCount * linkSpacing);

					return  discretionaryY / nodeValueSum;
				  });

				  // Fat links are those with lengths less than about 4 times their heights
				  // Fat links don't bend well
				  // Test that yScaleFactor is not so big that it causes "fat" links; adjust yScaleFactor accordingly
				  links.forEach(function (link) {
					var linkLength = Math.abs(link.source.x - link.target.x),
						linkHeight = link.value * yScaleFactor;
					if (linkLength / linkHeight < 4) {
					  yScaleFactor = 0.25 * linkLength / link.value;
					}
				  });
				}

				function initializeNodeYPosition() {
				  nodesByXPosition.forEach(function (nodes) {
					nodes.forEach(function (node, i) {
					  node.y = i;
					  node.heightAllowance = node.value * yScaleFactor + linkSpacing * node.linkSpaceCount;
					});
				  });
				}

				function calculateLinkThickness() {
				  links.forEach(function (link) {
					link.thickness = link.value * yScaleFactor;
				  });
				}

				function relaxLeftToRight(alpha) {
				  function weightedSource(link) {
					return center(link.source) * link.value;
				  }

				  nodesByXPosition.forEach(function (nodes) {
					nodes.forEach(function (node) {
					  if (node.rightLinks.length) {
						var y = d3.sum(node.rightLinks, weightedSource) / d3.sum(node.rightLinks, value);
						node.y += (y - center(node)) * alpha;
					  }
					});
				  });
				}

				function relaxRightToLeft(alpha) {
				  function weightedTarget(link) {
					return center(link.target) * link.value;
				  }

				  nodesByXPosition.slice().reverse().forEach(function (nodes) {
					nodes.forEach(function (node) {
					  if (node.leftLinks.length) {
						var y = d3.sum(node.leftLinks, weightedTarget) / d3.sum(node.leftLinks, value);
						node.y += (y - center(node)) * alpha;
					  }
					});
				  });
				}

				function resolveCollisions() {
				  function ascendingYPosition(a, b) {
					return a.y - b.y;
				  }

				  nodesByXPosition.forEach(function (nodes) {
					var node,
						dy,
						y0 = 0,
						n = nodes.length,
						i;

					nodes.sort(ascendingYPosition);

					// Push any overlapping nodes down.
					for (i = 0; i < n; ++i) {
					  node = nodes[i];
					  dy = y0 - node.y;
					  if (dy > 0) {
						node.y += dy;
					  }
					  y0 = node.y + node.heightAllowance + nodeSpacing;
					}

					// If the bottommost node goes outside the bounds, push it back up.
					dy = y0 - nodeSpacing - size[1];
					if (dy > 0) {
					  node.y -= dy;
					  y0 = node.y;

					  // Push any overlapping nodes back up.
					  for (i = n - 2; i >= 0; --i) {
						node = nodes[i];
						dy = node.y + node.heightAllowance + nodeSpacing - y0;
						if (dy > 0) {
						  node.y -= dy;
						}
						y0 = node.y;
					  }
					}
				  });
				}

				calculateYScaleFactor();
				initializeNodeYPosition();
				calculateLinkThickness();
				resolveCollisions();

				for (alpha = 1; iterations > 0; --iterations) {
				  alpha *= 0.99;
				  relaxRightToLeft(alpha);
				  resolveCollisions();
				  relaxLeftToRight(alpha);
				  resolveCollisions();
				}

				minY = d3.min(nodes, function (node) { return node.y; });
				adjustTop(minY);
			  }

			  function computeLinkYPositions() {

				function ascendingLeftNodeYPosition(a, b) {
				  var aLeftNode = (a.direction > 0) ? a.source : a.target,
					  bLeftNode = (b.direction > 0) ? b.source : b.target;
				  return aLeftNode.y - bLeftNode.y;
				}

				function ascendingRightNodeYPosition(a, b) {
				  var aRightNode = (a.direction > 0) ? a.target : a.source,
					  bRightNode = (b.direction > 0) ? b.target : b.source;
				  return aRightNode.y - bRightNode.y;
				}

				nodes.forEach(function (node) {
				  node.rightLinks.sort(ascendingRightNodeYPosition);
				  node.leftLinks.sort(ascendingLeftNodeYPosition);
				});

				nodes.forEach(function (node) {
				  var rightY = 0, leftY = 0;

				  node.rightLinks.forEach(function (link) {
					if (link.direction > 0) {
					  link.sourceY = rightY;
					  if (link.target.state === "collapsed") {
						rightY += link.thickness + linkSpacing;
					  }
					}
					else {
					  link.targetY = rightY;
					  if (link.source.state === "collapsed") {
						rightY += link.thickness + linkSpacing;
					  }
					}
				  });

				  node.leftLinks.forEach(function (link) {
					if (link.direction < 0) {
					  link.sourceY = leftY;
					  if (link.target.state === "collapsed") {
						leftY += link.thickness + linkSpacing;
					  }
					}
					else {
					  link.targetY = leftY;
					  if (link.source.state === "collapsed") {
						leftY += link.thickness + linkSpacing;
					  }
					}
				  });

				});
			  }


			  biHiSankey.arrowheadScaleFactor = function (_) {
				if (!arguments.length) { return arrowheadScaleFactor; }
				arrowheadScaleFactor = +_;
				return biHiSankey;
			  };

			  biHiSankey.collapsedNodes = function () {
				return nodes.filter(function (node) { return node.state === "collapsed"; });
			  };

			  biHiSankey.connected = function (nodeA, nodeB) {
				return nodeA.connectedNodes.indexOf(nodeB) >= 0;
			  };

			  biHiSankey.expandedNodes = function () {
				return nodes.filter(function (node) { return node.state === "expanded"; });
			  };

			  biHiSankey.layout = function (iterations) {
				computeNodeXPositions();
				computeLeftAndRightLinks();
				computeNodeValues();
				computeNodeYPositions(iterations);
				computeNodeValues();
				computeLinkYPositions();
				return biHiSankey;
			  };

			  biHiSankey.link = function () {
				var curvature = defaultLinkCurvature;

				function leftToRightLink(link) {
				  var arrowHeadLength = link.thickness * arrowheadScaleFactor,
					  straightSectionLength = (3 * link.thickness / 4) - arrowHeadLength,
					  x0 = link.source.x + link.source.width,
					  x1 = x0 + arrowHeadLength / 2,
					  x4 = link.target.x - straightSectionLength - arrowHeadLength,
					  xi = d3.interpolateNumber(x0, x4),
					  x2 = xi(curvature),
					  x3 = xi(1 - curvature),
					  y0 = link.source.y + link.sourceY + link.thickness / 2,
					  y1 = link.target.y + link.targetY + link.thickness / 2;
				  return "M" + x0 + "," + y0
					   + "L" + x1 + "," + y0
					   + "C" + x2 + "," + y0
					   + " " + x3 + "," + y1
					   + " " + x4 + "," + y1
					   + "L" + (x4 + straightSectionLength) + "," + y1;
				}

				function rightToLeftLink(link) {
				  var arrowHeadLength = link.thickness * arrowheadScaleFactor,
					  straightSectionLength = link.thickness / 4,
					  x0 = link.source.x,
					  x1 = x0 - arrowHeadLength / 2,
					  x4 = link.target.x + link.target.width + straightSectionLength + arrowHeadLength,
					  xi = d3.interpolateNumber(x0, x4),
					  x2 = xi(curvature),
					  x3 = xi(1 - curvature),
					  y0 = link.source.y + link.sourceY + link.thickness / 2,
					  y1 = link.target.y + link.targetY + link.thickness / 2;
				  return "M" + x0 + "," + y0
					   + "L" + x1 + "," + y0
					   + "C" + x2 + "," + y0
					   + " " + x3 + "," + y1
					   + " " + x4 + "," + y1
					   + "L" + (x4 - straightSectionLength) + "," + y1;
				}

				function link(d) {
				  if (d.source.x < d.target.x) {
					return leftToRightLink(d);
				  }
				  return rightToLeftLink(d);
				}

				link.curvature = function (_) {
				  if (!arguments.length) { return curvature; }
				  curvature = +_;
				  return link;
				};

				return link;
			  };

			  biHiSankey.links = function (_) {
				if (!arguments.length) { return links; }
				links = _.filter(function (link) {
				  return link.source !== link.target; // filter out links that go nowhere
				});
				return biHiSankey;
			  };

			  biHiSankey.linkSpacing = function (_) {
				if (!arguments.length) { return linkSpacing; }
				linkSpacing = +_;
				return biHiSankey;
			  };

			  biHiSankey.nodes = function (_) {
				if (!arguments.length) { return nodes; }
				nodes = _;
				return biHiSankey;
			  };

			  biHiSankey.nodeWidth = function (_) {
				if (!arguments.length) { return nodeWidth; }
				nodeWidth = +_;
				return biHiSankey;
			  };

			  biHiSankey.nodeSpacing = function (_) {
				if (!arguments.length) { return nodeSpacing; }
				nodeSpacing = +_;
				return biHiSankey;
			  };

			  biHiSankey.relayout = function () {
				computeLeftAndRightLinks();
				computeNodeValues();
				computeLinkYPositions();
				return biHiSankey;
			  };

			  biHiSankey.size = function (_) {
				if (!arguments.length) { return size; }
				size = _;
				return biHiSankey;
			  };

			  biHiSankey.visibleLinks = function () {
				return visible(links);
			  };

			  biHiSankey.initializeNodes = function (callback) {
				initializeNodeMap();
				computeNodeHierarchy();
				computeNodeLinks();
				computeAncestorLinks();
				mergeLinks();
				computeConnectedNodes();
				nodes.forEach(callback);
				return biHiSankey;
			  };

			  return biHiSankey;
			};


			'use strict';

			var sankeyDiagramDiv, svg, tooltip, biHiSankey, path, defs, colorScale, isTransitioning;

			var OPACITY = {
				NODE_DEFAULT: 0.9,
				NODE_FADED: 0.1,
				NODE_HIGHLIGHT: 0.8,
				LINK_DEFAULT: 0.6,
				LINK_FADED: 0.05,
				LINK_HIGHLIGHT: 0.9
			  },
			  LINK_COLOR = "#b3b3b3",
			  INFLOW_COLOR = "#CC0000",
			  OUTFLOW_COLOR = "#99CC00",
			  NODE_WIDTH = 36,
			  COLLAPSER = {
				RADIUS: NODE_WIDTH / 2,
				SPACING: 2
			  },
			  OUTER_MARGIN = 10,
			  MARGIN = {
				TOP: 2 * (COLLAPSER.RADIUS + OUTER_MARGIN),
				RIGHT: OUTER_MARGIN,
				BOTTOM: OUTER_MARGIN,
				LEFT: OUTER_MARGIN
			  },
			  TRANSITION_DURATION = 400,
			  HEIGHT = 2000 - MARGIN.TOP - MARGIN.BOTTOM,
			  WIDTH = 960 - MARGIN.LEFT - MARGIN.RIGHT,
			  LAYOUT_INTERATIONS = 32,
			  REFRESH_INTERVAL = 7000;

			var formatNumber = function (d) {
			  var numberFormat = d3.format(",.0f"); // zero decimal places
			  return  numberFormat(d) + " connections";
			},

			formatFlow = function (d) {
			  var flowFormat = d3.format(",.0f"); // zero decimal places with sign
			  return flowFormat(Math.abs(d));
			},

			// Used when temporarily disabling user interractions to allow animations to complete
			disableUserInterractions = function (time) {
			  isTransitioning = true;
			  setTimeout(function(){
				isTransitioning = false;
			  }, time);
			},

			hideTooltip = function () {
			  return tooltip.transition()
				.duration(TRANSITION_DURATION)
				.style("opacity", 0);
			},

			showTooltip = function () {
			  return tooltip
				.style("left", d3.event.pageX + "px")
				.style("top", d3.event.pageY + 15 + "px")
				.transition()
				  .duration(TRANSITION_DURATION)
				  .style("opacity", 1);
			};

			colorScale = d3.scale.category10(),

			sankeyDiagramDiv =  d3.select("body").append("div").attr("class","sankeyDiagramDiv")
			svg = sankeyDiagramDiv.append("svg")
					.attr("width", WIDTH + MARGIN.LEFT + MARGIN.RIGHT)
					.attr("height", HEIGHT + MARGIN.TOP + MARGIN.BOTTOM)
				  .append("g")
					.attr("transform", "translate(" + MARGIN.LEFT + "," + MARGIN.TOP + ")");

			svg.append("g").attr("id", "links");
			svg.append("g").attr("id", "nodes");
			svg.append("g").attr("id", "collapsers");

			tooltip = d3.select(".sankeyDiagramDiv").append("div").attr("id", "tooltip");

			tooltip.style("opacity", 0)
				.append("p")
				  .attr("class", "value");

			biHiSankey = d3.biHiSankey();

			// Set the biHiSankey diagram properties
			biHiSankey
			  .nodeWidth(NODE_WIDTH)
			  .nodeSpacing(10)
			  .linkSpacing(4)
			  .arrowheadScaleFactor(0.5) // Specifies that 0.5 of the link's stroke WIDTH should be allowed for the marker at the end of the link.
			  .size([WIDTH, HEIGHT]);

			path = biHiSankey.link().curvature(0.45);

			defs = svg.append("defs");

			defs.append("marker")
			  .style("fill", LINK_COLOR)
			  .attr("id", "arrowHead")
			  .attr("viewBox", "0 0 6 10")
			  .attr("refX", "1")
			  .attr("refY", "5")
			  .attr("markerUnits", "strokeWidth")
			  .attr("markerWidth", "1")
			  .attr("markerHeight", "1")
			  .attr("orient", "auto")
			  .append("path")
				.attr("d", "M 0 0 L 1 0 L 6 5 L 1 10 L 0 10 z");

			defs.append("marker")
			  .style("fill", OUTFLOW_COLOR)
			  .attr("id", "arrowHeadInflow")
			  .attr("viewBox", "0 0 6 10")
			  .attr("refX", "1")
			  .attr("refY", "5")
			  .attr("markerUnits", "strokeWidth")
			  .attr("markerWidth", "1")
			  .attr("markerHeight", "1")
			  .attr("orient", "auto")
			  .append("path")
				.attr("d", "M 0 0 L 1 0 L 6 5 L 1 10 L 0 10 z");

			defs.append("marker")
			  .style("fill", INFLOW_COLOR)
			  .attr("id", "arrowHeadOutlow")
			  .attr("viewBox", "0 0 6 10")
			  .attr("refX", "1")
			  .attr("refY", "5")
			  .attr("markerUnits", "strokeWidth")
			  .attr("markerWidth", "1")
			  .attr("markerHeight", "1")
			  .attr("orient", "auto")
			  .append("path")
				.attr("d", "M 0 0 L 1 0 L 6 5 L 1 10 L 0 10 z");

			function update() {
			  var link, linkEnter, node, nodeEnter, collapser, collapserEnter;

			  function dragmove(node) {
				node.x = Math.max(0, Math.min(WIDTH - node.width, d3.event.x));
				node.y = Math.max(0, Math.min(HEIGHT - node.height, d3.event.y));
				d3.select(this)
					.attr("transform", "translate(" + node.x + "," + node.y + ")")
					.style("cursor", "grabbing")
				biHiSankey.relayout();
				svg.selectAll(".sankeyNode").selectAll("rect").attr("height", function (d) { return d.height; });
				link.attr("d", path);
			  }

			  function containChildren(node) {
				node.children.forEach(function (child) {
				  child.state = "contained";
				  child.parent = this;
				  child._parent = null;
				  containChildren(child);
				}, node);
			  }

			  function expand(node) {
				node.state = "expanded";
				node.children.forEach(function (child) {
				  child.state = "collapsed";
				  child._parent = this;
				  child.parent = null;
				  containChildren(child);
				}, node);
			  }

			  function collapse(node) {
				node.state = "collapsed";
				containChildren(node);
			  }

			  function restoreLinksAndNodes() {
				link
				  .style("stroke", LINK_COLOR)
				  .style("marker-end", function () { return 'url(#arrowHead)'; })
				  .transition()
					.duration(TRANSITION_DURATION)
					.style("opacity", OPACITY.LINK_DEFAULT);
				

				node
				  .selectAll("rect")
					.style("fill", function (d) {
					  d.color = colorScale(d.name.replace(/ .*/, ""));
					  return d.color;
					})
					.style("stroke", function (d) {
					  return d3.rgb(colorScale(d.name.replace(/ .*/, ""))).darker(0.1);
					})
					.style("fill-opacity", OPACITY.NODE_DEFAULT);

				node.filter(function (n) { return n.state === "collapsed"; })
				  .transition()
					.duration(TRANSITION_DURATION)
					.style("opacity", OPACITY.NODE_DEFAULT);
			  }

			  function showHideChildren(node) {
				disableUserInterractions(2 * TRANSITION_DURATION);
				hideTooltip();
				if (node.state === "collapsed") { expand(node); }
				else { collapse(node); }

				biHiSankey.relayout();
				update();
				link.attr("d", path);
				restoreLinksAndNodes();
			  }

			  function highlightConnected(g) {
				link.filter(function (d) { return d.source === g; })
				  .style("marker-end", function () { return 'url(#arrowHeadInflow)'; })
				  .style("stroke", OUTFLOW_COLOR)
				  .style("opacity", OPACITY.LINK_DEFAULT);

				link.filter(function (d) { return d.target === g; })
				  .style("marker-end", function () { return 'url(#arrowHeadOutlow)'; })
				  .style("stroke", INFLOW_COLOR)
				  .style("opacity", OPACITY.LINK_DEFAULT);
				  
				link.forEach(function (aLink) {
				  aLink.moveToFront;
				});
			  }

			  function fadeUnconnected(g) {
				link.filter(function (d) { return d.source !== g && d.target !== g; })
				  .style("marker-end", function () { return 'url(#arrowHead)'; })
				  .transition()
					.duration(TRANSITION_DURATION)
					.style("opacity", OPACITY.LINK_FADED);

				node.filter(function (d) {
				  return (d.name === g.name) ? false : !biHiSankey.connected(d, g);
				}).transition()
				  .duration(TRANSITION_DURATION)
				  .style("opacity", OPACITY.NODE_FADED);
			  }

			  link = svg.select("#links").selectAll("path.link")
				.data(biHiSankey.visibleLinks(), function (d) { return d.id; });

			  link.transition()
				.duration(TRANSITION_DURATION)
				.style("stroke-WIDTH", function (d) { return Math.max(1, d.thickness); })
				.attr("d", path)
				.style("opacity", OPACITY.LINK_DEFAULT);


			  link.exit().remove();


			  linkEnter = link.enter().append("path")
				.attr("class", "link")
				.style("fill", "none");

			  linkEnter.on('mouseenter', function (d) {
				if (!isTransitioning) {
				  showTooltip().select(".value").text(function () {
					if (d.direction > 0) {
					  return d.source.name + " → " + d.target.name + "\n" + formatNumber(d.value);
					}
					return d.target.name + " ← " + d.source.name + "\n" + formatNumber(d.value);
				  });

				  d3.select(this)
					.style("stroke", LINK_COLOR)
					.transition()
					  .duration(TRANSITION_DURATION / 2)
					  .style("opacity", OPACITY.LINK_HIGHLIGHT);
				}
			  });

			  linkEnter.on('mouseleave', function () {
				if (!isTransitioning) {
				  hideTooltip();

				  d3.select(this)
					.style("stroke", LINK_COLOR)
					.transition()
					  .duration(TRANSITION_DURATION / 2)
					  .style("opacity", OPACITY.LINK_DEFAULT);
				}
			  });
			  
                
                <!-- //Highlight function, that shows usage of a node in the XDSM -->
                <!-- function highlight(data) -->
                <!-- { -->
                    <!-- xPath = data.xPath; -->
                    <!-- var allLinks = d3.selectAll(".link"); -->
                    <!-- var allLinks_tmp = allLinks[0]; -->
                    <!-- allLinks_tmp.forEach(function(p) { -->
                        <!-- var firstElement_tmp = p.__data__.name.split("/")[1] -->
                        <!-- var text_fromFirst = "/"+firstElement_tmp+xPath.split(firstElement_tmp)[1] -->
                        <!-- if (include(p.__data__.name,text_fromFirst)) -->
                        <!-- { -->
                            <!-- d3.select(p).style("opacity", .8);			 -->
                        <!-- } -->
                        <!-- else -->
                        <!-- { -->
                            <!-- d3.select(p).style("opacity", 0);			 -->
                        <!-- } -->
                    <!-- }); -->
                <!-- } -->
                
                function showVariableTable(aVariable)
                {											
                    var headLine = "Node Information (" + aVariable.name + ")";
                    var data = [];
                    // render the table(s)
                    data.push({ "name" : "Name", "value" : "\""+aVariable.name+"\"" })
                    data.push({ "name" : "xPath", "value" : aVariable.xPath })
                    if (aVariable.type){data.push({ "name" : "Type", "value" : aVariable.type })}
                    if (aVariable.level){data.push({ "name" : "Level", "value" : aVariable.level })}
                    if (aVariable.children || aVariable._children)
                    {
                        var childrenNum=0;
                        if (aVariable.children){childrenNum=childrenNum+aVariable.children.length}
                        if (aVariable._children){childrenNum=childrenNum+aVariable._children.length}
                        data.push({ "name" : "Number of children", "value" : childrenNum })
                    }
                    if (aVariable.dimension){data.push({ "name" : "Dimension", "value" : aVariable.dimension })}
                    else if(aVariable.dimension===null){data.push({ "name" : "Dimension", "value" : "undefined" })}
                    if (aVariable.value){data.push({ "name" : "Value(s)", "value" : aVariable.value })}
                    
                    var d3_body = d3.select("body");
                    
                    var panel_div = d3_body.append("div").attr("class", "myPanel panel-default")
                    panel_div.append("div").attr("class","panel-heading")
                        .append("div").attr("class","panel_title").append("h4").text(headLine)
                    var listGroup = panel_div.append("div").attr("class","panel-body")
                        .append("table").attr("id","myTable")
                        .append("tbody")
                        
                    data.forEach(function(listElement)
                    {
                        var row = listGroup.append("tr")
                        row.append("td").text(listElement.name)
                        row.append("td").text(listElement.value)
                        
                    })
                    $('.myPanel').lobiPanel({
                        reload: false,
                        editTitle: false,
                        expand: false,
                        unpin: false,
                        resize: "none",
                        minWidth: 200,
                        minHeight: 200,
                        maxWidth: 1100,
                        maxHeight: 1200,
                    });
                    $('.myPanel').lobiPanel('unpin');
                }

                //menu --> functions for right click options
                var nodeMenu = [
                {
                    title: 'Show node information',
                    onMouseDown: function(elm, d, i) {
                        showVariableTable(d);
                    },
                    onMouseUp: function(elm, d, i) {},
                    onMouseOver: function(elm, d, i) {},
                    childrenItems: []
                },
                {
                    title: 'Show usage of node in diagram',
                    onMouseDown: function(elm, d, i) {
                        d3.selectAll(".treeFrame").attr("fill-opacity", .5);
                        d3.selectAll(".nodeText").style("fill-opacity", 0.5);					
                        highlight(d);
                    },
                    onMouseUp: function(elm, d, i) {				
                        d3.selectAll(".link").style("opacity",.6)
                        d3.selectAll(".treeFrame").attr("fill-opacity", .8);
                        d3.selectAll(".nodeText").style("fill-opacity", 1);					
                    },
                    onMouseOver: function(elm, d, i) {
                    },
                    childrenItems: []
                },
                {
                    title: 'Copy x-path to clipboard',
                    onMouseDown: function(elm, d, i) {
                        window.prompt("Copy to clipboard: Ctrl+C, Enter", d.xPath);
                        d3.select('.d3-context-menu').style('display', 'none');
                    },
                    onMouseUp: function(elm, d, i) {
                    },
                    onMouseOver: function(elm, d, i) {
                    },
                    childrenItems: []
                }
                ]
                
                function showList(aTitle,aList,aMenu)
                {
                    if (aList.length != 0)
                    {
                        var d3_body = d3.select("body");
                        
                        var panel_div = d3_body.append("div").attr("class", "myPanel panel-default")
                        panel_div.append("div").attr("class","panel-heading")
                            .append("div").attr("class","panel_title").append("h4").text(aTitle)
                        panel_div.append("input")
                            .attr("id","myInput")
                            .attr("placeholder","Filter search...")
                            .attr("title","Type in a name")
                            .attr("onkeyup","filterSearch()")
                        var listGroup = panel_div.append("div").attr("class","panel-body")
                            .append("table").attr("id","myTable")
                            .append("tbody")
                            
                        var tr = listGroup
                            .selectAll('tr')
                            .data(aList).enter()
                                .append('tr')
                        var td = tr.append("td").html(function(d) { 
                            if (d.xPath){return d.xPath;} 
                            else {return d.name;} 
                        })
                        tr.on('contextmenu', d3.contextMenu(aMenu));
                        $('.myPanel').lobiPanel({
                            reload: false,
                            editTitle: false,
                            expand: false,
                            unpin: false,
                            resize: "none",
                            minWidth: 200,
                            minHeight: 200,
                            maxWidth: 1100,
                            maxHeight: 1200,
                        });
                        $('.myPanel').lobiPanel('unpin');
                        $('.myPanel').lobiPanel('height','5000');
                    }
                }
                
			  
				function showLinkTree(aLink,aVarCategory)
				{
					var name_tmp = "Variable flow: " + aLink.__data__.source.name +  "  →  " + aLink.__data__.target.name
					var theSchema = currentGraph.variableSchemes[aVarCategory];
					createTreeLayout(name_tmp,theSchema,aLink.__data__.name,link,nodeMenu);
				}
				
				function showEdgeTable(aLink)
				{						
                    
                    var headLine = "Edge Information (" + aLink.from + " - " + aLink.to + ")";
					var anEdgeNameSplit = aLink.name.split(',')
					var numberOfconnections = anEdgeNameSplit.length;
					var dimension=0;
					var nullDim=false;
					var undefinedLeafs;
					var theLeafNodes = JSON.parse(JSON.stringify(currentGraph.variableSchemes[varCategories[0].name]));
					var pipeData = aLink.name;
					prune_tree(pipeData,theLeafNodes)
					var undefLeafsNumber=0;
					var defLeafsNumber=0;
					for (var k=0;k<theLeafNodes.length;k++)
					{
						if (theLeafNodes[k].dimension!=null){dimension = dimension+theLeafNodes[k].dimension}
						else{nullDim=true}
						if (theLeafNodes[k].value.includes("could not be found")||theLeafNodes[k].value.includes("unknown"))
						{
							if (undefLeafsNumber==0){undefinedLeafs += theLeafNodes[k].xPath;}
							else {undefinedLeafs += "," + theLeafNodes[k].xPath;}
							undefLeafsNumber ++;
						}
						else
						{
							defLeafsNumber++;
						}
					}		
					//Render data for table
					var data = [];
					data.push({ "name" : "Total number of connections", "value" : numberOfconnections })
					data.push({ "name" : "Number of referenced connections", "value" : defLeafsNumber })
					data.push({ "name" : "Dimension of referenced connections", "value" : String(dimension) })
					if (undefinedLeafs)
					{
						data.push({ "name" : "Number of unreferenced connections", "value" :  undefLeafsNumber})					
					}
                    
                    
                    
                    var d3_body = d3.select("body");
                    
                    var panel_div = d3_body.append("div").attr("class", "myPanel panel-default")
                    panel_div.append("div").attr("class","panel-heading")
                        .append("div").attr("class","panel_title").append("h4").text(headLine)
                    var listGroup = panel_div.append("div").attr("class","panel-body")
                        .append("table").attr("id","myTable")
                        .append("tbody")
                        
                    data.forEach(function(listElement)
                    {
                        var row = listGroup.append("tr")
                        row.append("td").text(listElement.name)
                        row.append("td").text(listElement.value)
                        
                    })
                    $('.myPanel').lobiPanel({
                            reload: false,
                            editTitle: false,
                            expand: false,
                            unpin: false,
                            resize: "none",
                            minWidth: 200,
                            minHeight: 200,
                            maxWidth: 1100,
                            maxHeight: 1200,
                        });
                    $('.myPanel').lobiPanel('unpin');
				}
				
				//linkMenu --> functions for right click options
				var linkChildrenItemsTree = [];
				for (var j=0; j< varCategories.length; j++)
				{
					linkChildrenItemsTree.push({title: 'according to ' + varCategories[j].description,
											varCategory: varCategories[j].name,
											onMouseClick: function(elm, data, i) {showLinkTree(elm,data.varCategory)},
											onMouseOver: function(elm,data,i){}});
				}
                var linkChildrenItemsList = [];
				for (var j=0; j< varCategories.length; j++)
				{
					linkChildrenItemsList.push({title: 'according to ' + varCategories[j].description,
											varCategory: varCategories[j].name,
											onMouseClick: function(elm, data, i) {
                                                var variables = [];
                                                var pipeData = elm.__data__.name;
                                                var title = "List view of variable flow: " + elm.__data__.source.id +  "  →  " + elm.__data__.target.id
                                                variables = JSON.parse(JSON.stringify(currentGraph.variableSchemes[data.varCategory]))
                                                prune_tree(pipeData,variables)
                                                variables.forEach(function(variable)
                                                {
                                                    variable.name = variable.xPath
                                                    //work around because nodeMenu expexts the data, to have a "data" object inside
                                                    variable.data = variable
                                                    variable.data.name = variable.xPath.split("/")[variable.xPath.split("/").length-1]
                                                })
                                                
                                                showList(title,variables,nodeMenu);
                                            },
											onMouseOver: function(elm,data,i){}});
				}
				var linkMenu = [
				{
					title: 'Show edge info',
					onMouseDown: function(elm, k, i) {
						showEdgeTable(k)
					},
					onMouseUp: function(elm, k, i) {
					},
					onMouseOver: function(elm, d, i) {
					},
					childrenItems: []
				},
				{
					title: 'Show variable tree...',
					onMouseDown: function(elm, k, i) {
					},
					onMouseUp: function(elm, k, i) {
					},
					onMouseOver: function(elm, d, i) {
					},
					childrenItems: linkChildrenItemsTree
				},
				{
					title: 'Show variable list...',
					onMouseDown: function(elm, k, i) {
					},
					onMouseUp: function(elm, k, i) {
					},
					onMouseOver: function(elm, d, i) {
					},
					childrenItems: linkChildrenItemsList
				}
				]

				var thisLink = d3.select(this);
				linkEnter.on('contextmenu', d3.contextMenu(linkMenu))

			  linkEnter.sort(function (a, b) { return b.thickness - a.thickness; })
				.classed("leftToRight", function (d) {
				  return d.direction > 0;
				})
				.classed("rightToLeft", function (d) {
				  return d.direction < 0;
				})
				.style("marker-end", function () {
				  return 'url(#arrowHead)';
				})
				.style("stroke", LINK_COLOR)
				.style("opacity", 0)
				.transition()
				  .delay(TRANSITION_DURATION)
				  .duration(TRANSITION_DURATION)
				  .attr("d", path)
				  .style("stroke-WIDTH", function (d) { return Math.max(1, d.thickness); })
				  .style("opacity", OPACITY.LINK_DEFAULT);


			  node = svg.select("#nodes").selectAll(".sankeyNode")
				  .data(biHiSankey.collapsedNodes(), function (d) { return d.id; });


			  node.transition()
				.duration(TRANSITION_DURATION)
				.attr("transform", function (d) { return "translate(" + d.x + "," + d.y + ")"; })
				.style("opacity", OPACITY.NODE_DEFAULT)
				.select("rect")
				  
				  .style("stroke", function(d) { 
					  return d3.rgb(d.color).darker(2); })
				  .style("stroke-WIDTH", "1px")
				  .attr("height", function (d) { return d.height; })
				  .attr("width", biHiSankey.nodeWidth());


			  node.exit()
				.transition()
				  .duration(TRANSITION_DURATION)
				  .attr("transform", function (d) {
					var collapsedAncestor, endX, endY;
					collapsedAncestor = d.ancestors.filter(function (a) {
					  return a.state === "collapsed";
					})[0];
					endX = collapsedAncestor ? collapsedAncestor.x : d.x;
					endY = collapsedAncestor ? collapsedAncestor.y : d.y;
					return "translate(" + endX + "," + endY + ")";
				  })
				  .remove();


			  nodeEnter = node.enter().append("g").attr("class", "sankeyNode");

			  nodeEnter
				.attr("transform", function (d) {
				  var startX = d._parent ? d._parent.x : d.x,
					  startY = d._parent ? d._parent.y : d.y;
				  return "translate(" + startX + "," + startY + ")";
				})
				.style("opacity", 1e-6)
				.transition()
				  .duration(TRANSITION_DURATION)
				  .style("opacity", OPACITY.NODE_DEFAULT)
				  .attr("transform", function (d) { return "translate(" + d.x + "," + d.y + ")"; });

			  nodeEnter.append("text");
			  nodeEnter.append("rect")
				.style("fill", function(d) { 
					  return d.color = colorScale(d.name.replace(/ .*/, "")); })
				  .style("stroke", function(d) { 
					  return d3.rgb(d.color).darker(2); })
				.style("stroke-WIDTH", "1px")
				.attr("height", function (d) { return d.height; })
				.attr("width", biHiSankey.nodeWidth());
				
			  node.append("svg:title").text("Click left to drag, click right to inspect")
			  node.on("mouseenter", function (g) {
				if (!isTransitioning) {
				  restoreLinksAndNodes();
				  highlightConnected(g);
				  fadeUnconnected(g);

				  d3.select(this).select("rect")
					.style("stroke", function (d) {
					  return d3.rgb(d.color).darker(0.1);
					})
					.style("fill-opacity", OPACITY.LINK_DEFAULT);

				  tooltip
					.style("left", g.x + MARGIN.LEFT + "px")
					.style("top", d3.event.pageY-40 + "px")
					.transition()
					  .duration(TRANSITION_DURATION)
					  .style("opacity", 1).select(".value")
					  .text(function () {
						var additionalInstructions = g.children.length ? "\n(Double click to expand)" : "";
						return g.name + "\nInputs: " + formatFlow(g.inputs) + "\nOutputs: " + formatFlow(g.outputs);
					  });
				}
			  });

			  node.on("mouseleave", function () {
				if (!isTransitioning) {
				  hideTooltip();
				  restoreLinksAndNodes();
				}
			  });

			  node.filter(function (d) { return d.children.length; })
				.on("dblclick", showHideChildren);
				
				
				//aigner: Right click options for tools
				//##############################################################################################################################
				
				//aigner: Table for competence/tool information
				//############################################################
				function showToolTable(aTool)
                {
                    var aToolNameSplit = aTool.name.split(': ')			
                    var headLine;
                    if (aToolNameSplit.length>1){headLine = "Competence Information: (" + aToolNameSplit[1] + ")";}
                    else {headLine = "Competence Information: (" + aToolNameSplit[0] + ")";}
                    
                    
                    var data = [];
                    // render the table(s)
                    if (aTool.metadata.length==0)
                    {
                        data.push({ "name" : "NO TOOL METADATA AVAILABLE", "value" : "..." })
                    }
                    function findSubMetaData(aMetaData)
                    {
                        for(var key in aMetaData)
                        {
                            if (typeof aMetaData[key] === 'object')
                            {
                                data.push({ "name" : key, "value" : ''}) ;
                                findSubMetaData(aMetaData[key]);
                            }
                            else
                            {
                                data.push({ "name" : key, "value" : aMetaData[key] })
                            }
                        }
                    }
                    for (var j=0; j < aTool.metadata.length;j++)
                    {
                        var metaData = aTool.metadata[j];
                        findSubMetaData(metaData);						
                    }
                
                    
                    var d3_body = d3.select("body");
                    
                    var panel_div = d3_body.append("div").attr("class", "myPanel panel-default")
                    panel_div.append("div").attr("class","panel-heading")
                        .append("div").attr("class","panel_title").append("h4").text(headLine)
                    var listGroup = panel_div.append("div").attr("class","panel-body")
                        .append("table").attr("id","myTable")
                        .append("tbody")
                        
                    data.forEach(function(listElement)
                    {
                        var row = listGroup.append("tr")
                        row.append("td").text(listElement.name)
                        row.append("td").text(listElement.value)
                        
                    })
                    $('.myPanel').lobiPanel({
                            reload: false,
                            editTitle: false,
                            expand: false,
                            unpin: false,
                            resize: "none",
                            minWidth: 200,
                            minHeight: 200,
                            maxWidth: 1100,
                            maxHeight: 1200,
                        });
                    $('.myPanel').lobiPanel('unpin');
                }
				//############################################################
				
				//aigner: Creation of input/output tree
				//############################################################
				function showIOTree(categoryID, categoryDescr, theNode, io)
				{	
					var links = d3.selectAll(".link");
					var array="";
					var name_tmp = "";
					links.each(function(theLink)
					{		
						if (io=="in")
						{
							if (theLink.to == theNode.id)
							{
								array = array + "," + theLink.name;
							}
							name_tmp = "Input tree view:" + theNode.id + "; Categorization: " + categoryDescr;
						}
						else if (io=="out")
						{
							if (theLink.from == theNode.id)
							{
								array = array + "," + theLink.name;
							}
							name_tmp = "Output tree view:" + theNode.id + "; Categorization: " + categoryDescr;
						}
					})
					var theSchema = JSON.parse(JSON.stringify(currentGraph.variableSchemes[categoryID]));
					createTreeLayout(name_tmp,theSchema,array,link,nodeMenu)
				}
				//############################################################

				var inputChildrenitemsTree = [];
				var outputChildrenitemsTree = [];
				for (var j=0; j< varCategories.length; j++)
				{
					inputChildrenitemsTree.push({title: 'according to ' + varCategories[j].description,
											 varCategory: varCategories[j].name,
											 description: varCategories[j].description,
											 onMouseClick: function(elm, data, i) {showIOTree(data.varCategory,data.description,elm.__data__,"in")},
											 onMouseOver: function(elm,data,i){}});
					outputChildrenitemsTree.push({title: 'according to ' + varCategories[j].description,
											 varCategory: varCategories[j].name,
											 description: varCategories[j].description,
											 onMouseClick: function(elm, data, i) {showIOTree(data.varCategory,data.description,elm.__data__,"out")},
											 onMouseOver: function(elm,data,i){}});
				}
                var inputChildrenitemsList = [];
				var outputChildrenitemsList = [];
				for (var j=0; j< varCategories.length; j++)
				{
					inputChildrenitemsList.push({title: 'according to ' + varCategories[j].description,
											 varCategory: varCategories[j].name,
											 description: varCategories[j].description,
											 onMouseClick: function(elm, data, i) {
                                                var links = d3.selectAll(".link");
                                                    var pipeData="";
                                                    var name_tmp = "";
                                                    links.each(function(theLink)
                                                    {		
                                                        if (theLink.to == elm.__data__.id)
                                                        {
                                                            pipeData = pipeData + "," + theLink.name;
                                                        }
                                                    })
                                                    
                                                    var title = "List view of all inputs for " + elm.__data__.id + "; Categorization: " + data.description
                                                    var variables = JSON.parse(JSON.stringify(currentGraph.variableSchemes[data.varCategory]))
                                                    prune_tree(pipeData,variables)
                                                    variables.forEach(function(variable)
                                                    {
                                                        variable.name = variable.xPath
                                                        //work around because nodeMenu expexts the data, to have a "data" object inside
                                                        variable.data = variable
                                                        variable.data.name = variable.xPath.split("/")[variable.xPath.split("/").length-1]
                                                    })
                                                    
                                                    showList(title,variables,nodeMenu);
                                             },
											 onMouseOver: function(elm,data,i){}});
					outputChildrenitemsList.push({title: 'according to ' + varCategories[j].description,
											 varCategory: varCategories[j].name,
											 description: varCategories[j].description,
											 onMouseClick: function(elm, data, i) {
                                                    var links = d3.selectAll(".link");
                                                    var pipeData="";
                                                    var name_tmp = "";
                                                    links.each(function(theLink)
                                                    {		
                                                        if (theLink.from == elm.__data__.id)
                                                        {
                                                            pipeData = pipeData + "," + theLink.name;
                                                        }
                                                    })
                                                    
                                                    var title = "List view of all outputs from " + elm.__data__.id + "; Categorization: " + data.description
                                                    var variables = JSON.parse(JSON.stringify(currentGraph.variableSchemes[data.varCategory]))
                                                    prune_tree(pipeData,variables)
                                                    variables.forEach(function(variable)
                                                    {
                                                        variable.name = variable.xPath
                                                        //work around because nodeMenu expexts the data, to have a "data" object inside
                                                        variable.data = variable
                                                        variable.data.name = variable.xPath.split("/")[variable.xPath.split("/").length-1]
                                                    })
                                                    
                                                    showList(title,variables,nodeMenu);
                                            
                                             },
											 onMouseOver: function(elm,data,i){}});
				}
				//menu --> functions for right click options
				var toolMenu = [
				{
					title: 'Show competence info',
					onMouseDown: function(elm, k, i) {
						showToolTable(k);
					},
					onMouseUp: function(elm, k, i) {
					},
					onMouseOver: function(elm, d, i) {
					},
					childrenItems: []
				},
				{
					title: 'Show input variable tree...',
					onMouseDown: function(elm, k, i) {
					},
					onMouseUp: function(elm, k, i) {
					},
					onMouseOver: function(elm, d, i) {
					},
					childrenItems: inputChildrenitemsTree
				},
				{
					title: 'Show output variable tree...',
					onMouseDown: function(elm, k, i) {
					},
					onMouseUp: function(elm, k, i) {
					},
					onMouseOver: function(elm, d, i) {
					},
					childrenItems: outputChildrenitemsTree
				},
				{
					title: 'Show input variable list...',
					onMouseDown: function(elm, k, i) {
					},
					onMouseUp: function(elm, k, i) {
					},
					onMouseOver: function(elm, d, i) {
					},
					childrenItems: inputChildrenitemsList
				},
				{
					title: 'Show output variable list...',
					onMouseDown: function(elm, k, i) {
					},
					onMouseUp: function(elm, k, i) {
					},
					onMouseOver: function(elm, d, i) {
					},
					childrenItems: outputChildrenitemsList
				}
				]
				//##############################################################################################################################
				
				node.on('contextmenu', d3.contextMenu(toolMenu));
				
			  // allow nodes to be dragged to new positions
			  node.call(d3.behavior.drag()
				.origin(function (d) 
				{ 
					 d3.select(this).style("cursor", "grabbing")	
					return d; 
				})
				.on("dragstart", function () {
					d3.select(this).style("cursor", "grabbing")	
					this.parentNode.appendChild(this); })
				.on("drag", dragmove));

			  // add in the text for the nodes
			  node.filter(function (d) { return d.value !== 0; })
				.select("text")
				  .attr("x", -6)
				  .attr("y", function (d) { return d.height / 2; })
				  .attr("dy", ".35em")
				  .attr("text-anchor", "end")
				  .attr("transform", null)
				  .text(function (d) { return d.name; })
				.filter(function (d) { return d.x < WIDTH / 2; })
				  .attr("x", 6 + biHiSankey.nodeWidth())
				  .attr("text-anchor", "start");


			  collapser = svg.select("#collapsers").selectAll(".collapser")
				.data(biHiSankey.expandedNodes(), function (d) { return d.id; });


			  collapserEnter = collapser.enter().append("g").attr("class", "collapser");

			  collapserEnter.append("circle")
				.attr("r", COLLAPSER.RADIUS)
				.style("fill", function (d) {
				  d.color = colorScale(d.name.replace(/ .*/, ""));
				  return d.color;
				});

			  collapserEnter
				.style("opacity", OPACITY.NODE_DEFAULT)
				.attr("transform", function (d) {
				  return "translate(" + (d.x + d.width / 2) + "," + (d.y + COLLAPSER.RADIUS) + ")";
				});

			  collapserEnter.on("dblclick", showHideChildren);

			  collapser.select("circle")
				.attr("r", COLLAPSER.RADIUS);

			  collapser.transition()
				.delay(TRANSITION_DURATION)
				.duration(TRANSITION_DURATION)
				.attr("transform", function (d, i) {
				  return "translate("
					+ (COLLAPSER.RADIUS + i * 2 * (COLLAPSER.RADIUS + COLLAPSER.SPACING))
					+ ","
					+ (-COLLAPSER.RADIUS - OUTER_MARGIN)
					+ ")";
				});

			  collapser.on("mouseenter", function (g) {
				if (!isTransitioning) {
				  showTooltip().select(".value")
					.text(function () {
					  return g.name + "\n(Double click to collapse)";
					});

				  d3.select(this)
					.style("opacity", OPACITY.NODE_HIGHLIGHT)
					.select("circle")

				  node.filter(function (d) {
					return d.ancestors.indexOf(g) >= 0;
				  }).style("opacity", OPACITY.NODE_HIGHLIGHT)
					.select("rect")
				}
			  });

			  collapser.on("mouseleave", function (g) {
				if (!isTransitioning) {
				  hideTooltip();
				  d3.select(this)
					.style("opacity", OPACITY.NODE_DEFAULT)
					.select("circle")

				  node.filter(function (d) {
					return d.ancestors.indexOf(g) >= 0;
				  }).style("opacity", OPACITY.NODE_DEFAULT)
					.select("rect")
				}
			  });

			  collapser.exit().remove();

			}


			function createTreeLayout(theName,schema,theLinks,theAllLinks,nodeMenu)
			{	
                //aigner: Build the tree layout
                //######################################################################
				var treeData = (JSON.parse(JSON.stringify(schema)));
                //The tree will only be pruned if there is pipeData, such as in an edge or for the input of a tool
                if (theLinks)
                {
                    prune_tree(theLinks, treeData);
                }
                
                //build tree layout for vistoms
                var newTree = {};
                buildTree(newTree, treeData)                    
                treeData = newTree
               //######################################################################
               
               
				var width= 1000;
				var height= 500;
				var xOffset = 10;
				var xOffset2 = 100;	
				
				// Calculate total nodes, max label length
				var totalNodes = 0;
				var maxLabelLength = 0;
				// variables for drag/drop
				var selectedNode = null;
				var draggingNode = null;
				// Misc. variables
				var i = 0;
				var duration = 500;
				var root;
				

				// size of the diagram
				var viewerWidth = width/3;
				var viewerHeight = height+50;

				var tree = d3.layout.tree()
					.size([viewerHeight, viewerWidth])

				// define a d3 diagonal projection for use by the node paths later on.
				var diagonal = d3.svg.diagonal()
					.projection(function(d) {
						return [d.y+xOffset, d.x];
					});

				// A recursive helper function for performing some setup by walking through all nodes

				function visit(parent, visitFn, childrenFn) {
					if (!parent) return;

					visitFn(parent);

					var children = childrenFn(parent);
					if (children) {
						var count = children.length;
						for (var i = 0; i < count; i++) {
							visit(children[i], visitFn, childrenFn);
						}
					}
				}
                
				// Call visit function to establish maxLabelLength
				visit(treeData, function(d) {
					totalNodes++;
					maxLabelLength = Math.max(d.name.length, maxLabelLength);
				}, function(d) {
					return d.children && d.children.length > 0 ? d.children : null;
				});
				
				function getMaxLength(data)
				{
					var maxLen=0;;
					for (var i = 0; i < data.length; i++)
					{
						maxLen = Math.max(data[i].name.length, maxLen);
					}
					for (var i = 0; i < data.length; i++)
					{
						data[i].labelLength = maxLen;
						if (data[i].children)
						{getMaxLength(data[i].children);}
						if (data[i]._children)
						{getMaxLength(data[i]._children);}
					}
					
				}
				if(treeData._children)
				{getMaxLength(treeData._children);}
				if(treeData.children)
				{getMaxLength(treeData.children);}
				treeData.labelLength = treeData.name.length;


				// Collapse the node and all it's children
				function collapse(d) {
				  if(d.children) {
					d._children = d.children
					d._children.forEach(collapse)
					d.children = null
				  }
				}
				// Collapse the node and all it's children
				function expand(d) {
				  if(d._children) {
					d.children = d._children
					d.children.forEach(expand)
					d._children = null
				  }
				}
				// Toggle children on click.
				function click(d) 
				{
					if (d.children) {
						d._children = d.children;
						d.children = null;
					} else 
					{
						d.children = d._children;
						d._children = null;
					}
					update(d,theAllLinks);
				}
				// Collapse/expand entire tree on double-click
				function dblclick(d) 
				{
					if(d.children) 
					{
						collapse(d);
					}
					else if(d._children)
					{
						expand(d);
					}
					update(d,theAllLinks);
				}	

				
				//aigner: Here the tree layout is created
				var d3_body = d3.select("body")
                var lobiID = String(getRandomInt(0,1000))
                var divClassName = "treeDiv"+lobiID
                var treeLayoutdiv = d3_body.append("div").attr("class",divClassName+" panel-default")
                                                .style("left","200px")
                                                .style("top","200px")
                                                .style("position", "absolute")
                treeLayoutdiv.append("div").attr("class","panel-heading")
                    .append("div").attr("class","panel_title").append("h4").text(theName)
                $('.'+divClassName).lobiPanel({
                    reload: false,
                    editTitle: false,
                    unpin: false,
                    minWidth: 200,
                    maxWidth: 100000,
                    minHeight: 200,
                    maxHeight: 100000,
                });
                $('.'+divClassName).lobiPanel('unpin');
										
				var treeLayoutSVG = treeLayoutdiv.append("svg").attr("class","treeLayoutSVG")
				var treeGroup = treeLayoutSVG.append("g").attr("class","treeGroup").style("position","absolute")
								.attr("transform", "translate(50,0)");
				var margin = {top: 20, right: 90, bottom: 20, left: 90},
							  width = 960 - margin.left - margin.right,
							  height = 500 - margin.top - margin.bottom;

				// append the svg object to the body of the page
				// appends a 'group' element to 'svg'
				// moves the 'group' element to the top left margin
				var offset_tmp = 60;
				treeLayoutSVG = treeLayoutSVG.attr("width", width + 1.2*margin.right + margin.left+offset_tmp)
						 .attr("height", height + margin.top + margin.bottom+offset_tmp);
				treeLayoutdiv = treeLayoutdiv.attr("width", width + 1.2*margin.right + margin.left+offset_tmp)
						 .attr("height", height + margin.top + margin.bottom+offset_tmp)
						 .on("mouseover",function(){d3.select(this).style("cursor", "grab")})
				treeGroup = treeGroup
					.attr("width", width + margin.right + margin.left)
					.attr("height", height + margin.top + margin.bottom);
					
                
                var frame = treeGroup.append("rect")
					.attr("class","treeFrame")
					.attr("stroke-width", 1)
					.attr("stroke", "white")
					.attr("fill-opacity", .8);				
				
				// Define the root
				root = treeData;
				root.x0 = 0;
				root.y0 = 0;
				
				// Append a group which holds all nodes and which the zoom Listener can act upon.
				var svgGroup_xOff = root.name.length*10+50
				var svgGroup = treeGroup.append("g")
					.attr("class","tree_"+theName)
					.attr("transform", "translate("+ svgGroup_xOff + "," + String(margin.top+10) + ")");
				
				collapse(root);
				update(root,theAllLinks);
				

				function update(source,allLinks) 
				{
					// Compute the new height, function counts total children of root node and sets tree height accordingly.
					// This prevents the layout looking squashed when new nodes are made visible or looking sparse when nodes are removed
					// This makes the layout more consistent.
					var levelWidth = [1];
					var levelLength = [1];
					var childCount = function(level, n) {

						if (n.children && n.children.length > 0) {
							if (levelWidth.length <= level + 1) levelWidth.push(0);
							
							levelWidth[level + 1] += n.children.length;
							n.children.forEach(function(d) {
								childCount(level + 1, d);
							});
						}
					};
					childCount(0, root);

					var newHeight = d3.max(levelWidth) * 60; // 20 pixels per line
					var newWidth = viewerWidth+300;
					
					//BENNI: fill in viewerHeight instead of newHeight for other expanding/collapsing beaviour
					tree = tree.size([newHeight, newWidth]);
					
					frame = frame.attr("width",newWidth)
						.attr("height",newHeight+60)
						.attr("fill","white");
						
					
					
					// Compute the new tree layout.
					var nodes = tree.nodes(root),
						links = tree.links(nodes);
						
						
					//aigner: count all descendants of a node
					function countDescendants(node, counter) 
					{
						if (node._children)
						{
							node._children.forEach(function(n)
							{
								counter = countDescendants(n, counter);
							});
						}
						else if (node.children)
						{
							node.children.forEach(function(n)
							{
								counter = countDescendants(n, counter);
							});
						}
						else
						{
							counter ++;
						}
						return counter;
					}
					
					
					var depths = [];
					var labelLength = [];
					nodes.forEach(function(d) 
					{				
						//aigner: If node is collapsed show number of ancestors
						if (d._children)
						{
							d._childrenNum = countDescendants(d, 0);
							d.text = d.name + " (" + d._childrenNum + ")";
						}
						else 
						{
							d.text = d.name
						}
						
						//aigner: Find maximum labelLength for each level
						if (!depths.includes(d.depth))
						{
							depths.push(d.depth);
							labelLength.push(d.text.length);
						}
						else
						{
							if (d.depth!=0){labelLength[depths.indexOf(d.depth)] = Math.max(d.text.length, labelLength[depths.indexOf(d.depth)]);}
						}
					});
					// Set widths between levels based on labelLength of each level.
					var newWidth=0;	
					var depth_tmp = 0;			
					nodes.forEach(function(d) 
					{
						if (d.parent)
						{
							d.y = d.parent.y+(labelLength[depths.indexOf(d.depth)])*10+50;
						}
						else{d.y=0}
						
						
						if (newWidth<d.y)
						{newWidth=d.y;}
						
						// console.log("##############")
						// console.log(d.name)
						// console.log("x = " + d.x)
						// console.log("y = " + d.y)
						// console.log("depth = " + depths.indexOf(d.depth))
						// console.log("##############")
					});	
					
					
					
                    newWidth=Math.max(newWidth,getTextWidth(theName,"Arial 12pt"));
					//aigner: Adjust height and width of the frame
					$('.'+divClassName).lobiPanel('setWidth', newWidth + margin.top + margin.bottom +400+maxLabelLength*25+offset_tmp);
                    $('.'+divClassName).lobiPanel('setHeight', newHeight + margin.top + 2*margin.bottom+offset_tmp);
					treeLayoutSVG = treeLayoutSVG.attr("height", newHeight + margin.top + margin.bottom+offset_tmp)
					treeLayoutSVG = treeLayoutSVG.attr("width", newWidth + margin.top + margin.bottom +300+maxLabelLength*25+offset_tmp)
					frame = frame.attr("height", newHeight + margin.top + margin.bottom)
					frame = frame.attr("width", newWidth + margin.top + margin.bottom+300+maxLabelLength*15)

					
					//Function writeTreeToXML goes through tree nodes and puts the into an xml document
					function writeTreeToXML(aNode,anXMLDoc,anXPath)
					{
						//Variable "children" 
						//--> One children variable, no matter whether a node has "_children" (collapsed) or "children" (expanded)
						var children;
						if (aNode._children){children = aNode._children;}
						else if (aNode.children){children = aNode.children;}
						
						//Get current xml element with its xPath
						var element = anXMLDoc.evaluate(anXPath,anXMLDoc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ).singleNodeValue;
						if (element != null) {element.value = '...';}
						
						//If a node has children (collapsed or expanded), loop through them
						if (children)
						{
							for (var i=0; i < children.length;i++)
							{
								//Name of the new XML element --> childName
								var child = children[i];
								var childName = child.name.split(/[\[\]]+/);//Split childName at "[]" which is the uID 
								var cleanChildName = childName[0].split(/[\+\*\^\-\ \#]+/);//Split childName all special characters
								var newNode = anXMLDoc.createElement(String(cleanChildName[0]));
								
								//The children are appended to the xPath --> newXPath
								var newXPath = anXPath+"/"+cleanChildName[0];
								
								//If childName contains a uID, make the uID an attribute
								if (childName[1])
								{
									if (parseInt(childName[1]))
									{
										var dummyID = childName[1];
										newNode.setAttribute("dummyID", dummyID)
										newXPath = newXPath+"[@dummyID='"+dummyID+"']";
									}
									else
									{
										var uID = childName[1];
										newNode.setAttribute("uID", uID)
										newXPath = newXPath+"[@uID='"+uID+"']";
									}
								}
								if (cleanChildName.length>1) {newNode.setAttribute("elementName", childName[0])};
								
								//Append the newNode to the xml structure
								element.appendChild(newNode);
								
								
								
								////aigner: Sorting of XML elements according to "uID"
								// var items = element.children;
								// var itemsArr = [];
								// for (var j in items) {
									// if (items[j].nodeType == 1) { // get rid of the whitespace text nodes
										// itemsArr.push(items[j]);
									// }
								// }
								// itemsArr.sort(function(a,b){
									// if (a.getAttribute("uID") < b.getAttribute("uID"))
										// return -1;
									// if (a.getAttribute("uID") > b.getAttribute("uID"))
										// return 1;
									// return 0;
									// });

								// for (j = 0; j < itemsArr.length; ++j) {
								  // element.appendChild(itemsArr[j]);
								// }
								
								//call function writeTreeToXML recursively for all children
								writeTreeToXML(child,anXMLDoc,newXPath)
							}
						}
						else
						{
							if (aNode.value){element.innerHTML = String(aNode.value);}
							else{element.innerHTML = " ";}
						}
						//return the xml document
						return anXMLDoc;
					}
					
					function removeAttributeInAllElements(aDocument,attribute)
					{
					  var matchingElements = [];
					  var allElements = aDocument.getElementsByTagName('*');
					  for (var i = 0, n = allElements.length; i < n; i++)
					  {
						if (allElements[i].getAttribute(attribute) !== null)
						{
							allElements[i].removeAttribute(attribute);
						}
					  }
					  return matchingElements;
					}
					
					function putAncestorsInXMLString(strWrapper, aNode)
					{
						//Name of the new XML element
						var nodeName = aNode.name.split(/[\[\]]+/);//Split name at "[]" which is the uID 
						var cleanNodeName = nodeName[0].split(/[\+\*\^\-\ \#]+/);//Split nodeName all special characters
						//If nodeName contains a uID, make the uID an attribute
						if (nodeName[1])
						{
							var uID = nodeName[1];
							strWrapper.val = "<"+cleanNodeName[0]+" uID='"+ uID +"'>"+strWrapper.val+"</"+cleanNodeName[0]+">";;
						}
						else
						{
							strWrapper.val = "<"+cleanNodeName[0]+">"+strWrapper.val+"</"+cleanNodeName[0]+">";;
						}
						var aParent = aNode.parent;
						if (aParent)
						{
							return putAncestorsInXMLString(strWrapper, aParent);
						}
						else 
						{
							return strWrapper;
						}
					}
					
					function putAncestorsInXPath(strWrapper, aNode)
					{
						//Name of the new XML element
						var nodeName = aNode.name.split(/[\[\]]+/);//Split name at "[]" which is the uID 
						var cleanNodeName = nodeName[0].split(/[\+\*\^\-\ \#]+/);//Split nodeName all special characters
						//If nodeName contains a uID, make the uID an attribute
						if (nodeName[1])
						{
							var uID = nodeName[1];
							strWrapper.val = "/"+cleanNodeName[0]+"[@uID='"+uID+"']"+strWrapper.val;
						}
						else
						{
							strWrapper.val = "/"+cleanNodeName[0]+strWrapper.val;
						}
						var aParent = aNode.parent;
						if (aParent)
						{
							return putAncestorsInXPath(strWrapper, aParent);
						}
						else 
						{
							return strWrapper;
						}
					}
					
					// Function to download data to a file
					function download(filename, text) {
                        var element = document.createElement('a');
                        element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
                        element.setAttribute('download', filename);

                        element.style.display = 'none';
                        document.body.appendChild(element);

                        element.click();

                        document.body.removeChild(element);
                    }
					
					
					var treeNodeMenu = nodeMenu;
                    var treeMenu = [
                    {
						title: 'Download full tree as XML-file',
						onMouseDown: function(elm, d, i) {
							//Begin xml structure with the first element
							var xmlString = "<"+nodes[0].name+">"+"</"+nodes[0].name+">";
							//Create a new xml document
							var parser = new DOMParser();
							var xmlDocument = parser.parseFromString(xmlString, "text/xml"); //important to use "text/xml"
							//Get initial xPath of the tree and pass it to the function "writeTreeToXML"
							var initialXPath = "/"+nodes[0].name;								
							writeTreeToXML(nodes[0],xmlDocument,initialXPath);
							//remove all attributes dummyID
							removeAttributeInAllElements(xmlDocument,'dummyID');
							//Make the xml document a string
							var serializer = new XMLSerializer();
							var xmlString = serializer.serializeToString(xmlDocument);
							xmlString = vkbeautify.xml(xmlString);														
							//Download a document with the xml-schema
							download(theName+'_full.xml', xmlString);						
						},
						onMouseUp: function(elm, d, i) {
						},
						onMouseOver: function(elm, d, i) {
						},
						childrenItems: []
					},
					{
						title: 'Download tree as XML-file from current node',
						onMouseDown: function(elm, d, i) {
							var xmlString = putAncestorsInXMLString({ val : '' }, d).val;
							var initialXPath = putAncestorsInXPath({ val : '' }, d).val;												
							//Create a new xml document
							var parser = new DOMParser();
							var xmlDocument = parser.parseFromString(xmlString, "text/xml"); //important to use "text/xml"
							//Get initial xPath of the tree and pass it to the function "writeCurrentTreeToXML"
							writeTreeToXML(d,xmlDocument,initialXPath);
							//remove all attributes dummyID
							removeAttributeInAllElements(xmlDocument,'dummyID');
							//Make the xml document a string
							var serializer = new XMLSerializer();
							xmlString = serializer.serializeToString(xmlDocument);
							xmlString = vkbeautify.xml(xmlString);														
							//Download a document with the xml-schema
							download(theName+"_"+d.name+'.xml', xmlString);						
						},
						onMouseUp: function(elm, d, i) {
						},
						onMouseOver: function(elm, d, i) {
						},
						childrenItems: []
					}
                    ]
					treeNodeMenu = nodeMenu.concat(treeMenu)
					

					// Update the nodes…
					var treeNode = svgGroup.selectAll("g.treeNode")
						.data(nodes, function(d) {
							return d.id || (d.id = ++i);
						});

					var dblclick_timer = false;
					// Enter any new nodes at the parent's previous position.
					var nodeEnter = treeNode.enter().append('g')
					  .attr('class', 'treeNode')
					  .attr("transform", function(d) {
						return "translate(" + source.y0 + "," + source.x0 + ")";
					})
					.on('contextmenu', d3.contextMenu(treeNodeMenu))
					.on("mousedown", function(d) {
						if (d3.event.which != 3)
						{
							// if double click timer is active, this click is the double click
							if ( dblclick_timer )
							{
								clearTimeout(dblclick_timer)
								dblclick_timer = false
								// double click code code comes here
								//console.log("DOUBLE CLICK")
								dblclick(d);
							}
							// otherwise, what to do after single click (double click has timed out)
							else dblclick_timer = setTimeout( function(){
								dblclick_timer = false
								// single click code code comes here
								//console.log("SINGLE CLICK")
								click(d);
							}, 250)
						}
					})
						
					nodeEnter.append("circle")
						.attr('class', 'nodeCircle')
						.attr("r", 0)
						.style("fill", function(d) {
							if (d._children)
							{
								if(theName.includes("Input")){return '#ea9999'}
								else if(theName.includes("Output")){return '#d6ea99'}
								else {return "lightsteelblue"}
							}
							else {return "#fff"}
						})
						.style("stroke", function(d) {
							if(theName.includes("Input")){
								//console.log(d);
								return '#CC0000'}
							else if(theName.includes("Output")){
								//console.log(d);
								return '#99CC00'}
						})
						.attr("cx", function(d) {return xOffset});

					nodeEnter.append("text")
						.attr("x", function(d) 
						{
								return d.children || d._children ? -10+xOffset : 10+xOffset;
						})
						.attr("dy", ".35em")
						.attr('class', 'nodeText')
						.attr("text-anchor", function(d) {
							return d.children || d._children ? "end" : "start";
						})
						.text(function(d) {
							return d.text;
						})
						.style("fill-opacity", 0)
						

					// Update the text to reflect whether node has children or not.
					treeNode.select('text')
						.attr("x", function(d) {
							return d.children || d._children ? -10+xOffset : 10+xOffset;
						})
						.attr("text-anchor", function(d) {
							return d.children || d._children ? "end" : "start";
						})
						.text(function(d) {
							return d.text;
						});

					// Change the circle fill depending on whether it has children and is collapsed
					treeNode.select("circle.nodeCircle")
						.attr("r", 4.5)
						.style("fill", function(d) {
							if (d._children)
							{
								if (d._children)
								{
									if(theName.includes("Input")){return '#ea9999'}
									else if(theName.includes("Output")){return '#d6ea99'}
									else {return "lightsteelblue"}
								}
								else {return "#fff"}
							}
							else{return "#fff";}
						})					
								
					// UPDATE		
					// Transition nodes to their new position.
					var nodeUpdate = treeNode.transition()
						.duration(duration)
						.attr("transform", function(d) {
							return "translate(" + d.y + "," + d.x + ")";
						});

					// Fade the text in
					nodeUpdate.select("text")
						.style("fill-opacity", 1);
					
					nodeEnter.append("svg:title").text("Click left to expand, click right to inspect")
					
					// Remove any exiting nodes
					var nodeExit = treeNode.exit().transition()
					  .duration(duration)
					  .attr("transform", function(d) {
						  return "translate(" + source.y + "," + source.x + ")";
					  })
					  .remove();

					// Update the links…
					var link = svgGroup.selectAll("path.treeLink")
						.data(links, function(d) {
							return d.target.id;
						});

					// Enter any new links at the parent's previous position.
					link.enter().insert("path", "g")
						.attr("class", "treeLink")
						.attr("d", function(d) {
							var o = {
								x: source.x0,
								y: source.y0
							};
							return diagonal({
								source: o,
								target: o
							});
						});

					// Transition links to their new position.
					link.transition()
						.duration(duration)
						.attr("d", diagonal);

					// Transition exiting nodes to the parent's new position.
					link.exit().transition()
						.duration(duration)
						.attr("d", function(d) {
							var o = {
								x: source.x,
								y: source.y
							};
							return diagonal({
								source: o,
								target: o
							});
						})
						.remove();
					
					// Stash the old positions for transition.
					nodes.forEach(function(d) {						
						d.x0 = d.x;
						d.y0 = d.y;
					});
				}
			}

			
			
            //aigner: Here the data is read and the sankey diagram is created
			//#####################################################################//
			var graphs,	currentGraph, varCategories;
			function startSankeyDiagram(data, graphID) 
			{
				graphs =  data.graphs;
				for (var i=0;i<graphs.length;i++)
				{
					if (graphs[i].id==graphID)
					{
						currentGraph = graphs[i]
					}
				}
				varCategories =  data.categories;
				
                //Highlight function, that shows usage of a node in the XDSM
                function highlight(data)
                {
                    var xPath = data.xPath;
                    var allLinks = d3.selectAll(".link");
                    var allLinks_tmp = allLinks[0];
                    allLinks_tmp.forEach(function(p) {
                        var firstElement_tmp = p.__data__.name.split("/")[1]
                        var xPath_fromFirst = "/"+firstElement_tmp+xPath.split(firstElement_tmp)[1]
                        var p_name_list = p.__data__.name.split(",")
                        var highlight_on = false
                        p_name_list.forEach(function(p_name) 
                        {
                            if (include(p_name+"__",xPath_fromFirst+"__") || include(p_name,xPath_fromFirst+"/"))
                            {
                                highlight_on = true;
                            }
                        })
                        if (highlight_on)
                        {
                            d3.select(p).style("opacity", .8);			
                        }
                        else
                        {
                            d3.select(p).style("opacity", 0);			
                        }
                    });
                }
                
                function showVariableTable(aVariable)
                {											
                    var headLine = "Node Information (" + aVariable.name + ")";
                    var data = [];
                    // render the table(s)
                    data.push({ "name" : "Name", "value" : "\""+aVariable.name+"\"" })
                    data.push({ "name" : "xPath", "value" : aVariable.xPath })
                    if (aVariable.type){data.push({ "name" : "Type", "value" : aVariable.type })}
                    if (aVariable.level){data.push({ "name" : "Level", "value" : aVariable.level })}
                    if (aVariable.children || aVariable._children)
                    {
                        var childrenNum=0;
                        if (aVariable.children){childrenNum=childrenNum+aVariable.children.length}
                        if (aVariable._children){childrenNum=childrenNum+aVariable._children.length}
                        data.push({ "name" : "Number of children", "value" : childrenNum })
                    }
                    if (aVariable.dimension){data.push({ "name" : "Dimension", "value" : aVariable.dimension })}
                    else if(aVariable.dimension===null){data.push({ "name" : "Dimension", "value" : "undefined" })}
                    if (aVariable.value){data.push({ "name" : "Value(s)", "value" : aVariable.value })}
                    
                    var d3_body = d3.select("body");
                    
                    var panel_div = d3_body.append("div").attr("class", "myPanel panel-default")
                    panel_div.append("div").attr("class","panel-heading")
                        .append("div").attr("class","panel_title").append("h4").text(headLine)
                    var listGroup = panel_div.append("div").attr("class","panel-body")
                        .append("table").attr("id","myTable")
                        .append("tbody")
                        
                    data.forEach(function(listElement)
                    {
                        var row = listGroup.append("tr")
                        row.append("td").text(listElement.name)
                        row.append("td").text(listElement.value)
                        
                    })
                    $('.myPanel').lobiPanel({
                        reload: false,
                        editTitle: false,
                        expand: false,
                        unpin: false,
                        resize: "none",
                        minWidth: 200,
                        minHeight: 200,
                        maxWidth: 1100,
                        maxHeight: 1200,
                    });
                    $('.myPanel').lobiPanel('unpin');
                }
                
                //menu --> functions for right click options
                var nodeMenu = [
                {
                    title: 'Show node information',
                    onMouseDown: function(elm, d, i) {
                        showVariableTable(d);
                    },
                    onMouseUp: function(elm, d, i) {},
                    onMouseOver: function(elm, d, i) {},
                    childrenItems: []
                },
                {
                    title: 'Show usage of node in diagram',
                    onMouseDown: function(elm, d, i) {
                        d3.selectAll(".treeFrame").attr("fill-opacity", .5);
                        d3.selectAll(".nodeText").style("fill-opacity", 0.5);					
                        highlight(d);
                    },
                    onMouseUp: function(elm, d, i) {				
                        d3.selectAll(".link").style("opacity",.6)
                        d3.selectAll(".treeFrame").attr("fill-opacity", .8);
                        d3.selectAll(".nodeText").style("fill-opacity", 1);					
                    },
                    onMouseOver: function(elm, d, i) {
                    },
                    childrenItems: []
                },
                {
                    title: 'Copy x-path to clipboard',
                    onMouseDown: function(elm, d, i) {
                        window.prompt("Copy to clipboard: Ctrl+C, Enter", d.xPath);
                        d3.select('.d3-context-menu').style('display', 'none');
                    },
                    onMouseUp: function(elm, d, i) {
                    },
                    onMouseOver: function(elm, d, i) {
                    },
                    childrenItems: []
                }
                ]
				
				var graph = currentGraph.xdsm;
				var theNodes = graph.nodes
				var theLinks = graph.edges
				theLinks.forEach(function (link) {
				  link.source = link.from;
				  link.target = link.to;
				  link.value = link.name.split(",").length
				});
                
				
				//################################################################################################//	
				var headerDiv = sankeyDiagramDiv.append("div").attr("class","panel panel-primary")
				headerDiv.append("div").attr("class","panel-heading text-center")
						.append("h3")
						.attr("class","panel-title")
						.style("font-family","Arial")
						.style("font-size","20pt")
						.text("Sankey Diagram")
				var name_tmp="";
				if (currentGraph.name){name_tmp=currentGraph.name}
				else{name_tmp="Graph " + currentGraph.id}
				headerDiv.append("div").attr("class","panel-body")
					.style("font-family","Arial")
					.style("font-size","16pt")
					.text("Graph name: " + name_tmp)
				headerDiv.append("div").attr("class","panel-body")
					.style("font-family","Arial")
					.style("font-size","16pt")
					.text("Graph description: " + currentGraph.description)
				//################################################################################################//	
				
                //aigner: Switch views between xdsm, edge bundles and sankey diagram
                //################################################################################################//
                var quickMenuDiv = d3.select(".sankeyDiagramDiv").append("div").attr("class","quickMenuDiv")
                    .style("top","-10px")
                    .style("margin-left","10pt")
                var changeViewButton = quickMenuDiv.append("button")
                    .attr("class","btn btn-info")
                    .attr("data-toggle","tooltip")
                    .attr("data-placement","top")
                    .attr("title","Change visualization type")
                    .style("margin-left","10px")
                    .style("margin-bottom","10px")
                changeViewButton.append("span")
                    .attr("class","glyphicon glyphicon-random")
                    .attr("aria-hidden","true")
                changeViewButton.on("mousedown", function()
                {
                    bootbox.hideAll();
                 
                    bootbox.hideAll();
                    bootbox.prompt({
                        closeButton: false,
                        title: "Change visualization type",
                        inputType: 'select',
                        value: '',
                        inputOptions: [
                            {
                                text: 'Please choose...',
                                value: '',
                            },
                            {
                                text: 'XDSM view',
                                value: 'xdsm',
                            },
                            {
                                text: 'Edge Bundles view',
                                value: 'edgeBundles',
                            },
                            {
                                text: 'Sankey Diagram',
                                value: 'sankeyDiagram',
                            }
                        ],
                        callback: function (theView) {                            
                            if (theView == "xdsm")
                            {
                                clearView();
                                xdsm_script(data,currentGraph.id)
                            }
                            else if (theView == "edgeBundles")
                            {
                                clearView();
                                edgeBundles_script(data,currentGraph.id)
                            }
                            else if (theView == "sankeyDiagram")
                            {
                                clearView();
                                sankeyDiagram_script(data,currentGraph.id)
                            }
                            else
                            {}
                        }
                    });
                })
                //################################################################################################//
                
                
				//aigner: Tree option menu to select which kind of tree view the user wants to see
				//#####################################################################//
				function showFullTree(categoryID, categoryDescr)
				{
					var name_tmp = "Full data model tree view; Categorization: " + categoryDescr;
					var emptyArray="";
					var allLinks = d3.selectAll(".link");
					var theSchema = currentGraph.variableSchemes[categoryID];
					createTreeLayout(name_tmp,theSchema,emptyArray,allLinks,nodeMenu);
				}
				
				var childrenItems = [];
				for (var j=0; j< varCategories.length; j++)
				{
					childrenItems.push({title: 'according to ' + varCategories[j].description,
								   varCategory: varCategories[j].name,
								   description: varCategories[j].description,
								   onMouseClick: function(elm, d, i) {showFullTree(d.varCategory,d.description)},
								   onMouseOver: function(elm,d,i){}})
				}
				var treeOptionMenu = [
				{
					title: 'Show variable tree...',
					onMouseDown: function(elm, d, i) {
					},
					onMouseUp: function(elm, d, i) {
					},
					onMouseOver: function(elm, d, i) {
					},
					childrenItems: childrenItems
				}
				]
				//#####################################################################//                
                
                function showList(aTitle,aList,aMenu)
                {
                    if (aList.length != 0)
                    {
                        var d3_body = d3.select("body");
                        
                        var panel_div = d3_body.append("div").attr("class", "myPanel panel-default")
                        panel_div.append("div").attr("class","panel-heading")
                            .append("div").attr("class","panel_title").append("h4").text(aTitle)
                        panel_div.append("input")
                            .attr("id","myInput")
                            .attr("placeholder","Filter search...")
                            .attr("title","Type in a name")
                            .attr("onkeyup","filterSearch()")
                        var listGroup = panel_div.append("div").attr("class","panel-body")
                            .append("table").attr("id","myTable")
                            .append("tbody")
                            
                        var tr = listGroup
                            .selectAll('tr')
                            .data(aList).enter()
                                .append('tr')
                        var td = tr.append("td").html(function(d) { 
                            if (d.xPath){return d.xPath;} 
                            else {return d.name;} 
                        })
                        tr.on('contextmenu', d3.contextMenu(aMenu));
                        $('.myPanel').lobiPanel({
                            reload: false,
                            editTitle: false,
                            expand: false,
                            unpin: false,
                            resize: "none",
                            minWidth: 200,
                            minHeight: 200,
                            maxWidth: 1100,
                            maxHeight: 1200,
                        });
                        $('.myPanel').lobiPanel('unpin');
                        $('.myPanel').lobiPanel('height','5000');
                    }
                }
				
				var graph_menu = "      <nav>"
                          +"          <ul>"
                          +"             <li><a href='#'>Graph inspection <span>&#x25BE;</span></a>"
                          +"                  <ul>"                        
                          +"                      <li><a href='#'>Inspect data model<span>&#x25B8;</span></a>"
                          +"                          <ul class='data_model_view'>"
                          +"                              <li class='tree_view'><a href='#'>Tree view<span>&#x25B8;</span></a></li>"
                          +"                              <li class='list_view'><a href='#'>List view<span>&#x25B8;</span></a></li>"
                          +"                          </ul>"
                          +"                      </li>"
                          +"                  </ul>"
                          +"              </li>"
                          +"          </ul>"
                          +"      </nav>"
                          
				d3.select(".sankeyDiagramDiv").append('div').attr("class","graph_menu").style("margin-left","20px").html(graph_menu);                
                
                
                //aigner: Data Model Tree View Button
				//####################################################################################################################
				var dropdown1 = d3.select(".tree_view")
				var links = dropdown1.append("ul").attr("class","view_options");
				for (var j=0; j< varCategories.length; j++)
				{
					//console.log(varCategories[j])
					var linkLi = links.append("li");
					var linkA = linkLi.append("a")
						.attr("id",j)
						.text(varCategories[j].description)
						.on("mouseover", function(){d3.select(this).style("cursor", "pointer")})
						.on("click", function()
						{		
							showFullTree(varCategories[this.id].name,varCategories[this.id].description)
						})
				}
				//aigner: Set width of the div, so the VISTOMS dropdown (in the top of the page) still works
				//####################################################################################################################
                
                //aigner: Data Model List View Button
				//####################################################################################################################
				//aigner: Function, that shows a list of elements
                function showList(aTitle,aList,aMenu)
                {
                    if (aList.length != 0)
                    {
                        var d3_body = d3.select("body");
                        
                        var panel_div = d3_body.append("div").attr("class", "myPanel panel-default")
                        panel_div.append("div").attr("class","panel-heading")
                            .append("div").attr("class","panel_title").append("h4").text(aTitle)
                        panel_div.append("input")
                            .attr("id","myInput")
                            .attr("placeholder","Filter search...")
                            .attr("title","Type in a name")
                            .attr("onkeyup","filterSearch()")
                        var listGroup = panel_div.append("div").attr("class","panel-body")
                            .append("table").attr("id","myTable")
                            .append("tbody")
                            
                        var tr = listGroup
                            .selectAll('tr')
                            .data(aList).enter()
                                .append('tr')
                        var td = tr.append("td").html(function(d) { 
                            if (d.xPath){return d.xPath;} 
                            else {return d.name;} 
                        })
                        tr.on('contextmenu', d3.contextMenu(aMenu));
                        $('.myPanel').lobiPanel({
                            reload: false,
                            editTitle: false,
                            expand: false,
                            unpin: false,
                            resize: "none",
                            minWidth: 200,
                            minHeight: 200,
                            maxWidth: 1100,
                            maxHeight: 1200,
                        });
                        $('.myPanel').lobiPanel('unpin');
                    }
                }
                
                var dropdownList = d3.select(".list_view")
				var linksList = dropdownList.append("ul").attr("class","view_options");
				for (var j=0; j< varCategories.length; j++)
				{
					//console.log(varCategories[j])
					var linkLi = linksList.append("li");
					var linkA = linkLi.append("a")
						.attr("id",j)
						.text(varCategories[j].description)
						.on("mouseover", function(){d3.select(this).style("cursor", "pointer")})
						.on("click", function()
						{	
                            var variables = JSON.parse(JSON.stringify(currentGraph.variableSchemes[varCategories[this.id].name]));
                            variables.forEach(function(variable)
                            {
                                variable.name = variable.xPath
                                //work around because nodeMenu expexts the data, to have a "data" object inside
                                variable.data = variable
                                variable.data.name = variable.xPath.split("/")[variable.xPath.split("/").length-1]
                            })
                            var title = "List view: full variable set categorized according to " + varCategories[this.id].description
                            showList(title,variables,nodeMenu)
						})
				}
				
                d3.select(".graph_menu").moveToBack()
				d3.select(".quickMenuDiv").moveToBack()
				headerDiv.moveToBack()
				
				
				biHiSankey
				  .nodes(theNodes)
				  .links(theLinks)
				  .initializeNodes(function (node) {
					node.state = node.parent ? "contained" : "collapsed";
				  })
				  .layout(LAYOUT_INTERATIONS);

				disableUserInterractions(2 * TRANSITION_DURATION);
				
				update();
			};
			//#####################################################################//
			startSankeyDiagram(data,graphID);
			
			},{"d3":1}]},{},[2]);
		}
		</script>
</html>
