<!-- For the next release
---
position: 8
title: Anotated Source
---
-->

    """
    Alchemy.js is a graph drawing application for the web.
    Copyright (C) 2014  GraphAlchemist, Inc.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    lets
    """

    class Alchemy
        constructor: () ->
            # Alchemy houses a number modules that can be considered submodules
            @version = "#VERSION#"
            @layout = {}
            @interactions = {}
            @utils = {}
            @visControls = {}
            @styles = {}
            @models = {}
            @drawing = {}
            @editor = {}
            @log = {}
            @currentRelationshipTypes = {}
            @state =
                "interactions": "default"
                "layout": "default"
            
            # node and edge internals
            # It is unadvised to access internals directly.
            # Use alchemy.get.nodes() or alchemy.get.edges() instead.
            
            # alchemy._nodes stores a node object as the value with the unique
            # id specified in the GraphJSON.
            @_nodes = {}

            # alchemy._edges stores an array of edges under every unique id.
            # the edge model is a key value pair where the "key" is either
            # 1) an id provided in the GraphJSON data, or 2) an id generated by
            # Alchemy taking the format of "#{source}-#{target}".  
            # The value is an array of edge 'packets', where the length of the array
            # is typically 1.
            @_edges = {}

        begin: (userConf) =>
            # overide configuration with user inputs
            @setConf(userConf)

            if typeof alchemy.conf.dataSource is 'string'
                d3.json alchemy.conf.dataSource, alchemy.startGraph
            else if typeof alchemy.conf.dataSource is 'object'
                alchemy.startGraph alchemy.conf.dataSource
            @

        setConf: (userConf) -> 
            # apply base themes
            if userConf.theme?
                _.merge alchemy.defaults, alchemy.themes["#{userConf.theme}"]

            # alias British/American colour/color spelling, hopefully temporary
            for key, value of userConf
                if key is "clusterColors" 
                    userConf["clusterColours"] = value
                if key is "backgroundColor"
                    userConf["backgroundColour"] = value
                if key is "nodeColor"
                    userConf["nodeColour"] = value

            @conf = _.merge alchemy.defaults, userConf

        #API methods
        getNodes: (id, ids...) =>
            # returns one or more nodes as an array
            if ids
                ids.push id
                params = _.union ids
                results = []
                for p in params
                    results.push alchemy._nodes[p].properties
                results
            else
                [@_nodes[id].properties]

        getEdges: (id=null, target=null) =>
            # returns one or more edges as an array
            if id? and target?
                edge_id = "#{id}-#{target}"
                edge = @_edges[edge_id]
                [edge.properties]
            else if id? and not target?
                results = _.map @_edges, (edge) -> 
                            if (edge.properties.source is id) or (edge.properties.target is id)
                                edge.properties
                _.compact results

        allNodes: => _.map @_nodes, (n) -> n.properties
        allEdges: => _.map @_edges, (e) -> e.properties

    currentRelationshipTypes = {}

    if typeof module isnt 'undefined' and module.exports
      module.exports = new Alchemy()
    else
      @alchemy = new Alchemy()

    # Alchemy.js is a graph drawing application for the web.
    # Copyright (C) 2014  GraphAlchemist, Inc.

    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Affero General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.

    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU Affero General Public License for more details.

    # You should have received a copy of the GNU Affero General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.

    alchemy.create =
        nodes: (nodeMap, nodeMaps...) ->
                registerNode = (node) ->
                    # check if the node already exists
                    if not alchemy._nodes[node.id]
                        alchemyNode = new alchemy.models.Node(node)
                        alchemy._nodes[node.id] = alchemyNode
                        [alchemyNode]
                    else
                        # if the node already exists, suggest that the user uses a 
                        # different method for creating/updating the node
                        console.warn("""
                                    A node with the id #{node.id} already exists.
                                    Consider using the alchemy.get.nodes() method to 
                                    retrieve the node and then using the Node methods.
                                    """)
                if nodeMaps.length isnt 0
                    nodeMaps.push nodeMap
                    # create the results set
                    results = []
                    for n in nodeMaps
                        # check if the node already exists
                        registerNode n
                    results
                else
                    registerNode nodeMap

        edges: (edgeMap, edgeMaps...) ->
            registerEdge = (edge) ->
                # data provided a unique id and that edge does not yet exist
                if edge.id and not alchemy._edges[edge.id]
                    alchemyEdge = new alchemy.models.Edge(edge)
                    alchemy._edges[edge.id] = [alchemyEdge]
                    [alchemyEdge]
                # data provided a unique id and that edge already exists
                else if edge.id and alchemy._edges[edge.id]
                    console.warn """
                        An edge with that id #{someEdgeMap.id} already exists.
                        Consider using the alchemy.get.edge() method to 
                        retrieve the edge and then using the Edge methods.
                        Note: id's are not required for edges.  Alchemy will create
                        an unlimited number of edges for the same source and target node.
                        Simply omit 'id' when creating the edge.
                        """
                # data did not provide a unique id and so alchemy uses source-target
                else
                    edgeArray = alchemy._edges["#{edge.source}-#{edge.target}"]
                    if edgeArray
                        # edges already exist with this source target, append a new edge object
                        alchemyEdge = new alchemy.models.Edge(edge, edgeArray.length)
                        edgeArray.push alchemyEdge
                        [alchemyEdge]
                    else
                        # edge array does not exist - create the array and give the edge
                        # an id of 'source-target-0' for the first position in the array
                        alchemyEdge = new alchemy.models.Edge(edge, 0)
                        alchemy._edges["#{edge.source}-#{edge.target}"] = [alchemyEdge]
                        [alchemyEdge]
                        
            if edgeMaps.length isnt 0
                console.warn "Make sure this function supports multiple arguments"
            else
                registerEdge edgeMap

    # Alchemy.js is a graph drawing application for the web.
    # Copyright (C) 2014  GraphAlchemist, Inc.

    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Affero General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.

    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU Affero General Public License for more details.

    # You should have received a copy of the GNU Affero General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.

    alchemy.get = 
        # returns one or more nodes as an array
        nodes: (id, ids...) ->
                if id?
                    # All passed ids with artificially enforced type safety
                    allIDs = _.map arguments, (arg) -> String(arg)
                    _.filter alchemy._nodes, (val, key)->
                        val if _.contains allIDs, key
                else
                    console.warn "Please specify a node id."

        edges: (id=null, target=null) ->
            # returns one or more edges as an array
            if id? and target?
                edge_id = "#{id}-#{target}"
                edge = alchemy._edges[edge_id]
                [edge]
            else if id? and not target?
                if alchemy._edges[id]?
                    [_.flatten(alchemy._edges[id])]
                else
                    # edge does not exist, so return all edges with `id` as the 
                    # `source OR `target` this method scans ALL edges....
                    results = _.map alchemy._edges, (edge) ->
                        if (edge.properties.source is id) or (edge.properties.target is id)
                            edge.properties
                _.compact results

        allNodes: (type) ->
            if type?
                _.filter alchemy._nodes, (n) -> n if n._nodeType is type
            else
                _.map alchemy._nodes, (n) -> n

        activeNodes: () ->
            _.filter alchemy._nodes, (node) -> node if node._state is "active"

        allEdges: ->
            _.flatten _.map(alchemy._edges, (edgeArray) -> e for e in edgeArray)
        
        state: (key) -> if alchemy.state.key? then alchemy.state.key

        clusters: ->
            clusterMap = alchemy.layout._clustering.clusterMap
            nodesByCluster = {}
            _.each clusterMap, (key, value) ->
                nodesByCluster[value] = _.select alchemy.get.allNodes(), (node) ->
                    node.getProperties()[alchemy.conf.clusterKey] is value
            nodesByCluster

        clusterColours: ->
            clusterMap = alchemy.layout._clustering.clusterMap
            clusterColoursObject = {}
            _.each clusterMap, (key, value) ->
               clusterColoursObject[value] = alchemy.conf.clusterColours[key % alchemy.conf.clusterColours.length]
            clusterColoursObject


    # Alchemy.js is a graph drawing application for the web.
    # Copyright (C) 2014  GraphAlchemist, Inc.

    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Affero General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.

    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU Affero General Public License for more details.

    # You should have received a copy of the GNU Affero General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.

    alchemy.set =
        state: (key, value) -> alchemy.state.key = value

    # Alchemy.js is a graph drawing application for the web.
    # Copyright (C) 2014  GraphAlchemist, Inc.

    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Affero General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.

    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU Affero General Public License for more details.

    # You should have received a copy of the GNU Affero General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.

    class alchemy.clustering
        constructor: ->
            nodes = alchemy._nodes
            conf = alchemy.conf
            clustering = @

            @clusterKey = conf.clusterKey
            @identifyClusters()
        
            _charge = -500
            _linkStrength = (edge) ->
                sourceCluster = nodes[edge.source.id]._properties[@clusterKey]
                targetCluster = nodes[edge.target.id]._properties[@clusterKey]
                if sourceCluster is targetCluster
                    0.15
                else
                    0
            _friction = () ->
                0.7
            _linkDistancefn = (edge) ->
                nodes = alchemy._nodes
                if nodes[edge.source.id]._properties.root or nodes[edge.target.id]._properties.root
                    300
                else if nodes[edge.source.id]._properties[@clusterKey] is nodes[edge.target.id]._properties[@clusterKey]
                    10
                else
                    600
            _gravity = (k) -> 8 * k

            @layout =
                charge: _charge
                linkStrength: (edge) -> _linkStrength(edge)
                friction: () -> _friction()
                linkDistancefn: (edge) -> _linkDistancefn(edge)
                gravity: (k) -> _gravity(k)

        identifyClusters: ->
            nodes = alchemy.get.allNodes()
            clusters = _.uniq _.map(_.values(nodes), (node)-> node.getProperties()[alchemy.conf.clusterKey])
            @clusterMap = _.zipObject clusters, [0..clusters.length]
        
        getClusterColour: (clusterValue) ->
            # Modulo reuses colors if not enough are supplied
            index = @clusterMap[clusterValue] % alchemy.conf.clusterColours.length
            alchemy.conf.clusterColours[index]

        edgeGradient: (edges) ->
            defs = alchemy.vis.select "#{alchemy.conf.divSelector} svg"
            Q = {}
            nodes = alchemy._nodes
            for edge in _.map(edges, (edge) -> edge._d3)
                # skip root
                continue if nodes[edge.source.id]._properties.root or nodes[edge.target.id]._properties.root
                # skip nodes from the same cluster
                continue if nodes[edge.source.id]._properties[@clusterKey] is nodes[edge.target.id]._properties[@clusterKey]
                if nodes[edge.target.id]._properties[@clusterKey] isnt nodes[edge.source.id]._properties[@clusterKey]
                    # gradient `id`
                    id = nodes[edge.source.id]._properties[@clusterKey] + "-" + nodes[edge.target.id]._properties[@clusterKey]
                    if id of Q
                        continue
                    else if id not of Q
                        startColour = @getClusterColour(nodes[edge.target.id]._properties[@clusterKey])
                        endColour = @getClusterColour(nodes[edge.source.id]._properties[@clusterKey])
                        Q[id] = {'startColour': startColour,'endColour': endColour}
            for ids of Q
                gradient_id = "cluster-gradient-" + ids
                gradient = defs.append("svg:linearGradient").attr("id", gradient_id)
                gradient.append("svg:stop").attr("offset", "0%").attr "stop-color", Q[ids]['startColour']
                gradient.append("svg:stop").attr("offset", "100%").attr "stop-color", Q[ids]['endColour']
        
    alchemy.clusterControls =
        init: ()->
            changeClusterHTML = """
                                <input class='form-control form-inline' id='cluster-key' placeholder="Cluster Key"></input>
                                """
            alchemy.dash
                   .select "#clustering-container"
                   .append "div"
                   .attr "id", "cluster-key-container"
                   .attr 'class', 'property form-inline form-group'
                   .html changeClusterHTML
                   .style "display", "none"
                
            alchemy.dash
                   .select "#cluster_control_header"
                   .on "click", ()->
                        element = alchemy.dash.select "#cluster-key-container"
                        display = element.style "display"

                element.style "display", (e)-> if display is "block" then "none" else "block"

                if alchemy.dash.select("#cluster-key-container").style("display") is "none"
                    alchemy.dash
                           .select "#cluster-arrow"
                           .attr "class", "fa fa-2x fa-caret-right"
                else 
                    alchemy.dash
                           .select "#cluster-arrow"
                           .attr "class", "fa fa-2x fa-caret-down"
            
            alchemy.dash
                .select "#cluster-key"
                .on "keydown", -> 
                    if d3.event.keyIdentifier is "Enter"
                        alchemy.conf.cluster = true
                        alchemy.conf.clusterKey = this.value
                        alchemy.generateLayout()

    # Alchemy.js is a graph drawing application for the web.
    # Copyright (C) 2014  GraphAlchemist, Inc.

    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Affero General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.

    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU Affero General Public License for more details.

    # You should have received a copy of the GNU Affero General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.

    alchemy.controlDash = 
        init: ->
            if @dashIsShown()
                divSelector = alchemy.conf.divSelector
                # add dashboard wrapper
                alchemy.dash = d3.select "#{divSelector}"
                                 .append "div"
                                 .attr "id", "control-dash-wrapper"
                                 .attr "class", "col-md-4 initial"

                # add the dash toggle button 
                alchemy.dash
                       .append "i"
                       .attr "id", "dash-toggle"
                       .attr "class", "fa fa-flask col-md-offset-12"

                # add the control dash
                alchemy.dash
                       .append "div"
                       .attr "id", "control-dash"
                       .attr "class", "col-md-12"

                alchemy.dash.select '#dash-toggle'
                       .on 'click', alchemy.interactions.toggleControlDash

                alchemy.controlDash.zoomCtrl()
                alchemy.controlDash.search()
                alchemy.controlDash.filters()
                alchemy.controlDash.stats()
                alchemy.controlDash.clustering()

        search: ->
            if alchemy.conf.search
                alchemy.dash
                       .select "#control-dash"
                       .append "div"
                       .attr "id", "search"
                       .html """
                            <div class='input-group'>
                                <input class='form-control' placeholder='Search'>
                                <i class='input-group-addon search-icon'><span class='fa fa-search fa-1x'></span></i>
                            </div> 
                              """
                alchemy.search.init()
        
        zoomCtrl: ->
            if alchemy.conf.zoomControls 
                alchemy.dash
                    .select "#control-dash-wrapper"
                    .append "div"
                    .attr "id", "zoom-controls"
                    .attr "class", "col-md-offset-12"
                    .html "<button id='zoom-reset'  class='btn btn-defualt btn-primary'><i class='fa fa-crosshairs fa-lg'></i></button>
                            <button id='zoom-in'  class='btn btn-defualt btn-primary'><i class='fa fa-plus'></i></button>
                            <button id='zoom-out' class='btn btn-default btn-primary'><i class='fa fa-minus'></i></button>"
                
                alchemy.dash
                       .select '#zoom-in'
                       .on "click", -> alchemy.interactions.clickZoom 'in'
                
                alchemy.dash
                       .select '#zoom-out'
                       .on "click", -> alchemy.interactions.clickZoom 'out'
                
                alchemy.dash
                       .select '#zoom-reset'
                       .on "click", -> alchemy.interactions.clickZoom 'reset'

        filters: ->
            if alchemy.conf.nodeFilters or alchemy.conf.edgeFilters
                alchemy.dash
                    .select "#control-dash"
                    .append "div"
                    .attr "id", "filters"
                alchemy.filters.init()

        stats: ->
            if alchemy.conf.nodeStats or alchemy.conf.edgeStats
                stats_html = """
                        <div id = "stats-header" data-toggle="collapse" data-target="#stats #all-stats">
                        <h3>
                            Statistics
                        </h3>
                        <span class = "fa fa-caret-right fa-2x"></span>
                        </div>
                        <div id="all-stats" class="collapse">
                            <ul class = "list-group" id="node-stats"></ul>
                            <ul class = "list-group" id="rel-stats"></ul>  
                        </div>
                    """

                alchemy.dash
                    .select "#control-dash"
                    .append "div"
                    .attr "id", "stats"
                    .html stats_html
                    .select '#stats-header'
                    .on 'click', () ->
                        if alchemy.dash.select('#all-stats').classed "in"
                            alchemy.dash
                                   .select "#stats-header>span"
                                   .attr "class", "fa fa-2x fa-caret-right"
                        else
                            alchemy.dash
                                   .select "#stats-header>span"
                                   .attr "class", "fa fa-2x fa-caret-down"

                alchemy.stats.init()

        clustering: ->
            if alchemy.conf.clusterControl
                clusterControl_html = """
                        <div id="clustering-container">
                            <div id="cluster_control_header" data-toggle="collapse" data-target="#clustering #cluster-options">
                                 <h3>Clustering</h3>
                                <span id="cluster-arrow" class="fa fa-2x fa-caret-right"></span>
                            </div>
                        </div>
                        """
                alchemy.dash
                    .select "#control-dash"
                    .append "div"
                    .attr "id", "clustering"
                    .html clusterControl_html
                    .select '#cluster_control_header'

                alchemy.clusterControls.init()

        dashIsShown: ->
            conf = alchemy.conf

            conf.showEditor    || conf.captionToggle  || conf.toggleRootNodes ||
            conf.removeElement || conf.clusterControl || conf.nodeStats       ||
            conf.edgeStats     || conf.edgeFilters    || conf.nodeFilters     || 
            conf.edgesToggle   || conf.nodesToggle    || conf.search
    # Alchemy.js is a graph drawing application for the web.
    # Copyright (C) 2014  GraphAlchemist, Inc.

    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Affero General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.

    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU Affero General Public License for more details.

    # You should have received a copy of the GNU Affero General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.
    alchemy.filters =
        init: () ->
            alchemy.filters.show()

            if alchemy.conf.edgeFilters then alchemy.filters.showEdgeFilters()
            if alchemy.conf.nodeFilters then alchemy.filters.showNodeFilters()
            #generate filter forms
            if alchemy.conf.nodeTypes
                nodeKey = Object.keys alchemy.conf.nodeTypes

                nodeTypes = ''
                for nodeType in alchemy.conf.nodeTypes[nodeKey]
                    # Create Filter list element
                    caption = nodeType.replace '_', ' '
                    nodeTypes += "<li class='list-group-item nodeType' role='menuitem' id='li-#{nodeType}' name=#{nodeType}>#{caption}</li>"
                alchemy.dash.select '#node-dropdown'
                       .html nodeTypes

            if alchemy.conf.edgeTypes
                for e in alchemy.dash.selectAll(".edge")[0]
                    alchemy.currentRelationshipTypes[[e].caption] = true

                edgeTypes = ''
                for edgeType in alchemy.conf.edgeTypes
                    # Create Filter list element
                    caption = edgeType.replace '_', ' '
                    edgeTypes += "<li class='list-group-item edgeType' role='menuitem' id='li-#{edgeType}' name=#{edgeType}>#{caption}</li>"
                alchemy.dash.select '#rel-dropdown'
                       .html edgeTypes

            if alchemy.conf.captionsToggle then alchemy.filters.captionsToggle()
            if alchemy.conf.edgesToggle then alchemy.filters.edgesToggle()
            if alchemy.conf.nodesToggle then alchemy.filters.nodesToggle()
            alchemy.filters.update()

        show: ->
            filter_html = """
                        <div id = "filter-header" data-toggle="collapse" data-target="#filters form">
                            <h3>Filters</h3>
                            <span class = "fa fa-2x fa-caret-right"></span>
                        </div>
                            <form class="form-inline collapse">
                            </form>
                          """
            alchemy.dash.select('#control-dash #filters').html filter_html
            alchemy.dash.selectAll '#filter-header'
                .on 'click', () ->
                    if alchemy.dash.select('#filters>form').classed "in"
                        alchemy.dash.select "#filter-header>span"
                               .attr "class", "fa fa-2x fa-caret-right"
                    else
                        alchemy.dash.select "#filter-header>span"
                               .attr "class", "fa fa-2x fa-caret-down"

            alchemy.dash.select '#filters form'
                   # .submit false

        #create relationship filters
        showEdgeFilters: () ->
            rel_filter_html = """
                            <div id="filter-rel-header" data-target = "#rel-dropdown" data-toggle="collapse">
                                <h4>
                                    Edge Types
                                </h4>
                                <span class="fa fa-lg fa-caret-right"></span>
                            </div>
                            <ul id="rel-dropdown" class="collapse list-group" role="menu">
                            </ul>
                               """
            alchemy.dash.select '#filters form'
                   .append "div"
                   .attr "id", "filter-relationships"
                   .html rel_filter_html
            alchemy.dash.select "#filter-rel-header"
                .on 'click', () ->
                    if alchemy.dash.select('#rel-dropdown').classed "in"
                        alchemy.dash.select "#filter-rel-header>span"
                               .attr "class", "fa fa-lg fa-caret-right"
                    else
                        alchemy.dash.select "#filter-rel-header>span"
                               .attr "class", "fa fa-lg fa-caret-down"

        #create node filters
        showNodeFilters: () ->
            node_filter_html = """
                                <div id="filter-node-header" data-target = "#node-dropdown" data-toggle="collapse">
                                    <h4>
                                        Node Types
                                    </h4>
                                    <span class="fa fa-lg fa-caret-right"></span>
                                </div>
                                <ul id="node-dropdown" class="collapse list-group" role="menu">
                                </ul>
                               """
            alchemy.dash.select '#filters form'
                   .append "div"
                   .attr "id", "filter-nodes"
                   .html node_filter_html
            alchemy.dash.select "#filter-node-header"
                .on 'click', () ->
                    if alchemy.dash.select('#node-dropdown').classed "in"
                        alchemy.dash.select "#filter-node-header>span"
                               .attr "class", "fa fa-lg fa-caret-right"
                    else
                        alchemy.dash.select "#filter-node-header>span"
                               .attr "class", "fa fa-lg fa-caret-down"

        #create captions toggle
        captionsToggle: () ->
            alchemy.dash.select "#filters form"
              .append "li"
              .attr {"id":"toggle-captions","class":"list-group-item active-label toggle"}
              .html "Show Captions"
              .on "click", ->
                isDisplayed = alchemy.dash.select("g text").attr("style")

                if isDisplayed is "display: block" || null
                    alchemy.dash.selectAll "g text"
                           .attr "style", "display: none"
                else
                    alchemy.dash.selectAll "g text"
                           .attr "style", "display: block"

        #create edges toggle
        edgesToggle: () ->
            alchemy.dash.select "#filters form"
              .append "li"
              .attr {"id":"toggle-edges","class":"list-group-item active-label toggle"}
              .html "Toggle Edges"
              .on "click", ->
                  if _.contains(_.pluck(_.flatten(_.values(alchemy._edges)), "_state"), "active")
                    _.each _.values(alchemy._edges), (edges)->
                        _.each edges, (e)-> if e._state is "active" then e.toggleHidden()
                  else
                    _.each _.values(alchemy._edges), (edges)->
                        _.each edges, (e)->
                            source = alchemy._nodes[e._properties.source]
                            target = alchemy._nodes[e._properties.target]
                            if source._state is "active" and target._state is "active"
                              e.toggleHidden()

        #create nodes toggle
        nodesToggle: () ->
            alchemy.dash.select "#filters form"
              .append "li"
              .attr {"id":"toggle-nodes","class":"list-group-item active-label toggle"}
              .html "Toggle Nodes"
              .on "click", ->
                  if _.contains(_.pluck(_.values(alchemy._nodes), "_state"), "active")
                    _.each _.values(alchemy._nodes), (n)->
                        if alchemy.conf.toggleRootNodes and n._d3.root then return
                        if n._state is "active" then n.toggleHidden()
                  else
                    _.each _.values(alchemy._nodes), (n)->
                        if alchemy.conf.toggleRootNodes and n._d3.root then return
                        n.toggleHidden()

        #update filters
        update: () ->
            alchemy.dash.selectAll ".nodeType, .edgeType"
                .on "click", () ->
                    element = d3.select this
                    tag = element.attr "name"
                    alchemy.vis.selectAll ".#{tag}"
                        .each (d)->
                            if alchemy._nodes[d.id]?
                                node = alchemy._nodes[d.id]
                                node.toggleHidden()
                            else
                                edge = alchemy._edges[d.id][0]
                                source = alchemy._nodes[edge._properties.source]
                                target = alchemy._nodes[edge._properties.target]
                                if source._state is "active" and target._state is "active"
                                  edge.toggleHidden()
                    alchemy.stats.nodeStats()

    # Alchemy.js is a graph drawing application for the web.
    # Copyright (C) 2014  GraphAlchemist, Inc.

    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Affero General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.

    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU Affero General Public License for more details.

    # You should have received a copy of the GNU Affero General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.

    alchemy.interactions =
        edgeClick: (d) ->
            d3.event.stopPropagation()
            edge = alchemy._edges[d.id][d.pos]

            if edge._state != "hidden"
                edge._state = do -> 
                    return "active" if edge._state is "selected"
                    "selected"
                edge.setStyles()
            if typeof alchemy.conf.edgeClick? is 'function'
                alchemy.conf.edgeClick()

        edgeMouseOver: (d) ->
            edge = alchemy._edges[d.id][d.pos]
            if edge._state != "hidden"
                if edge._state != "selected"
                    edge._state = "highlighted"
                edge.setStyles()

        edgeMouseOut: (d) ->
            edge = alchemy._edges[d.id][d.pos]
            if edge._state != "hidden"
                if edge._state != "selected"
                    edge._state = "active"
                edge.setStyles()

        nodeMouseOver: (n) ->
            node = alchemy._nodes[n.id]
            if node._state != "hidden"
                if node._state != "selected"
                    node._state = "highlighted"
                    node.setStyles()
                if typeof alchemy.conf.nodeMouseOver is 'function'
                    alchemy.conf.nodeMouseOver(node)
                else if typeof alchemy.conf.nodeMouseOver is ('number' or 'string')
                    # the user provided an integer or string to be used
                    # as a data lookup key on the node in the graph json
                    node.properties[alchemy.conf.nodeMouseOver]

        nodeMouseOut: (n) ->
            node = alchemy._nodes[n.id]
            if node._state != "hidden"
                if node._state != "selected"
                    node._state = "active"
                    node.setStyles()
                if alchemy.conf.nodeMouseOut? and typeof alchemy.conf.nodeMouseOut is 'function'
                    alchemy.conf.nodeMouseOut(n)

        nodeClick: (n) ->
            # Don't consider drag a click
            return if d3.event.defaultPrevented

            d3.event.stopPropagation()
            node = alchemy._nodes[n.id]

            if node._state != "hidden"
                node._state = do -> 
                    return "active" if node._state is "selected"
                    "selected"
                node.setStyles()
            if typeof alchemy.conf.nodeClick is 'function'
                alchemy.conf.nodeClick(n)

        zoom: (extent) ->
                    if not @._zoomBehavior?
                        @._zoomBehavior = d3.behavior.zoom()
                    @._zoomBehavior.scaleExtent extent
                                    .on "zoom", ->
                                        alchemy.vis.attr("transform", "translate(#{ d3.event.translate }) 
                                                                    scale(#{ d3.event.scale })" )
                                        
        clickZoom:  (direction) ->
                        [x, y, scale] = alchemy.vis
                                               .attr "transform"
                                               .match /(-*\d+\.*\d*)/g
                                               .map (a) -> parseFloat(a)

                        alchemy.vis
                            .attr "transform", ->
                                if direction is "in"
                                    scale += 0.2 if scale < alchemy.conf.scaleExtent[1]
                                    return "translate(#{x},#{y}) scale(#{ scale })"
                                else if direction is "out"
                                    scale -= 0.2 if scale > alchemy.conf.scaleExtent[0]
                                    return "translate(#{x},#{y}) scale(#{ scale })"
                                else if direction is "reset"
                                    return "translate(0,0) scale(1)"
                                else
                                    console.log 'error'
                        if not @._zoomBehavior?
                            @._zoomBehavior = d3.behavior.zoom()
                        @._zoomBehavior.scale(scale)
                                       .translate([x,y])

        toggleControlDash: () ->
            #toggle off-canvas class on click
            offCanvas = alchemy.dash.classed("off-canvas") or
                        alchemy.dash.classed("initial")
            alchemy.dash
                   .classed {
                        "off-canvas": !offCanvas,
                        "initial"   : false,
                        "on-canvas" : offCanvas
                    }

        nodeDragStarted: (d, i) ->
            d3.event.preventDefault
            d3.event.sourceEvent.stopPropagation()
            d3.select(@).classed "dragging", true
            d.fixed = true

        nodeDragged: (d, i) ->
            d.x += d3.event.dx
            d.y += d3.event.dy
            d.px += d3.event.dx
            d.py += d3.event.dy

            node = d3.select @
            node.attr "transform", "translate(#{d.x}, #{d.y})"
            edgeIDs = alchemy._nodes[d.id]._adjacentEdges
            for id in edgeIDs
                selection = alchemy.vis.select "#edge-#{id}"
                alchemy._drawEdges.updateEdge selection.data()[0]

        nodeDragended: (d, i) ->
            d3.select(@).classed "dragging": false
            if !alchemy.conf.forceLocked  #alchemy.configuration for forceLocked
                alchemy.force.start() #restarts force on drag

        deselectAll: () ->
            # this function is also fired at the end of a drag, do nothing if this
            if d3.event?.defaultPrevented then return
            if alchemy.conf.showEditor is true
                alchemy.modifyElements.nodeEditorClear()
            
            _.each alchemy._nodes, (n)->
                n._state = "active"
                n.setStyles()
            
            _.each alchemy._edges, (edge)->
                _.each edge, (e)->
                    e._state = "active"
                    e.setStyles()
            
            # call user-specified deselect function if specified
            if alchemy.conf.deselectAll and typeof(alchemy.conf.deselectAll is 'function')
                alchemy.conf.deselectAll()

    # Alchemy.js is a graph drawing application for the web.
    # Copyright (C) 2014  GraphAlchemist, Inc.

    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Affero General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.

    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU Affero General Public License for more details.

    # You should have received a copy of the GNU Affero General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.

    class alchemy.Layout
        constructor: ->
            conf = alchemy.conf
            nodes = alchemy._nodes
            @k = Math.sqrt Math.log(_.size(alchemy._nodes)) / (conf.graphWidth() * conf.graphHeight())
            @_clustering = new alchemy.clustering
            @d3NodeInternals = _.map alchemy._nodes, (v,k)-> v._d3

            if conf.cluster
                @_charge = () -> @_clustering.layout.charge
                @_linkStrength = (edge) -> @_clustering.layout.linkStrength(edge)
            else
                @_charge = () -> -10 / @k
                @_linkStrength = (edge) ->
                    if nodes[edge.source.id].getProperties('root') or nodes[edge.target.id].getProperties('root')
                        1
                    else
                        0.9

            if conf.cluster
                @_linkDistancefn = (edge) -> @_clustering.layout.linkDistancefn(edge)
            else if conf.linkDistancefn is 'default'
                @_linkDistancefn = (edge) ->
                    1 / (@k * 50)
            else if typeof conf.linkDistancefn is 'number'
                @_linkDistancefn = (edge) -> conf.linkDistancefn
            else if typeof conf.linkDistancefn is 'function'
                @_linkDistancefn = (edge) -> conf.linkDistancefn(edge)

            

        gravity: () =>
            if alchemy.conf.cluster
                @_clustering.layout.gravity @k
            else
                50 * @k

        linkStrength: (edge) =>
            @_linkStrength edge

        friction: () -> 0.9

        collide: (node) ->
            conf = alchemy.conf
            r = 2 * (node.radius + node['stroke-width']) + conf.nodeOverlap
            nx1 = node.x - r
            nx2 = node.x + r
            ny1 = node.y - r
            ny2 = node.y + r
            return (quad, x1, y1, x2, y2) ->
                if quad.point and (quad.point isnt node)
                    x = node.x - Math.abs quad.point.x
                    y = node.y - quad.point.y
                    l = Math.sqrt(x * x + y * y)
                    r = r
                    if l < r
                        l = (l - r) / l * alchemy.conf.alpha
                        node.x -= x *= l
                        node.y -= y *= l
                        quad.point.x += x
                        quad.point.y += y
                x1 > nx2 or
                x2 < nx1 or
                y1 > ny2 or
                y2 < ny1

        tick: () =>
            if alchemy.conf.collisionDetectionls
                q = d3.geom.quadtree @d3NodeInternals
                for node in @d3NodeInternals
                    q.visit @collide(node)

            # alchemy.node
            alchemy.vis
                .selectAll "g.node"
                .attr "transform", (d) -> "translate(#{d.x},#{d.y})"

            edges = alchemy.vis.selectAll "g.edge"
            @drawEdge = alchemy.drawing.DrawEdge
            @drawEdge.styleText edges
            @drawEdge.styleLink edges

        positionRootNodes: () ->
            conf = alchemy.conf
            container =
                width: conf.graphWidth()
                height: conf.graphHeight()

            rootNodes = _.filter alchemy.get.allNodes(), (node) -> node.getProperties('root')
            # if there is one root node, position it in the center
            if rootNodes.length is 1
                n = rootNodes[0]
                [n._d3.x, n._d3.px] = [container.width / 2, container.width / 2]
                [n._d3.y, n._d3.py] = [container.height/ 2, container.height/ 2]
                # fix root nodes until force layout is complete
                n._d3.fixed = true
                return
            # position nodes towards center of graph
            else
                for n, i in rootNodes
                    n._d3.x = container.width / Math.sqrt(rootNodes.length * (i+1))
                    n._d3.y = container.height / 2
                    n._d3.fixed = true

        chargeDistance: () ->
            500

        linkDistancefn: (edge) =>
            @_linkDistancefn edge

        charge: () ->
            @_charge()

    alchemy.generateLayout = (start=false)->
        conf = alchemy.conf

        alchemy.layout = new alchemy.Layout
        alchemy.force = d3.layout.force()
            .size [conf.graphWidth(), conf.graphHeight()]
            .nodes _.map(alchemy._nodes, (node) -> node._d3)
            .links _.flatten(_.map(alchemy._edges, (edgeArray) -> e._d3 for e in edgeArray))

        alchemy.force
            .charge alchemy.layout.charge()
            .linkDistance (link) -> alchemy.layout.linkDistancefn(link)
            .theta 1.0
            .gravity alchemy.layout.gravity()
            .linkStrength (link) -> alchemy.layout.linkStrength(link)
            .friction alchemy.layout.friction()
            .chargeDistance alchemy.layout.chargeDistance()

    # Alchemy.js is a graph drawing application for the web.
    # Copyright (C) 2014  GraphAlchemist, Inc.

    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Affero General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.

    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU Affero General Public License for more details.

    # You should have received a copy of the GNU Affero General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.

    alchemy.search = 
        init: () ->
            searchBox = alchemy.dash
                               .select "#search input"

            searchBox.on "keyup", ()->
                input = searchBox[0][0].value.toLowerCase()

                alchemy.vis
                       .selectAll ".node"
                       .classed "inactive", false
                alchemy.vis
                       .selectAll "text"
                       .attr "style", -> "display: inline;" if input != ""
                alchemy.vis
                       .selectAll ".node"
                       .classed "inactive", (node) ->
                           DOMtext = d3.select @
                                       .text()
       
                           switch alchemy.conf.searchMethod
                               when 'contains'
                                   hidden = DOMtext.toLowerCase().indexOf(input) < 0
                               when 'begins'
                                   hidden = DOMtext.toLowerCase().indexOf(input) != 0
       
                           if hidden
                               alchemy.vis
                                      .selectAll "[source-target*='#{node.id}']"
                                      .classed "inactive", hidden
                           else
                               alchemy.vis
                                      .selectAll "[source-target*='#{node.id}']"
                                      .classed "inactive", (edge)-> 
                                           nodeIDs = [edge.source.id, edge.target.id]
                                           
                                           sourceHidden = alchemy.vis.select("#node-#{nodeIDs[0]}").classed "inactive"
                                           targetHidden = alchemy.vis.select("#node-#{nodeIDs[1]}").classed "inactive"
                                           
                                           targetHidden or sourceHidden
                           hidden
    # Alchemy.js is a graph drawing application for the web.
    # Copyright (C) 2014  GraphAlchemist, Inc.

    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Affero General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.

    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU Affero General Public License for more details.

    # You should have received a copy of the GNU Affero General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.

    alchemy.startGraph = (data) =>
        conf = alchemy.conf

        if d3.select(conf.divSelector).empty()
            console.warn alchemy.utils.warnings.divWarning()

        # see if data is ok
        if not data
            alchemy.utils.warnings.dataWarning()

        # create nodes map and update links
        alchemy.create.nodes.apply @, data.nodes

        data.edges.forEach (e) ->
            alchemy.create.edges e

        # create SVG
        alchemy.vis = d3.select conf.divSelector
            .attr "style", "width:#{conf.graphWidth()}px; height:#{conf.graphHeight()}px; background:#{conf.backgroundColour}"
            .append "svg"
                .attr "xmlns", "http://www.w3.org/2000/svg"
                .attr "xlink", "http://www.w3.org/1999/xlink"
                .attr "pointer-events", "all"
                .on 'click', alchemy.interactions.deselectAll
                .call alchemy.interactions.zoom(conf.scaleExtent)
                .on "dblclick.zoom", null
                .append 'g'
                    .attr "transform","translate(#{conf.initialTranslate}) scale(#{conf.initialScale})"
        
        # Create zoom event handlers
        alchemy.interactions.zoom().scale conf.initialScale
        alchemy.interactions.zoom().translate conf.initialTranslate

        alchemy.generateLayout()
        alchemy.controlDash.init()

        #enter/exit nodes/edges
        d3Edges = _.flatten _.map(alchemy._edges, (edgeArray) -> e._d3 for e in edgeArray)
        d3Nodes = _.map alchemy._nodes, (n) -> n._d3

        # if start
        alchemy.layout.positionRootNodes()
        alchemy.force.start()
        while alchemy.force.alpha() > 0.005
            alchemy.force.tick()

        alchemy._drawEdges = alchemy.drawing.DrawEdges
        alchemy._drawEdges.createEdge d3Edges
        alchemy._drawNodes = alchemy.drawing.DrawNodes
        alchemy._drawNodes.createNode d3Nodes

        initialComputationDone = true
        console.log Date() + ' completed initial computation'

        nodes = alchemy.vis.selectAll 'g.node'
                        .attr 'transform', (id, i) -> "translate(#{id.x}, #{id.y})"

        # configuration for forceLocked
        if !conf.forceLocked
            alchemy.force
                    .on "tick", alchemy.layout.tick
                    .start()

        # call user-specified functions after load function if specified
        # deprecate?
        if conf.afterLoad?
            if typeof conf.afterLoad is 'function'
                conf.afterLoad()
            else if typeof conf.afterLoad is 'string'
                alchemy[conf.afterLoad] = true

        if conf.cluster or conf.directedEdges
            defs = d3.select("#{alchemy.conf.divSelector} svg").append "svg:defs"

        if conf.directedEdges
            arrowSize = conf.edgeArrowSize + (conf.edgeWidth() * 2)
            marker = defs.append "svg:marker"
                .attr "id", "arrow"
                .attr "viewBox", "0 -#{arrowSize * 0.4} #{arrowSize} #{arrowSize}"
                .attr 'markerUnits', 'userSpaceOnUse'
                .attr "markerWidth", arrowSize
                .attr "markerHeight", arrowSize
                .attr "orient", "auto"
            marker.append "svg:path"
                .attr "d", "M #{arrowSize},0 L 0,#{arrowSize * 0.4} L 0,-#{arrowSize * 0.4}"
            if conf.curvedEdges
                marker.attr "refX", arrowSize + 1
            else
                marker.attr 'refX', 1 

        if conf.nodeStats
            alchemy.stats.nodeStats()

        if conf.showEditor
            editor = new alchemy.editor.Editor
            editorInteractions = new alchemy.editor.Interactions
            d3.select "body"
                .on 'keydown', editorInteractions.deleteSelected

            editor.startEditor()

    # Alchemy.js is a graph drawing application for the web.
    # Copyright (C) 2014  GraphAlchemist, Inc.

    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Affero General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.

    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU Affero General Public License for more details.

    # You should have received a copy of the GNU Affero General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.

    alchemy.stats = 
        init: () -> 
            alchemy.stats.update()

        nodeStats: () ->
            #general node stats
            nodeStats = ''
            nodeData = []

            allNodes = alchemy.get.allNodes().length
            activeNodes = alchemy.get.activeNodes().length
            inactiveNodes = allNodes - activeNodes

            nodeStats += "<li class = 'list-group-item gen_node_stat'>Number of nodes: <span class='badge'>#{allNodes}</span></li>"            
            nodeStats += "<li class = 'list-group-item gen_node_stat'>Number of active nodes: <span class='badge'>#{activeNodes}</span></li>"
            nodeStats += "<li class = 'list-group-item gen_node_stat'>Number of inactive nodes: <span class='badge'>#{inactiveNodes}</span></li>"

            #add stats for all node types
            if alchemy.conf.nodeTypes
                nodeKeys = Object.keys(alchemy.conf.nodeTypes)
                nodeTypes = ''
                for nodeType in alchemy.conf.nodeTypes[nodeKeys]
                    caption = nodeType.replace('_', ' ')
                    nodeNum = alchemy.vis.selectAll("g.node.#{nodeType}")[0].length
                    nodeTypes += "<li class = 'list-group-item nodeType' id='li-#{nodeType}' 
                                    name = #{caption}>Number of <strong style='text-transform: uppercase'>#{caption}</strong> nodes: <span class='badge'>#{nodeNum}</span></li>"
                    nodeData.push(["#{nodeType}", nodeNum])
                nodeStats += nodeTypes

            #add the graph
            nodeGraph = "<li id='node-stats-graph' class='list-group-item'></li>" 
            nodeStats += nodeGraph
            alchemy.dash
                   .select '#node-stats'
                   .html nodeStats
            @insertSVG "node", nodeData

        edgeStats: () ->
            #general edge stats
            edgeData = null
            edgeNum = alchemy.vis.selectAll(".edge")[0].length
            activeEdges = alchemy.vis.selectAll(".edge.active")[0].length
            inactiveEdges = alchemy.vis.selectAll(".edge.inactive")[0].length

            edgeGraph = "<li class = 'list-group-item gen_edge_stat'>Number of relationships: <span class='badge'>#{edgeNum}</span></li>
                        <li class = 'list-group-item gen_edge_stat'>Number of active relationships: <span class='badge'>#{activeEdges}</span></li>
                        <li class = 'list-group-item gen_edge_stat'>Number of inactive relationships: <span class='badge'>#{inactiveEdges}</span></li>
                        <li id='edge-stats-graph' class='list-group-item'></li>"

            #add stats for edge types
            if alchemy.conf.edgeTypes
                edgeData = []
                for e in alchemy.vis.selectAll(".edge")[0]
                    alchemy.currentRelationshipTypes[[e].caption] = true

                for edgeType in alchemy.conf.edgeTypes
                    if not edgeType then continue
                    caption = edgeType.replace('_', ' ')
                    edgeNum = alchemy.vis.selectAll(".edge.#{edgeType}")[0].length
                    edgeData.push(["#{caption}", edgeNum])

            alchemy.dash
                   .select '#rel-stats'
                   .html edgeGraph 
            alchemy.stats.insertSVG "edge", edgeData
            return edgeData

        insertSVG: (element, data) ->
            if data is null 
                alchemy.dash
                       .select "##{element}-stats-graph"
                       .html "<br><h4 class='no-data'>There are no #{element}Types listed in your conf.</h4>"
            else
                width = alchemy.conf.graphWidth() * .25
                height = 250
                radius = width / 4
                color = d3.scale.category20()

                arc = d3.svg.arc()
                    .outerRadius(radius - 10)
                    .innerRadius(radius/2)

                pie = d3.layout.pie()
                    .sort(null)
                    .value((d) -> d[1])

                svg = alchemy.dash
                             .select "##{element}-stats-graph"
                             .append "svg"
                             .append "g"
                             .style {"width": width, "height":height}
                             .attr "transform", "translate(" + width/2 + "," + height/2 + ")"

                arcs = svg.selectAll ".arc"
                    .data pie(data)
                    .enter()
                    .append "g"
                    .classed "arc", true
                    .on "mouseover", (d,i) -> 
                        alchemy.dash
                          .select "##{data[i][0]}-stat"
                          .classed "hidden", false
                    .on "mouseout", (d,i) -> 
                        alchemy.dash
                          .select "##{data[i][0]}-stat"
                          .classed "hidden", true

                arcs.append "path"
                    .attr "d", arc
                    .attr "stroke", (d, i) -> color(i)
                    .attr "stroke-width", 2
                    .attr "fill-opacity", "0.3"

                arcs.append "text"
                    .attr "transform", (d) -> "translate(" + arc.centroid(d) + ")"
                    .attr "id", (d, i)-> "#{data[i][0]}-stat"
                    .attr "dy", ".35em"
                    .classed "hidden", true
                    .text (d, i) -> data[i][0]

        update: () -> 
            if alchemy.conf.nodeStats then alchemy.stats.nodeStats()
            if alchemy.conf.edgeStats then alchemy.stats.edgeStats()
    # Alchemy.js is a graph drawing application for the web.
    # Copyright (C) 2014  GraphAlchemist, Inc.

    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Affero General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.

    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU Affero General Public License for more details.

    # You should have received a copy of the GNU Affero General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.

    alchemy.defaults =

        # Renderer
        renderer: "svg"

        # Layout
        graphWidth: ->
            d3.select(@divSelector).node().parentElement.clientWidth
        graphHeight: ->
            if d3.select(@divSelector).node().parentElement.nodeName is "BODY"
                window.innerHeight
            else 
                d3.select(@divSelector).node().parentElement.clientHeight
        alpha: 0.5
        collisionDetection: true
        nodeOverlap: 25
        fixNodes: false
        fixRootNodes: false
        forceLocked: true
        linkDistancefn: 'default'
        nodePositions: null # not currently implemented

        # Editing
        showEditor: false # should change to nodeEditor and edgeEditor
        captionToggle: false
        toggleRootNodes: false
        removeElement: false

        #Clustering
        cluster: false
        clusterKey: "cluster"
        clusterColours: d3.shuffle ["#DD79FF", "#FFFC00",
                                    "#00FF30", "#5168FF",
                                    "#00C0FF", "#FF004B",
                                    "#00CDCD", "#f83f00",
                                    "#f800df", "#ff8d8f",
                                    "#ffcd00", "#184fff",
                                    "#ff7e00"]
        clusterControl: false

        #Stats
        nodeStats: false
        edgeStats: false

        # Filtering
        edgeFilters: false
        nodeFilters: false
        edgesToggle: false
        nodesToggle: false

        # Controls
        zoomControls: false

        # Nodes
        nodeCaption: 'caption'
        nodeCaptionsOnByDefault: false
        nodeStyle:
            "all":
                "radius": 10
                "color"  : "#68B9FE"
                "borderColor": "#127DC1"
                "borderWidth": (d, radius) -> radius / 3
                "captionColor": "#FFFFFF"
                "captionBackground": null
                "captionSize": 12
                "selected":
                    "color" : "#FFFFFF"
                    "borderColor": "#349FE3"
                "highlighted":
                    "color" : "#EEEEFF"
                "hidden":
                    "color": "none" 
                    "borderColor": "none"

        nodeColour: null # WILL BE DEPRECATED IN 1.0
        nodeMouseOver: 'caption'
        nodeRadius: 10 # WILL BE DEPRECATED IN 1.0
        nodeTypes: null
        rootNodes: 'root'
        rootNodeRadius: 15

        # Edges
        edgeCaption: 'caption'
        edgeCaptionsOnByDefault: false
        edgeClick: 'default'
        edgeStyle:
            "all":
                "width": 4
                "color": "#CCC"
                "opacity": 0.2
                "directed": true
                "curved": true
                "selected":
                    "opacity": 1
                "highlighted":
                    "opacity": 1
                "hidden":
                    "opacity": 0
        edgeTypes: null
        curvedEdges: false
        edgeWidth: -> 4
        edgeOverlayWidth: 20
        directedEdges: false
        edgeArrowSize: 5

        # Search
        search: false
        searchMethod: "contains"

        # Misc
        backgroundColour: "#000000"
        theme: null
        afterLoad: 'afterLoad'
        divSelector: '#alchemy'
        dataSource: null
        initialScale: 1
        initialTranslate: [0,0]
        scaleExtent: [0.5, 2.4]
        dataWarning: "default"
        warningMessage: "There be no data!  What's going on?"
    # Alchemy.js is a graph drawing application for the web.
    # Copyright (C) 2014  GraphAlchemist, Inc.

    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Affero General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.

    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU Affero General Public License for more details.

    # You should have received a copy of the GNU Affero General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.

    alchemy.drawing.DrawEdge =
        createLink: (edge) =>
            conf = alchemy.conf
            curved = conf.curvedEdges
            directed = conf.directedEdges
            interactions = alchemy.interactions
            utils = alchemy.drawing.EdgeUtils

            edge.append 'path'
                .attr 'class', 'edge-line'
                .attr 'id', (d) -> "path-#{d.id}"
            edge.filter (d) -> d.caption?
                .append 'text'
            edge.append 'path'
                .attr 'class', 'edge-handler'
                .style 'stroke-width', "#{conf.edgeOverlayWidth}"

        styleLink: (edge) =>
            conf = alchemy.conf
            directed = conf.directedEdges
            utils = alchemy.drawing.EdgeUtils
            edge.each (d) ->
                edgeWalk = utils.edgeWalk d
                g = d3.select(@)
                g.style utils.edgeStyle d
                
                if !conf.curvedEdges #and !directed
                    g.attr('transform', 
                       "translate(#{edgeWalk.startEdgeX}, #{edgeWalk.startEdgeY}) rotate(#{edgeWalk.edgeAngle})")

                g.select('.edge-line')
                 .attr 'd',

**This can be refactored for readability (please!)**                    
                
                if conf.curvedEdges
                        angle = utils.edgeAngle d

                        sideOfY = if Math.abs(angle) > 90 then -1 else 1
                        sideOfX = do (angle) ->
                                return 0 if angle is 0
                                return if angle < 0 then -1 else 1

                        startLine = utils.startLine(d)
                        endLine = utils.endLine(d)
                        sourceX = startLine.x
                        sourceY = startLine.y
                        targetX = endLine.x
                        targetY = endLine.y

                        dx = targetX - sourceX
                        dy = targetY - sourceY
                        
                        hyp = Math.sqrt( dx * dx + dy * dy)

                        offsetX = (dx * alchemy.conf.nodeRadius + 2) / hyp
                        offsetY = (dy * alchemy.conf.nodeRadius + 2) / hyp

                        arrowX = (-sideOfX * ( conf.edgeArrowSize )) + offsetX
                        arrowY = ( sideOfY * ( conf.edgeArrowSize )) + offsetY
                        # "M #{startLine.x},#{startLine.y} A #{hyp}, #{hyp} #{utils.captionAngle(d)} 0, 1 #{endLine.x}, #{endLine.y}")
                        "M #{sourceX-offsetX},#{sourceY-offsetY} A #{hyp}, #{hyp} #{utils.edgeAngle(d)} 0, 1 #{targetX - arrowX}, #{targetY - arrowY}"

                else
                    if conf.directedEdges
                        """
                        M #{edgeWalk.startPathX} #{edgeWalk.startPathBottomY}
                        L #{edgeWalk.arrowBendX} #{edgeWalk.arrowBendBottomY}
                        L #{edgeWalk.arrowBendX} #{edgeWalk.arrowTipBottomY}
                        L #{edgeWalk.arrowEndX} #{edgeWalk.arrowEndY} 
                        L #{edgeWalk.arrowBendX} #{edgeWalk.arrowTipTopY} 
                        L #{edgeWalk.arrowBendX} #{edgeWalk.arrowBendTopY}
                        L #{edgeWalk.startPathX} #{edgeWalk.startPathTopY}
                        Z
                        """
                    else
                        """
                        M #{edgeWalk.startPathX} #{edgeWalk.startPathBottomY}
                        L #{edgeWalk.arrowEndX} #{edgeWalk.arrowBendBottomY}
                        L #{edgeWalk.arrowEndX} #{edgeWalk.arrowBendTopY}
                        L #{edgeWalk.startPathX} #{edgeWalk.startPathTopY}
                        Z
                        """
                g.select '.edge-handler'
                        .attr('d', (d) -> g.select('.edge-line').attr('d'))

        classEdge: (edge) =>
            edge.classed 'active', true

        styleText: (edge) =>
            conf = alchemy.conf
            curved = conf.curvedEdges
            directed = conf.directedEdges
            utils = alchemy.drawing.EdgeUtils

            if curved
                edge.select 'text' 
                    .each (d) ->
                        edgeWalk = utils.edgeWalk d
                        d3.select(@).attr 'dx', (d) -> utils.middlePath(d).x
                                    .attr 'dy', (d) -> utils.middlePath(d).y + 20
                                    .attr 'transform', "rotate(#{utils.captionAngle(d)})"
                                    .text d.caption
                                    .style "display", (d)-> return "block" if conf.edgeCaptionsOnByDefault
            else
                edge.select 'text'
                    .each (d) ->
                        edgeWalk = utils.edgeWalk d
                        captionAngle = utils.captionAngle(d)
                        if captionAngle is 180
                            dx = - edgeWalk.edgeLength / 2
                        else
                            dx = edgeWalk.edgeLength / 2
                        d3.select(@).attr 'dx', "#{dx}"
                                    .attr 'dy', "#{- d['stroke-width'] * 1.1}"
                                    .attr 'transform', "rotate(#{captionAngle})"
                                    .text d.caption
                                    .style "display", (d)->
                                        return "block" if conf.edgeCaptionsOnByDefault

            # TODO: Code to start having text follow path.
            # This will eliminate the need for alot of math and extra work if we can
            # simply get the text to xlink to the path itself.  It's not currently
            # working and we need to get on with the release, but it needs to be
            # implemented.
            #
            # edge.select 'text'
            #     .each (d) ->
            #         d3.select @
            #           .text d.caption
            #           .style "display", (d)-> return "block" if conf.edgeCaptionsOnByDefault
            #           .attr "xlink:xlink:href", "#path-#{d.source.id}-#{d.target.id}"

        setInteractions: (edge) =>
            interactions = alchemy.interactions
            editorEnabled = alchemy.get.state("interactions") is "editor"
            if editorEnabled
                editorInteractions = new alchemy.editor.Interactions
                edge.select '.edge-handler'
                    .on 'click', editorInteractions.edgeClick
            else
                edge.select '.edge-handler'
                    .on 'click', interactions.edgeClick
                    .on 'mouseover', (d)-> interactions.edgeMouseOver(d)
                    .on 'mouseout', (d)-> interactions.edgeMouseOut(d)

    # Alchemy.js is a graph drawing application for the web.
    # Copyright (C) 2014  GraphAlchemist, Inc.

    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Affero General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.

    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU Affero General Public License for more details.

    # You should have received a copy of the GNU Affero General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.

    alchemy.drawing.DrawEdges =
        createEdge: (d3Edges) ->
            drawEdge = alchemy.drawing.DrawEdge
            edge = alchemy.vis.selectAll "g.edge"
                            .data d3Edges
            edge.enter().append 'g'
                        .attr "id", (d) -> "edge-#{d.id}-#{d.pos}"
                        .attr 'class', (d)->
                            "edge #{d.edgeType}"
                        .attr 'source-target', (d) -> "#{d.source.id}-#{d.target.id}"    
            drawEdge.createLink edge
            drawEdge.classEdge edge
            drawEdge.styleLink edge
            drawEdge.styleText edge
            drawEdge.setInteractions edge
            edge.exit().remove()

            if alchemy.conf.directedEdges and alchemy.conf.curvedEdges
                edge.select('.edge-line')
                    .attr('marker-end', 'url(#arrow)')

        updateEdge: (d3Edge) ->
            drawEdge = alchemy.drawing.DrawEdge
            edge = alchemy.vis.select "#edge-#{d3Edge.id}-#{d3Edge.pos}"
            drawEdge.classEdge edge
            drawEdge.styleLink edge
            drawEdge.styleText edge
            drawEdge.setInteractions edge

    # Alchemy.js is a graph drawing application for the web.
    # Copyright (C) 2014  GraphAlchemist, Inc.

    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Affero General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.

    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU Affero General Public License for more details.

    # You should have received a copy of the GNU Affero General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.

    alchemy.drawing.DrawNode =
        styleText: (node) ->
            conf = alchemy.conf
            utils = alchemy.drawing.NodeUtils
            nodes = alchemy._nodes
            node.selectAll "text"
                .attr 'dy', (d) ->
                    if nodes[d.id].getProperties().root
                        conf.rootNodeRadius / 2 
                    else 
                        conf.nodeRadius * 2 - 5
                .html (d) -> utils.nodeText(d)
                .style "display", (d)->
                    return "block" if conf.nodeCaptionsOnByDefault

        createNode: (node) ->
            node.append 'circle'
                .attr 'id', (d) -> "circle-#{d.id}"
            node.append 'svg:text'
                .attr 'id', (d) -> "text-#{d.id}"

        styleNode: (node) ->
            utils = alchemy.drawing.NodeUtils

            node.selectAll 'circle'
                .attr 'r', (d) ->
                    if typeof d.radius is "function"
                        d.radius()
                    else
                        d.radius
                .attr 'shape-rendering', 'optimizeSpeed'
                .each (d) -> d3.select(@).style utils.nodeStyle d


        setInteractions: (node) ->
            conf = alchemy.conf
            coreInteractions = alchemy.interactions
            editorEnabled = alchemy.get.state("interactions") is "editor"

            # reset drag
            drag = d3.behavior.drag()
                .origin Object
                .on "dragstart", null
                .on "drag", null
                .on "dragend", null

            if editorEnabled
                editorInteractions = new alchemy.editor.Interactions
                node.on 'mouseup',(d)->  editorInteractions.nodeMouseUp(d)
                    .on 'mouseover', (d)-> editorInteractions.nodeMouseOver(d)
                    .on 'mouseout', (d)-> editorInteractions.nodeMouseOut(d)
                    .on 'dblclick', (d)-> coreInteractions.nodeDoubleClick(d)
                    .on 'click', (d)-> editorInteractions.nodeClick(d)

            else 
                node.on 'mouseup', null
                    .on 'mouseover', (d)-> coreInteractions.nodeMouseOver(d)
                    .on 'mouseout', (d)-> coreInteractions.nodeMouseOut(d)
                    .on 'dblclick', (d)-> coreInteractions.nodeDoubleClick(d)
                    .on 'click', (d)-> coreInteractions.nodeClick(d)

                drag = d3.behavior.drag()
                        .origin(Object)
                        .on "dragstart", coreInteractions.nodeDragStarted
                        .on "drag", coreInteractions.nodeDragged
                        .on "dragend", coreInteractions.nodeDragended

                if not conf.fixNodes
                    nonRootNodes = node.filter (d) -> d.root isnt true
                    nonRootNodes.call drag

                if not conf.fixRootNodes
                    rootNodes = node.filter (d) -> d.root is true
                    rootNodes.call drag

    # Alchemy.js is a graph drawing application for the web.
    # Copyright (C) 2014  GraphAlchemist, Inc.

    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Affero General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.

    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU Affero General Public License for more details.

    # You should have received a copy of the GNU Affero General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.

    alchemy.drawing.DrawNodes =
        createNode: (d3Nodes) ->
            drawNode = alchemy.drawing.DrawNode

            # alchemyNode is an array of one or more alchemyNode._d3 packets
            node = alchemy.vis.selectAll "g.node"
                            .data d3Nodes, (n) -> n.id
            node.enter().append "g"
                    .attr "class", (d) ->
                        nodeType = alchemy._nodes[d.id]._nodeType
                        "node #{nodeType} active"
                    .attr 'id', (d) -> "node-#{d.id}"
                    .classed 'root', (d) -> d.root

            drawNode.createNode node
            drawNode.styleNode node
            drawNode.styleText node
            drawNode.setInteractions node
            node.exit().remove()

        updateNode: (alchemyNode) ->
            # alchemyNode is an array of one or more alchemyNode._d3 packets
            drawNode = alchemy.drawing.DrawNode
            node = alchemy.vis.select "#node-#{alchemyNode.id}"
            drawNode.styleNode node
            drawNode.styleText node
            drawNode.setInteractions node


    # Alchemy.js is a graph drawing application for the web.
    # Copyright (C) 2014  GraphAlchemist, Inc.

    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Affero General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.

    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU Affero General Public License for more details.

    # You should have received a copy of the GNU Affero General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.

    alchemy.drawing.EdgeUtils =
        edgeStyle: (d) ->
            edge = alchemy._edges[d.id][d.pos]
            styles = alchemy.svgStyles.edge.update edge
            nodes = alchemy._nodes

            # edge styles based on clustering
            if alchemy.conf.cluster
                clustering = alchemy.layout._clustering
                styles.stroke = do (d) ->
                    nodes = alchemy._nodes
                    clusterKey = alchemy.conf.clusterKey
                    source = nodes[d.source.id]._properties
                    target = nodes[d.target.id]._properties
                    if source.root or target.root
                        index = if source.root then target[clusterKey] else source[clusterKey]
                        "#{clustering.getClusterColour(index)}"
                    else if source[clusterKey] is target[clusterKey]
                        index = source[clusterKey]
                        "#{clustering.getClusterColour(index)}"
                    else if source[clusterKey] isnt target[clusterKey]
                        # use gradient between the two clusters' colours
                        id = "#{source[clusterKey]}-#{target[clusterKey]}"
                        gid = "cluster-gradient-#{id}"
                        "url(##{gid})"

            styles

        triangle: (edge) ->
            width = edge.target.x - edge.source.x
            height = edge.target.y - edge.source.y

            width: width
            height: height
            hyp: Math.sqrt height * height + width * width

This is the primary function used to draw the svg paths between
two nodes for directed or undirected noncurved edges. 

        edgeWalk: (edge) ->
            arrowSize = alchemy.conf.edgeArrowSize
            arrowScale = 0.3
            
Build a right triangle.

            triangle = @triangle(edge)
            width  = triangle.width
            height = triangle.height
            hyp = triangle.hyp

The widht of the stroke places a large part in how the arrow lays out with larger edge widths.

            edgeWidth = edge['stroke-width']

After all of our calculations, we offset the edge by 2 pixels to account for the curve of the node.
This typically is only noticable with opaque styles.

            curveOffset = 2

We start the edge at the very *edge* of the node, taking into account distances created by the stroke-width of the node
and edge itself.  The length of startPathX is then accounted for in the edgeLength.

            startPathX = 0 + edge.source.radius + edge.source['stroke-width'] - (edgeWidth / 2) + curveOffset
            edgeLength = hyp - startPathX - curveOffset * 1.5


The absolute angle of the edge used for caption rendering and
path rendering.

            edgeAngle: Math.atan2(height, width) / Math.PI * 180

The start of the edge in absolute coordinates.  The start of the edge and end
of the edge are simply the center of the source and target nodes.

            startEdgeX: edge.source.x
            startEdgeY: edge.source.y

            #endEdgeX: edge.target.x + (width * edge.target.radius + edge.target['stroke-width']) / hyp
            #endEdgeY: edge.target.y + (height * edge.target.radius + edge.target['stroke-width']) / hyp

The middle point of the edge, where the caption will be anchored.

            midLineX: edge.source.x + width / 2
            midLineY: edge.source.x + height / 2
            endLineX: edge.source.x + width / hyp
            endLineY: edge.source.x + height / hyp
            
Here we offset the start of the path to the very edge of the node by adding the stroke-width and the radius.
Additionally, we account for the 'stroke-width' of the edge itself, and then offeset that by one pixel to account
for the curve of the node.

            startPathX: startPathX
            startPathBottomY: edgeWidth / 2
            
            arrowBendX: edgeLength - arrowSize
            arrowBendBottomY: edgeWidth / 2
            
            arrowTipBottomY: edgeWidth / 2 + (arrowSize * arrowScale)
            
            arrowEndX: edgeLength
            arrowEndY: 0
            
            arrowTipTopY: -(arrowSize * arrowScale + edgeWidth / 2)
            
            arrowBendTopY: - edgeWidth / 2

            startPathTopY: - edgeWidth / 2

            edgeLength: edgeLength

        # Temporary drop in to reimplement curved directed edges.
        # Will be replaced once the math for the better alternative is worked out.
        curvedDirectedEdgeWalk: (edge, point)->
            conf = alchemy.conf

            # build a right triangle
            width  = edge.target.x - edge.source.x
            height = edge.target.y - edge.source.y
            # as in hypotenuse 
            hyp = Math.sqrt(height * height + width * width)

            newpoint = if point is 'middle'
                    distance = (hyp / 2)
                    x: edge.source.x + width * distance / hyp
                    y: edge.source.y + height * distance / hyp
                else if point is 'linkStart'
                    distance = edge.source.radius+ edge.source['stroke-width']
                    x: edge.source.x + width * distance / hyp
                    y: edge.source.y + height * distance / hyp
                else if point is 'linkEnd'
                    if conf.curvedEdges
                        distance = hyp
                    else
                        distance = hyp - (edge.target.radius + edge.target['stroke-width'])
                    if conf.directedEdges
                        distance = distance - conf.edgeArrowSize
                    x: edge.source.x + width * distance / hyp
                    y: edge.source.y + height * distance / hyp
            newpoint
        middleLine: (edge) -> @curvedDirectedEdgeWalk edge, 'middle'
        startLine: (edge) -> @curvedDirectedEdgeWalk edge, 'linkStart'
        endLine: (edge) -> @curvedDirectedEdgeWalk edge, 'linkEnd'
        
        edgeLength: (edge) ->
            # build a right triangle
            width  = edge.target.x - edge.source.x
            height = edge.target.y - edge.source.y
            # as in hypotenuse 
            hyp = Math.sqrt(height * height + width * width)
        edgeAngle: (edge) ->
            width  = edge.target.x - edge.source.x
            height = edge.target.y - edge.source.y
            Math.atan2(height, width) / Math.PI * 180
        
        captionAngle: (angle) ->
            if angle < -90 or angle > 90
                180
            else
                0
        middlePath: (edge) ->
            pathNode = alchemy.vis
                              .select "#path-#{edge.id}"
                              .node()
            midPoint = pathNode.getPointAtLength pathNode.getTotalLength()/2
 
            x: midPoint.x
            y: midPoint.y
                
        # Temporary fill in for curved edges until math is completed for new path only edges
        middlePathCurve: (edge) ->
            pathNode = d3.select("#path-#{edge.id}").node()
            midPoint = pathNode.getPointAtLength(pathNode.getTotalLength()/2)

            x: midPoint.x
            y: midPoint.y
    alchemy.drawing.NodeUtils =
            nodeStyle: (d) ->
                conf = alchemy.conf          
                if conf.cluster
                    d.fill = do (d)->
                        clustering = alchemy.layout._clustering
                        node = alchemy._nodes[d.id].getProperties()
                        clusterMap = clustering.clusterMap
                        key = alchemy.conf.clusterKey
                        colours = conf.clusterColours
                        # Modulo makes sure to reuse colors if it runs out
                        colourIndex = clusterMap[node[key]] % colours.length
                        colour = colours[colourIndex]
                        "#{colour}"
                d

            nodeText: (d) ->
                conf = alchemy.conf
                nodeProps = alchemy._nodes[d.id]._properties
                if conf.nodeCaption and typeof conf.nodeCaption is 'string'
                    if nodeProps[conf.nodeCaption]?
                        nodeProps[conf.nodeCaption]
                    else
                        ''
                else if conf.nodeCaption and typeof conf.nodeCaption is 'function'
                    caption = conf.nodeCaption(nodeProps)
                    if caption is undefined or String(caption) is 'undefined'
                        alchemy.log["caption"] = "At least one caption returned undefined"
                        conf.caption = false
                    caption

    alchemy.svgStyles =
        node:
            populate: (node) ->
                conf = alchemy.conf
                defaultStyle = _.omit conf.nodeStyle.all, "selected", "highlighted", "hidden"
                d = node

                # if user put in hard value, turn into a function
                toFunc = (inp)->
                    return inp if typeof inp is "function"
                    return -> inp

                nodeTypeKey = _.keys(conf.nodeTypes)[0]
                nodeType = node.getProperties()[nodeTypeKey]

                if conf.nodeStyle[nodeType] is undefined
                    nodeType = "all"

                typedStyle = _.assign _.cloneDeep(defaultStyle), conf.nodeStyle[nodeType]
                style = _.assign typedStyle, conf.nodeStyle[nodeType][node._state]

                radius = toFunc style.radius
                fill = toFunc style.color
                stroke = toFunc style.borderColor
                strokeWidth = toFunc style.borderWidth

                svgStyles = {}
                svgStyles["radius"] = radius d
                svgStyles["fill"] = fill d
                svgStyles["stroke"] = stroke d
                svgStyles["stroke-width"] = strokeWidth d, radius(d)
                
                svgStyles

        edge:
            populate: (edge) ->
                conf = alchemy.conf
                defaultStyle = _.omit conf.edgeStyle.all, "selected", "highlighted", "hidden"

                # if user put in hard value, turn into a function
                toFunc = (inp)->
                    if typeof inp is "function"
                        return inp
                    return -> inp

                edgeType = edge._edgeType

                if conf.edgeStyle[edgeType] is undefined
                    edgeType = "all"

                typedStyle = _.assign _.cloneDeep(defaultStyle), conf.edgeStyle[edgeType]
                style = _.assign typedStyle, conf.edgeStyle[edgeType][edge._state]

                width = toFunc style.width
                color = toFunc style.color
                opacity = toFunc style.opacity

                svgStyles =
                    "stroke": color edge
                    "stroke-width": width edge
                    "opacity": opacity edge
                    "fill": "none"
                    # Uncomment for flower
                    # "fill": color edge

                svgStyles

            update: (edge) ->
                conf = alchemy.conf
                style = edge._style
                toFunc = (inp)->
                    if typeof inp is "function"
                        return inp
                    return -> inp

                width = toFunc style.width
                color = toFunc style.color
                opacity = toFunc style.opacity

                svgStyles =
                    "stroke": color edge
                    "stroke-width": width edge
                    "opacity": opacity edge
                    "fill": "none"

                svgStyles
    # Alchemy.js is a graph drawing application for the web.
    # Copyright (C) 2014  GraphAlchemist, Inc.

    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Affero General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.

    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU Affero General Public License for more details.

    # You should have received a copy of the GNU Affero General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.

    class alchemy.editor.Editor
        constructor: ->
            @utils = new alchemy.editor.Utils

        # init: =>
        #     if alchemy.conf.showEditor is true
        #         @showOptions()
        #         @nodeEditorInit()
        #         @edgeEditorInit()
        
        editorContainerHTML:
                """
                    <div id="editor-header" data-toggle="collapse" data-target="#editor #element-options">
                        <h3>Editor</h3><span class="fa fa-2x fa-caret-right"></span>
                    </div>
                    <div id="element-options" class="collapse">
                        <ul class="list-group"> 
                            <li class="list-group-item" id="remove">Remove Selected</li> 
                            <li class="list-group-item" id="editor-interactions">Editor mode enabled, click to disable editor interactions</li>
                        </ul>
                    </div>
                    """

        elementEditorHTML: (type) -> 
                """
                    <h4>#{type} Editor</h4>
                    <form id="add-property-form">
                        <div id="add-property">
                            <input class="form-control" id="add-prop-key" placeholder="New Property Name">
                            <input class="form-control" id="add-prop-value" placeholder="New Property Value">
                        </div>
                        <input id="add-prop-submit" type="submit" value="Add Property" placeholder="add a property to this node">
                    </form>
                    <form id="properties-list">
                        <input id="update-properties" type="submit" value="Update Properties">
                    </form>
                """

        startEditor: =>
            divSelector = alchemy.conf.divSelector
            html = @editorContainerHTML
            editor = alchemy.dash
                            .select "#control-dash"
                            .append "div"
                            .attr "id", "editor"
                            .html html
            
            editor.select '#editor-header'
                    .on 'click', () ->
                        if alchemy.dash.select('#element-options').classed "in"
                            alchemy.dash
                                   .select "#editor-header>span"
                                   .attr "class", "fa fa-2x fa-caret-right"
                        else
                            alchemy.dash
                                   .select "#editor-header>span"
                                   .attr "class", "fa fa-2x fa-caret-down"   

            editor_interactions = editor.select '#element-options ul #editor-interactions'
                .on 'click', ->
                    d3.select @
                      .attr "class", () ->
                        if alchemy.get.state() is 'editor'
                            alchemy.set.state 'interactions', 'default'
                            "inactive list-group-item"
                        else
                            alchemy.set.state 'interactions', 'editor'
                            "active list-group-item"
                      .html ->
                          if alchemy.get.state() is 'editor'
                              """Disable Editor Interactions"""
                          else 
                              """Enable Editor Interactions"""

            editor.select "#element-options ul #remove"
                  .on "click", -> alchemy.editor.remove()

            utils = @utils
            
            editor_interactions.on "click", () -> 
                    if !alchemy.dash.select("#editor-interactions").classed "active"
                        utils.enableEditor()
                        alchemy.dash.select "#editor-interactions"
                            .classed {"active": true, "inactive": false}
                            .html """Editor mode enabled, click to disable editor interactions"""
                    else 
                        utils.disableEditor()
                        alchemy.dash
                            .select "#editor-interactions"
                            .classed {"active": false, "inactive": true}
                            .html """Editor mode disabled, click to enable editor interactions"""

        # nodeEditorInit: () ->
        #     addPropHTML = """
        #                     <div id="add-property">
        #                         <input class='form-control' id='node-add-prop-key' placeholder="Property Name"></input>
        #                         <input class='form-control' id='node-add-prop-value' placeholder="Property Value"></input>
        #                     </div>
        #                 """
        #     d3.select("#element-options")
        #         .append("div")
        #         .attr("id", "node-editor")
        #         .attr("class", () ->
        #             if d3.select("#editor-interactions").classed("active")
        #                 return "enabled"
        #             else return "hidden"
        #         )
        #         .html("""<h4>Node Editor</h4>""")

        #     # node editor form and add property form
        #     d3.select("#node-editor")
        #         .append("form")
        #         .attr("id", "node-add-property")
        #         .html(addPropHTML)

        #     d3.select("#node-add-property")
        #         .append("input")
        #         .attr("id", "node-add-prop-submit")
        #         .attr("type", "submit")
        #         .attr("value", "Add Property")

        #     # submission handler
        #     d3.select("#node-add-property")
        #         .on "submit" , ->
        #             event.preventDefault()
        #             if d3.select(".node.selected").empty()
        #                 d3.selectAll("#node-add-prop-value, #node-add-prop-key")
        #                     .attr("placeholder", "select a node first")
                                
        nodeEditor: (n) =>
            divSelector = alchemy.conf.divSelector        
            editor = alchemy.dash.select "#control-dash #editor"
            options = editor.select '#element-options'
            # options.html(html)
            html = @elementEditorHTML "Node"
            elementEditor = options.append 'div'
                                    .attr 'id', 'node-editor'
                                    .html html

            elementEditor.attr "class", () ->
                        active = alchemy.dash
                                        .select "#editor-interactions"
                                        .classed "active"
                        return "enabled" if active
                        "hidden"
            
            add_property = editor.select "#node-editor form #add-property"
            add_property.select "#node-add-prop-key"
                        .attr "placeholder", "New Property Name"
                        .attr "value", null
            add_property.select "#node-add-prop-value"
                        .attr "placeholder", "New Property Value"
                        .attr "value", null

            alchemy.dash
                .select "#add-property-form"
                .on "submit", ->
                    event.preventDefault()
                    key = alchemy.dash
                                 .select '#add-prop-key'
                                 .property 'value'
                    key = key.replace /\s/g, "_"
                    value = alchemy.dash
                                   .select '#add-prop-value'
                                   .property 'value'
                    updateProperty key, value, true
                    alchemy.dash
                           .selectAll "#add-property .edited-property"
                           .classed "edited-property":false
                    @reset()

            nodeProperties = alchemy._nodes[n.id].getProperties()
            alchemy.vis
                   .select "#node-#{n.id}"
                   .classed "editing":true

            property_list = editor.select "#node-editor #properties-list"

            for property, val of nodeProperties
                node_property = property_list.append "div"
                                             .attr "id", "node-#{property}"
                                             .attr "class", "property form-inline form-group"
                
                node_property.append "label"
                                .attr "for", "node-#{property}-input"
                                .attr "class","form-control property-name"
                                .text "#{property}"
                
                node_property.append "input"
                                .attr "id", "node-#{property}-input"
                                .attr "class", "form-control property-value"
                                .attr "value", "#{val}"

            alchemy.dash
                   .select "#properties-list"
                   .on "submit", -> 
                       event.preventDefault()
                       properties = alchemy.dash.selectAll ".edited-property"
                       for property in properties[0]
                           selection = alchemy.dash.select property
                           key = selection.select("label").text()
                           value = selection.select("input").attr 'value'
                           updateProperty key, value, false
                       alchemy.dash
                              .selectAll "#node-properties-list .edited-property"
                              .classed "edited-property":false
                       @.reset()

            d3.selectAll "#add-prop-key, #add-prop-value, .property"
                .on "keydown", ->
                    if d3.event.keyCode is 13
                        event.preventDefault()
                    d3.select(@).classed {"edited-property":true}

            updateProperty = (key, value, newProperty) ->
                nodeID = n.id
                if (key!="") and (value != "")
                    alchemy._nodes[nodeID].setProperty "#{key}", "#{value}"
                    drawNodes = alchemy._drawNodes
                    drawNodes.updateNode alchemy.viz.select("#node-#{nodeID}")
                    if newProperty is true 
                        alchemy.dash
                               .select "#node-add-prop-key"
                               .attr "value", "property added/updated to key: #{key}"
                        alchemy.dash
                               .select "#node-add-prop-value"
                               .attr "value", "property at #{key} updated to: #{value}"
                    else
                        alchemy.dash
                               .select "#node-#{key}-input"
                               .attr "value", "property at #{key} updated to: #{value}"

                else
                    if newProperty is true 
                        alchemy.dash
                               .select "#node-add-prop-key"
                               .attr "value", "null or invalid input"
                        alchemy.dash
                               .select "#node-add-prop-value"
                               .attr "value", "null or invlid input"
                    else
                        alchemy.dash
                               .select "#node-#{key}-input"
                               .attr "value", "null or invalid input"
                
        editorClear: () ->
            alchemy.dash
                   .selectAll ".node"
                   .classed "editing":false
            alchemy.dash
                   .selectAll ".edge"
                   .classed "editing":false
            alchemy.dash
                   .select "#node-editor"
                   .remove()
            alchemy.dash
                   .select "#edge-editor"
                   .remove()
            alchemy.dash
                   .select "#node-add-prop-submit"
                   .attr "placeholder", ()->
                       if alchemy.vis.selectAll(".selected").empty()
                           return "select a node or edge to edit properties"
                       return "add a property to this element"

        edgeEditor: (e) ->
            divSelector = alchemy.conf.divSelector        
            editor = alchemy.dash "#control-dash #editor"
            options = editor.select '#element-options'
            html = @elementEditorHTML "Edge"
            elementEditor = options.append 'div'
                                    .attr 'id', 'edge-editor'
                                    .html html

            elementEditor.attr "class", () ->
                        return "enabled" if alchemy.dash
                                                   .select "#editor-interactions"
                                                   .classed "active"
                        "hidden"
            
            add_property = editor.select "#edge-editor form #add-property"
            add_property.select "#add-prop-key"
                        .attr "placeholder", "New Property Name"
                        .attr "value", null
            add_property.select "#add-prop-value"
                        .attr "placeholder", "New Property Value"
                        .attr "value", null
            
            edgeProperties = alchemy._edges[e.id].getProperties()
            alchemy.vis
                   .select "#edge-#{e.id}"
                   .classed "editing":true

            property_list = editor.select "#edge-editor #properties-list"

            for property, val of edgeProperties
                edge_property = property_list.append "div"
                                                .attr "id", "edge-#{property}"
                                                .attr "class", "property form-inline form-group"
                
                edge_property.append "label"
                                .attr "for", "edge-#{property}-input"
                                .attr "class","form-control property-name"
                                .text "#{property}"
                
                edge_property.append "input"
                                .attr "id", "edge-#{property}-input"
                                .attr "class", "form-control property-value"
                                .attr "value", "#{val}"

            alchemy.dash
                   .selectAll "#add-prop-key, #add-prop-value, .property"
                   .on "keydown", ->
                       if d3.event.keyCode is 13
                           event.preventDefault()
                       d3.select(@).classed {"edited-property":true}

            alchemy.dash
                .select "#add-property-form"
                .on "submit", ->
                    event.preventDefault()
                    key = alchemy.dash
                                 .select "#add-prop-key"
                                 .property 'value'
                    key = key.replace /\s/g, "_"
                    value = alchemy.dash
                                   .select "#add-prop-value"
                                   .property 'value'
                    updateProperty key, value, true

                    alchemy.dash
                           .selectAll "#add-property .edited-property"
                           .classed "edited-property":false
                    @reset()

            d3.select "#properties-list"
                .on "submit", -> 
                    event.preventDefault()
                    properties = alchemy.dash.selectAll ".edited-property"
                    for property in properties[0]
                        selection = alchemy.dash.select property
                        key = selection.select("label").text()
                        value = selection.select("input").property 'value'
                        updateProperty key, value, false

                    alchemy.dash
                           .selectAll "#properties-list .edited-property"
                           .classed "edited-property":false
                    @reset()

            updateProperty = (key, value, newProperty) ->
                edgeID = e.id
                if (key!="") and (value != "")
                    alchemy._edges[edgeID].setProperty "#{key}", "#{value}"
                    edgeSelection = alchemy.vis.select "#edge-#{edgeID}"
                    drawEdges = new alchemy.drawing.DrawEdges
                    drawEdges.updateEdge alchemy.vis.select("#edge-#{edgeID}")
                    if newProperty is true 
                        alchemy.dash
                               .select "#add-prop-key"
                               .attr "value", "property added/updated to key: #{key}"
                        alchemy.dash
                               .select "#add-prop-value"
                               .attr "value", "property at #{key} updated to: #{value}"
                    else
                        alchemy.dash
                               .select "#edge-#{key}-input"
                               .attr "value", "property at #{key} updated to: #{value}"

                else
                    if newProperty is true 
                        alchemy.dash
                               .select "#add-prop-key"
                               .attr "value", "null or invalid input"
                        alchemy.dash
                               .select "#add-prop-value"
                               .attr "value", "null or invlid input"
                    else
                        alchemy.dash
                               .select "#edge-#{key}-input"
                               .attr "value", "null or invalid input"
    class alchemy.editor.Interactions
        # @mouseUpNode = null
        # @sourceNode = null
        # @targetNode = null
        # @newEdge = null
        # @click = null
        constructor: ->
            @editor = new alchemy.editor.Editor

        nodeMouseOver: (n) ->
            if !d3.select(@).select("circle").empty()
                radius = d3.select @
                           .select "circle"
                           .attr "r"
                d3.select @
                  .select "circle"
                    .attr "r", radius*3
            @

        nodeMouseUp: (n) =>
            if @sourceNode != n
                @mouseUpNode = true
                @targetNode = n
                @click = false
            else 
                @click = true
            @

        nodeMouseOut: (n) ->
            if !d3.select(@).select("circle").empty()
                radius = d3.select @
                           .select "circle"
                           .attr "r"
                d3.select @
                  .select "circle"
                  .attr "r", radius/3
            @

        nodeClick: (c) =>
            d3.event.stopPropagation()
            # select the correct nodes
            if !alchemy.vis.select("#node-#{c.id}").empty()
                selected = alchemy.vis
                                  .select "#node-#{c.id}"
                                  .classed 'selected'
                alchemy.vis
                       .select "#node-#{c.id}"
                       .classed 'selected', !selected
            @editor.editorClear()
            @editor.nodeEditor c

        edgeClick: (e) =>
            d3.event.stopPropagation()
            @editor.editorClear()
            @editor.edgeEditor e
            
        addNodeStart: (d, i) =>
            d3.event.sourceEvent.stopPropagation()
            @sourceNode = d
            alchemy.vis
                .select '#dragline'
                .classed "hidden":false
            @

        addNodeDragging: (d, i) =>
            # rework
            x2coord = d3.event.x
            y2coord = d3.event.y
            alchemy.vis
                .select '#dragline'
                .attr "x1", @sourceNode.x
                .attr "y1", @sourceNode.y
                .attr "x2", x2coord
                .attr "y2", y2coord
                .attr "style", "stroke: #FFF"
            @


        addNodeDragended: (d, i) =>
            #we moused up on an existing (different) node
            if !@click 
                if !@mouseUpNode
                    dragline = alchemy.vis.select "#dragline"
                    targetX = dragline.attr "x2"
                    targetY = dragline.attr "y2"

                    @targetNode =
                        id: "#{_.uniqueId('addedNode_')}",
                        x: parseFloat(targetX),
                        y: parseFloat(targetY),
                        caption: "node added"

                @newEdge =
                    id: "#{@sourceNode.id}-#{@targetNode.id}", 
                    source: @sourceNode.id, 
                    target: @targetNode.id, 
                    caption: "edited"

                alchemy.editor.update @targetNode, @newEdge
            
            @reset()
            @

        deleteSelected: (d) =>
            switch d3.event.keyCode
                when 8, 46
                    if !(d3.select(d3.event.target).node().tagName is ("INPUT" or "TEXTAREA"))
                        d3.event.preventDefault()
                        alchemy.editor.remove()

        reset: =>
            # reset interaciton variables
            @mouseUpNode = null
            @sourceNode = null
            @targetNode = null
            @newEdge = null
            @click = null

            #reset dragline
            alchemy.vis
                .select "#dragline"
                .classed "hidden":true
                .attr "x1", 0            
                .attr "y1", 0
                .attr "x2", 0
                .attr "y2", 0 
            @

        @

    class alchemy.editor.Utils
        constructor: () ->
            @drawNodes = alchemy._drawNodes
            @drawEdges = alchemy._drawEdges

        enableEditor: () =>
            alchemy.set.state "interactions", "editor"
            dragLine = alchemy.vis
                .append "line"
                .attr "id", "dragline"

            @drawNodes.updateNode alchemy.node
            @drawEdges.updateEdge alchemy.edge
            selectedElements = alchemy.vis.selectAll ".selected"
            editor = new alchemy.editor.Editor
            if (not selectedElements.empty()) and (selectedElements.length is 1)
                if selectedElements.classed 'node'
                    editor.nodeEditor selectedElements.datum()
                    alchemy.dash
                        .select "#node-editor" 
                        .attr "class", "enabled"
                        .style "opacity", 1
                else if selectedElements.classed 'edge'
                    editor.edgeEditor selectedElements.datum()
                    alchemy.dash
                        .select "#edge-editor"
                        .attr "class", "enabled"
                        .style "opacity", 1
            else
                selectedElements.classed "selected":false

        disableEditor: () ->
            alchemy.setState "interactions", "default"
            alchemy.vis
                   .select "#dragline"
                   .remove()

            alchemy.dash
                   .select "#node-editor"
                   .transition()
                   .duration 300
                   .style "opacity", 0
            alchemy.dash
                   .select "#node-editor"
                   .transition()
                   .delay 300
                   .attr "class", "hidden"

            @drawNodes.updateNode alchemy.node
            alchemy.vis
                   .selectAll ".node"
                   .classed "selected":false

        remove: () ->
            selectedNodes = alchemy.vis.selectAll ".selected.node"
            for node in selectedNodes[0]
                nodeID = alchemy.vis
                                .select node
                                .data()[0]
                                .id

                node_data = alchemy._nodes[nodeID]
                if node_data?  
                    for edge in node_data.adjacentEdges
                        alchemy._edges = _.omit alchemy._edges, "#{edge}"
                        alchemy.edge = alchemy.edge.data _.map(alchemy._edges, (e) -> e._d3), (e)->e.id
                        alchemy.vis
                               .select "#edge-#{edge}"
                               .remove()
                    alchemy._nodes = _.omit alchemy._nodes, "#{nodeID}"
                    alchemy.node = alchemy.node.data _.map(alchemy._nodes, (n) -> n._d3), (n)->n.id
                    alchemy.vis
                           .select node
                           .remove()
                    if alchemy.get.state("interactions") is "editor"
                        alchemy.modifyElements.nodeEditorClear()

        addNode: (node) ->
            newNode = alchemy._nodes[node.id] = new alchemy.models.Node {id:"#{node.id}"}
            newNode.setProperty "caption", node.caption
            newNode.setD3Property "x", node.x
            newNode.setD3Property "y", node.y
            alchemy.node = alchemy.node.data _.map(alchemy._nodes, (n) -> n._d3), (n)->n.id

        addEdge: (edge) ->
            newEdge = alchemy._edges[edge.id] = new alchemy.models.Edge edge
            alchemy.edge = alchemy.edge.data _.map(alchemy._edges, (e) -> e._d3), (e)->e.id

        update: (node, edge) ->
            #only push the node if it didn't previously exist
            if !@mouseUpNode
                alchemy.editor.addNode node
                alchemy.editor.addEdge edge
                @drawEdges.createEdge alchemy.edge
                @drawNodes.createNode alchemy.node

            else
                alchemy.editor.addEdge edge
                @drawEdges.createEdge alchemy.edge

            alchemy.layout.tick()

    class alchemy.models.Edge
        # takes an edge property map from GraphJSON
        # as well as an index, which is the position of the edge map in
        # the array of edges stored in alchemy._edges at each "source-target"
        # this is used to create the id for the individual node which will be "source-target-index"
        # e.g. 1-0-1
        constructor: (edge, index=null) ->
            a = alchemy
            conf = a.conf

            @id = @_setID edge
            @_index = index
            @_state = "active"
            @_properties = edge
            @_edgeType = @_setEdgeType()
            @_style =
                if conf.edgeStyle[@_edgeType]?
                    _.merge _.clone(conf.edgeStyle["all"]), conf.edgeStyle[@_edgeType]
                else
                    _.clone conf.edgeStyle["all"]
            @_d3 = _.merge
                'id': @id
                'pos': @_index
                'edgeType': @_edgeType
                'source': a._nodes[@_properties.source]._d3
                'target': a._nodes[@_properties.target]._d3
                , a.svgStyles.edge.populate @
            @_setCaption(edge, conf)
            # Add id to source/target's edgelist
            a._nodes["#{edge.source}"]._addEdge "#{@id}-#{@_index}"
            a._nodes["#{edge.target}"]._addEdge "#{@id}-#{@_index}"

        _setD3Properties: (props) => _.merge @_d3, props
        _setID: (e) => if e.id? then e.id else "#{e.source}-#{e.target}"

        _setCaption: (edge, conf) =>
            cap = conf.edgeCaption
            edgeCaption = do (edge) ->
                switch typeof cap
                    when ('string' or 'number') then edge[cap]
                    when 'function' then cap(edge)
            if edgeCaption
                @_d3.caption = edgeCaption
        _setEdgeType: ->
            conf = alchemy.conf
            if conf.edgeTypes
                if _.isPlainObject conf.edgeTypes
                    lookup = Object.keys alchemy.conf.edgeTypes
                    edgeType = @_properties[lookup]
                else if _.isArray conf.edgeTypes
                    edgeType = @_properties["caption"]
                else if typeof conf.edgeTypes is 'string'
                    edgeType = @_properties[conf.edgeTypes]
            if edgeType is undefined then edgeType = "all"
            @_setD3Properties 'edgeType', edgeType
            edgeType

        setProperties: (property, value=null) =>
            if _.isPlainObject property
                _.assign @_properties, property
                if 'source' of property then @_setD3Properties {'source': alchemy._nodes[property.source]._d3}
                if 'target' of property then @_setD3Properties {'target': alchemy._nodes[property.target]._d3}
            else
                @_properties[property] = value
                if (property is 'source') or (property is 'target')
                    @_setD3Properties {property: alchemy._nodes[value]._d3}
            @

        getProperties: (key=null, keys...) =>
            if not key? and (keys.length is 0)
                @_properties
            else if keys.length isnt 0
                query = _.union [key], keys
                _.pick @_properties, query
            else
                @_properties[key]

        # Style methods
        getStyles: (key=null) =>
            if key?
                @_style[key]
            else
                @_style

        setStyles: (key, value=null) ->
            # If undefined, set styles based on state
            if key is undefined
                key = alchemy.svgStyles.edge.populate @

            # takes a key, value or map of key values
            # the user passes a map of styles to set multiple styles at once
            if _.isPlainObject key
                _.assign @_style, key
            else if typeof key is "string"
                @_style[key] = value

            @_setD3Properties alchemy.svgStyles.edge.update(@)
            alchemy._drawEdges.updateEdge @_d3
            @

        toggleHidden: ()->
            @._state = if @._state is "hidden" then "active" else "hidden"
            @.setStyles()

        # Find if both endpoints are active
        # there are probably better ways to do this
        allNodesActive: () =>
            source = alchemy.vis.select "#node-#{@properties.source}"
            target = alchemy.vis.select "#node-#{@properties.target}"

            !source.classed("inactive") && !target.classed("inactive")

    class alchemy.models.Node
        constructor: (node) ->
            a = alchemy
            conf = a.conf

            @id = node.id
            @_properties = node
            @_d3 = _.merge
                'id': @id
                'root': @_properties[conf.rootNodes]
                , a.svgStyles.node.populate(@)
            @_nodeType = @_setNodeType()
            @_style =
                if conf.nodeStyle[@_nodeType]
                    conf.nodeStyle[@_nodeType]
                else
                    conf.nodeStyle["all"]
            @_state = "active"

            @_adjacentEdges = []

        # internal methods
        _setNodeType: =>
            conf = alchemy.conf
            if conf.nodeTypes
                if _.isPlainObject conf.nodeTypes
                    lookup = Object.keys alchemy.conf.nodeTypes
                    types = _.values conf.nodeTypes
                    nodeType = @_properties[lookup]
                else if typeof conf.nodeTypes is 'string'
                    nodeType = @_properties[conf.nodeTypes]
            if nodeType is undefined then nodeType = "all"
            @_setD3Properties 'nodeType', nodeType
            nodeType

        _setD3Properties: (props) =>
            _.merge @_d3, props

        _addEdge: (edgeDomID) ->
            # Stores edge.id for easy edge lookup
            @_adjacentEdges = _.union @_adjacentEdges, [edgeDomID]

        # Edit node properties
        getProperties: (key=null, keys...) =>
            if not key? and (keys.length is 0)
                @_properties
            else if keys.length isnt 0
                query = _.union [key], keys
                _.pick @_properties, query
            else
                @_properties[key]

        setProperty: (property, value=null) =>
            if _.isPlainObject property
                _.assign @_properties, property
            else
                @_properties[property] = value
            @

        removeProperty: (property) =>
            if @_properties.property?
                _.omit @_properties, property
            @


        # Style methods
        getStyles: (key=null) =>
            if key?
                @_style[key]
            else
                @_style

        setStyles: (key, value=null) ->
            # If undefined, set styles based on state
            if key is undefined
                key = alchemy.svgStyles.node.populate @
            # takes a key, value or map of key values
            # the user passes a map of styles to set multiple styles at once
            else if _.isPlainObject key
                _.assign @_style, key
            else
                @_style[key] = value
            @_setD3Properties alchemy.svgStyles.node.populate @
            alchemy._drawNodes.updateNode @_d3
            @

        toggleHidden: ->
            @._state = if @._state is "hidden" then "active" else "hidden"
            @setStyles()
            _.each @._adjacentEdges, (id)->
                [source, target, pos] = id.split("-")
                e = alchemy._edges["#{source}-#{target}"][pos]
                sourceState = alchemy._nodes["#{source}"]._state
                targetState = alchemy._nodes["#{target}"]._state
                if e._state is "hidden" and (sourceState is "active" and targetState is "active")
                  e.toggleHidden()
                else if e._state is "active" and (sourceState is "hidden" or targetState is "hidden")
                  e.toggleHidden()

        # Convenience methods
        outDegree: () -> @_adjacentEdges.length

    alchemy.themes = 
        "default":
            "backgroundColour": "#000000"
            "nodeStyle":
                "all":
                    "radius": -> 10
                    "color"  : -> "#68B9FE"
                    "borderColor": ->"#127DC1"
                    "borderWidth": (d, radius) -> radius / 3
                    "captionColor": -> "#FFFFFF"
                    "captionBackground": -> null
                    "captionSize": 12
                    "selected":
                        "color" : -> "#FFFFFF"
                        "borderColor": -> "#349FE3"
                    "highlighted":
                        "color" : -> "#EEEEFF"
                    "hidden":
                        "color": -> "none" 
                        "borderColor": -> "none"
            "edgeStyle":
                "all":
                    "width": 4
                    "color": "#CCC"
                    "opacity": 0.2
                    "directed": true
                    "curved": true
                    "selected":
                        "opacity": 1
                    "highlighted":
                        "opacity": 1
                    "hidden":
                        "opacity": 0

        "white":
            "backgroundColour": "#FFFFFF"
            "nodeStyle":
                "all":
                    "radius": -> 10
                    "color"  : -> "#68B9FE"
                    "borderColor": ->"#127DC1"
                    "borderWidth": (d, radius) -> radius / 3
                    "captionColor": -> "#FFFFFF"
                    "captionBackground": -> null
                    "captionSize": 12
                    "selected":
                        "color": -> "#FFFFFF"
                        "borderColor": -> "38DD38"
                    "highlighted":
                        "color" : -> "#EEEEFF"
                    "hidden":
                        "color": -> "none" 
                        "borderColor": -> "none"
            "edgeStyle":
                "all":
                    "width": 4
                    "color": "#333"
                    "opacity": 0.4
                    "directed": false
                    "curved": false
                    "selected":
                        "color": "#38DD38"
                        "opacity": 0.9
                    "highlighted":
                        "color": "#383838"
                        "opacity": 0.7
                    "hidden":
                        "opacity": 0

    alchemy.utils.warnings = 
        dataWarning: ->
            if alchemy.conf.dataWarning and typeof alchemy.conf.dataWarning is 'function'
                alchemy.conf.dataWarning()
            else if alchemy.conf.dataWarning is 'default'
                no_results = """
                            <div class="modal fade" id="no-results">
                                <div class="modal-dialog">
                                    <div class="modal-content">
                                        <div class="modal-header">
                                            <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                                            <h4 class="modal-title">Sorry!</h4>
                                        </div>
                                        <div class="modal-body">
                                            <p>#{alchemy.conf.warningMessage}</p>
                                        </div>
                                        <div class="modal-footer">
                                            <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                                        </div>
                                    </div>
                                </div>
                            </div>
                           """
                $('body').append no_results
                $('#no-results').modal 'show'
        divWarning: ->
            """
                create an element that matches the value for 'divSelector' in your conf.
                For instance, if you are using the default 'divSelector' conf, simply provide
                <div id='#alchemy'></div>.
            """