var icon = icons.edit,
      callback = function() {
        self.rebindAll(self.editRebinds());
      };
    createIcon(icon, "edit", callback);
  };
  
  this.addControlExpand = function() {
    var icon = icons.expand,
      callback = function() {
        self.rebindAll(self.expandRebinds());
      };
    createIcon(icon, "expand", callback);
  };
  
  this.addControlDelete = function() {
    var icon = icons.trash,
      callback = function() {
        self.rebindAll(self.deleteRebinds());
      };
    createIcon(icon, "delete", callback);
  };
  
  this.addControlConnect = function() {
    var icon = icons.edge,
      idprefix = "select_edge_collection",
      callback = function() {
        if (askForCollection && adapter.getEdgeCollections().length > 1) {
          modalDialogHelper.createModalDialog("Select Edge Collection",
            idprefix, [{
              type: "list",
              id: "edge",
              objects: adapter.getEdgeCollections(),
              text: "Select collection",
              selected: adapter.getSelectedEdgeCollection()
            }], function () {
              var edgeCollection = $("#" + idprefix + "edge")
                 .children("option")
                 .filter(":selected")
                 .text();
              adapter.useEdgeCollection(edgeCollection);
            },
            "Select"
          );
        }
        self.rebindAll(self.connectNodesRebinds());
      };
    createIcon(icon, "connect", callback);
  };
  
  this.addAll = function () {
    self.addControlDrag();
    self.addControlView();
    self.addControlEdit();
    self.addControlExpand();
    self.addControlDelete();
    self.addControlConnect();
    self.addControlNewNode();
  };
}
<commit_msg>Fixes false positioning of edge #894. Tested  in FF, Chrome, Safari, IE11 (all newest versions)<commit_after>/*jslint indent: 2, nomen: true, maxlen: 100, white: true  plusplus: true */
/*global $, _, d3*/
/*global document, window, prompt*/
/*global modalDialogHelper, uiComponentsHelper */
/*global EventDispatcher*/
////////////////////////////////////////////////////////////////////////////////
/// @brief Graph functionality
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author <NAME>
/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

function EventDispatcherControls(list, nodeShaper, edgeShaper, start, dispatcherConfig) {
  "use strict";
  
  if (list === undefined) {
    throw "A list element has to be given.";
  }
  if (nodeShaper === undefined) {
    throw "The NodeShaper has to be given.";
  }
  if (edgeShaper === undefined) {
    throw "The EdgeShaper has to be given.";
  }
  if (start === undefined) {
    throw "The Start callback has to be given.";
  }
  
  var self = this,
    /*
    icons = {
      expand: "expand",
      add: "add",
      trash: "trash",
      drag: "drag",
      edge: "connect",
      edit: "edit",
      view: "view"
    },
    */
    icons = {
      expand: {
        icon: "expand",
        title: "SPOT"
      },
      add: {
        icon: "plus-square-o",
        title: "NODE"
      },
      trash: {
        icon: "trash-o",
        title: "TRASH"
      },
      drag: {
        icon: "arrows",
        title: "DRAG"
      },
      edge: {
        icon: "link",
        title: "EDGE"
      },
      edit: {
        icon: "pencil",
        title: "EDIT"
      },
      view: {
        icon: "search",
        title: "VIEW"
      }
    },
    dispatcher = new EventDispatcher(nodeShaper, edgeShaper, dispatcherConfig),
    adapter = dispatcherConfig.edgeEditor.adapter,
    askForCollection = (!!adapter
      && _.isFunction(adapter.useNodeCollection)
      && _.isFunction(adapter.useEdgeCollection)),
      
    
    appendToList = function(button) {
      list.appendChild(button);
    },
    createIcon = function(icon, title, callback) {
      var btn = uiComponentsHelper.createIconButton(
        icon,
        "control_event_" + title,
        callback
      );
      appendToList(btn);
    },
    rebindNodes = function(actions) {
      dispatcher.rebind("nodes", actions);
    },
    rebindEdges = function(actions) {
      dispatcher.rebind("edges", actions);
    },
    rebindSVG = function(actions) {
      dispatcher.rebind("svg", actions);
    },
    getCursorPosition = function (ev) {
      var e = ev || window.event,
        res = {};
      res.x = e.clientX;
      res.y = e.clientY;
      res.x += document.body.scrollLeft;
      res.y += document.body.scrollTop;
      return res;
    },
    getCursorPositionInSVG = function (ev) {
      var pos = getCursorPosition(ev),
          off = $('svg#graphViewerSVG').offset(),
          svg, bBox, bCR;
      svg = d3.select("svg#graphViewerSVG").node();
      // Normal case. SVG has no clipped view box.
      bCR = svg.getBoundingClientRect();
      if ($("svg#graphViewerSVG").height() <= bCR.height ) {
        return {
          x: pos.x - off.left,
          y: pos.y - off.top
        };
      }
      // Firefox case. SVG has a clipped view box.
      bBox = svg.getBBox();
      return {
        x: pos.x - (bCR.left - bBox.x),
        y: pos.y - (bCR.top - bBox.y)
      };
    },
    callbacks = {
      nodes: {},
      edges: {},
      svg: {}
    },

  /*******************************************
  * Create callbacks wenn clicking on objects
  *
  *******************************************/

    createNewNodeCB = function() {
      var prefix = "control_event_new_node",
        idprefix = prefix + "_",
        createCallback = function(ev) {
          var pos = getCursorPositionInSVG(ev);
          modalDialogHelper.createModalCreateDialog(
            "Create New Node",
            idprefix,
            {},
            function(data) {
              dispatcher.events.CREATENODE(data, function(node) {
                $("#" + idprefix + "modal").modal('hide');
                nodeShaper.reshapeNodes();
                start();
              }, pos.x, pos.y)();
            }
          );
        };
      callbacks.nodes.newNode = createCallback; 
    },
    createViewCBs = function() {
      var prefix = "control_event_view",
        idprefix = prefix + "_",
        nodeCallback = function(n) {
          modalDialogHelper.createModalViewDialog(
            "View Node " + n._id,
            "control_event_node_view_",
            n._data,
            function() {
              modalDialogHelper.createModalEditDialog(
                "Edit Node " + n._id,
                "control