(function(global){	// BEGIN CLOSURE

var Joint = global.Joint;

var point = Joint.point;
var rect = Joint.rect;

/**
 * @name Joint.dia
 * @namespace Holds functionality related to all diagrams and their elements.
 */
var dia = Joint.dia = {
    /**
     * Current dragged object.
     * @private
     */
    _currentDrag: false,
    /**
     * Current zoomed object.
     * @private
     */
    _currentZoom: false,
    /**
     * Table with all registered objects.
     *  - registered objects can embed and can be embedded
     *  - the table is of the form: {RaphaelPaper1: [shape1, shape2, ...]}
     * @private
     */
    _registeredObjects: {},
    /**
     * Table whith all registered joints.
     *  - the table is of the form: {RaphaelPaper1: [joint1, joint2, ...]}
     * @private
     */
    _registeredJoints: {},
    /**
     * Create new joint and register it. All joints appearing in a diagram should
     * be created using this function. Otherwise they won't be registered and
     * therefore not serialized when needed.
     * @param {Object} args Joint parameters.
     * @see Joint
     * @return {Joint}
     */
    Joint: function(args){
	var j = Joint.apply(null, arguments);
	this.registerJoint(j);
	return j;
    },
    /**
     * Returns registered elements of the current paper.
     * @return {array} Array of registered elements.
     */
    registeredElements: function(){
	return (this._registeredObjects[Joint.paper().euid()] || (this._registeredObjects[Joint.paper().euid()] = []));
    },
    /**
     * Returns registered joints of the current paper.
     * @return {array} Array of registered joints.
     */
    registeredJoints: function(){
	return (this._registeredJoints[Joint.paper().euid()] || (this._registeredJoints[Joint.paper().euid()] = []));
    },
    /**
     * Register object to the current paper.
     * You don't have to use this method unless you really know what you're doing.
     * @param {Element|Joint} obj Object to be registered.
     * @return {Element|Joint} Registered object.
     */
    register: function(obj){
	(this._registeredObjects[Joint.paper().euid()] || (this._registeredObjects[Joint.paper().euid()] = [])).push(obj);
    },
    /**
     * Cancel registration of an element in the current paper.
     * @param {Element} obj Object to be unregistered.
     */
    unregister: function(obj){
	var register = (this._registeredObjects[Joint.paper().euid()] || (this._registeredObjects[Joint.paper().euid()] = [])),
	    idx = register.length;
	while (idx--)
	    if (register[idx] === obj)
		register.splice(idx, 1);
    },
    /**
     * Register joint to the current paper. Avoid registering the the same joint twice.
     * You don't have to use this method unless you really know what you're doing.
     * @param {Joint} j Joint object to be registered.
     */
    registerJoint: function(j){
	(this._registeredJoints[Joint.paper().euid()] || (this._registeredJoints[Joint.paper().euid()] = [])).push(j);
    },
    /**
     * Cancel registration of a joint in the current paper.
     * @param {Joint} j Joint to be unregistered.
     */
    unregisterJoint: function(j){
	var register = (this._registeredJoints[Joint.paper().euid()] || (this._registeredJoints[Joint.paper().euid()] = [])),
	    idx = register.length;
	while (idx--)
	    if (register[idx] === j)
		register.splice(idx, 1);
    }
};

/**
 * Abstract object of all diagram elements.
 * This object is never used directly, instead, specific diagram elements inherits from it.
 * Allows easy creation of new specific diagram elements preserving all features that Joint library and Joint.dia plugin offer.
 * <h3>Wrapper</h3>
 *  All custom elements must have a wrapper set. Wrapper is the key object that Joint library counts with.
 *  There cannot be any element without a wrapper. Usually it is an object which wraps all the subelements
 *  that a specific diagram element contains. The wrapper must be set in init method.
 *  To set a wrapper, use setWrapper(aWrapper) method. The single parameter to the method is a Raphaël vector object.
 *  Later on, you can access this object using wrapper property.
 * <h3>Inner</h3>
 *  Inner objects are subelements of an element. Although they are optional, they are commonly used. To add a subelement
 *  to the element, use addInner(anInner) method. It takes a Raphaël vector object as an argument. All inner objects are
 *  placed to an array that you can access using inner property.
 * <h3><i>init</i> method</h3>
 *  The <i>init</i> method has to be part of every element you create. It takes all element options as an argument,
 *  sets wrapper and adds inners.
 * <h3><i>joint</i> method</h3>
 *  If you have specific elements, in which connections are not controlled by wrapper, you can implement your own joint method.
 * <h3><i>zoom</i> method</h3>
 *  As Joint.dia library does not know how your specific element should behave after scaling, you can use zoom method to implement
 *  the desired behaviour.
 * @name Element
 * @memberOf Joint.dia
 * @constructor
 * @example
var mydia = Joint.dia.mydia = {};
var Element = Joint.dia.Element;

mydia.MyElement = Element.extend({
  // init method must be always presented
  init: function(properties){
    var p = this.properties;
    // parameters processing
    p.position = properties.position;
    p.radius = properties.radius || 30;
    // every element must have a wrapper
    this.setWrapper(this.paper.circle(p.position.x, p.position.y, p.radius));
    // optional inner elements
    this.addInner(this.paper.text(p.position.x, p.position.y, "my element"));
  }
});

// ...

var e = mydia.MyElement.create({
  position: {x: 50, y: 50},
  radius: 20
});
 */
var Element = dia.Element = function(){};

/**
 * Use this to instantiate particular elements.
 * @private
 */
Element.create = function(properties){
    var instance = new this(properties);
    if (instance.init) instance.init(properties);
    instance.defaults(instance.properties);
    instance.paper.safari();        // fix webkit bug
    return instance;
};

/**
 * @private
 */
Element.extend = function(prototype){
    var C = prototype.constructor = function(properties){
	this.construct(properties);
    };
    C.base = this;
    var proto = C.prototype = new this();
    Joint.Mixin(proto, prototype);
    Joint.Supplement(C, this);
    return C;
};

Element.prototype = {
    parentElement: null,
    toolbox: null,
    _isElement: true,
    // auxiliaries for scaling and translating
    lastScaleX: 1.0,
    lastScaleY: 1.0,
    dx: undefined,
    dy: undefined,
    // original bounding box (before scaling a translating)
    // set in setWrapper()
    origBBox: undefined,

    construct: function(properties){
	this.properties = {
	    dx: 0, dy: 0,		// translation
	    rot: 0,			// rotation
	    sx: 1.0, sy: 1.0,		// scale
	    module: this.module,
	    object: this.object,
	    parent: properties.parent
	};
	this.wrapper = null;
        this.shadow = null;
        this.shadowAttrs = {
            stroke: 'none', 
            fill: '#999', 
            translation: '7,7',
            opacity: 0.5
        };
	this.inner = [];
	// ghost attributes
	this.ghostAttrs = {
	    opacity: 0.5,
	    "stroke-dasharray": "-",
	    stroke: "black"
	};
	this._opt = {
	    draggable: true,	// enable dragging?
	    ghosting: false,		// enable ghosting?
	    toolbox: false		// enable toolbox?
	};

	this.paper = Joint.paper();
	dia.register(this); // register me in the global table
    },
    defaults: function(properties) {
        if (properties.shadow) {
            Joint.Mixin(this.shadowAttrs, properties.shadow);
            this.createShadow();
        }
    },
    /**
     * @methodOf Joint.dia.Element#
     * @return Element unique id.
     */
    euid: function(){
	return Joint.generateEuid.call(this);
    },
    // this is needed in joint library when
    // manipulating with a raphael object joints array
    // - just delegate joints array methods to the wrapper
    joints: function(){
	return this.wrapper.joints();
    },

    /**
     * Used in joint.js for unified access to the wrapper.
     * For all RaphaelObjects returns just this.
     * @private
     * @return {RaphaelObject} Return wrapper.
     */
    yourself: function(){
	return this.wrapper;
    },

    updateJoints: function(){
	var joints = this.wrapper.joints();
	if (joints){
	    for (var i = 0, l = joints.length; i < l; i++){
		joints[i].update();
	    }
	}
    },

    /**
     * Toggle ghosting of the element.
     * Dragging a diagram object causes moving of the wrapper and all inners, and update
     * of all correspondent connections. It can be sometimes expensive. If your elements
     * are complex and you want to prevent all this rendering and computations,
     * you can enable ghosting. It means that only a ghost of your wrapper will be dragged.
     * @methodOf Joint.dia.Element#
     * @return {Element}
     */
    toggleGhosting: function(){
	this._opt.ghosting = !this._opt.ghosting;
	return this;
    },

    /**
     * Create a ghost shape which is used when dragging.
     * (in the case _opt.ghosting is enabled)
     * @private
     */
    createGhost: function(){
        this.ghost = this.cloneWrapper(this.ghostAttrs);
    },

    /**
     * Create a shadow.
     * @private
     */
    createShadow: function(){
        this.shadowAttrs.rotation = this.wrapper.attrs.rotation;
        this.shadow = this.cloneWrapper(this.shadowAttrs);
        this.shadow.toBack();
    },

    /**
     * Creates the same object as the wrapper is.
     * Used for ghosting and shadows.
     * @private
     * @return {RaphaelObject} created clone
     */
    cloneWrapper: function(attrs) {
	var wa = this.wrapper.attrs,
	    paper = this.wrapper.paper,
            clone;

	switch (this.wrapper.type) {
	case "rect":
	    clone = paper.rect(wa.x, wa.y, wa.width, wa.height, wa.r);
	    break;
	case "circle":
	    clone = paper.circle(wa.cx, wa.cy, wa.r);
	    break;
	case "ellipse":
	    clone = paper.ellipse(wa.cx, wa.cy, wa.rx, wa.ry);
	    break;
	default:
	    break;
	}
	clone.attr(attrs);
        return clone;
    },

    /**
     * Get object position.
     * @private
     * @return point
     */
    objPos: function(objname){
	switch (this[objname].type){
	case "rect":
	    return point(this[objname].attr("x"), this[objname].attr("y"));
	case "circle":
	case "ellipse":
	    return point(this[objname].attr("cx"), this[objname].attr("cy"));
	default:
	    break;
	}
    },

    wrapperPos: function(){
	return this.objPos("wrapper");
    },
    ghostPos: function(){
	return this.objPos("ghost");
    },

    /**
     * Sends the wrapper and all inners to the front.
     * @methodOf Joint.dia.Element#
     * @return {Element}
     */
    toFront: function(){
        this.shadow && this.shadow.toFront();
	this.wrapper && this.wrapper.toFront();
	for (var i = 0, len = this.inner.length; i < len; i++)
	    this.inner[i].toFront();
	return this;
    },

    /**
     * Sends the wrapper and all inners to the back.
     * @methodOf Joint.dia.Element#
     * @return {Element}
     */
    toBack: function(){
	for (var i = this.inner.length - 1; i >= 0; --i)
	    this.inner[i].toBack();
	this.wrapper && this.wrapper.toBack();
        this.shadow && this.shadow.toBack();
	return this;
    },

    /**
     * dia.Element mousedown event.
     * @private
     */
    dragger: function(e){
	if (!this.wholeShape._opt.draggable) return;
	dia._currentDrag = this.wholeShape;
	if (dia._currentDrag._opt.ghosting){
	    dia._currentDrag.createGhost();
	    dia._currentDrag.ghost.toFront();
	} else
	    dia._currentDrag.toFront();

	dia._currentDrag.removeToolbox();
	// small hack to get the connections to front
	dia._currentDrag.translate(1,1);
	dia._currentDrag.translate(-1,-1);

	dia._currentDrag.dx = e.clientX;
	dia._currentDrag.dy = e.clientY;
	e.preventDefault && e.preventDefault();
    },

    /**
     * dia.Element zoom tool mousedown event.
     * @private
     */
    zoomer: function(e){
	dia._currentZoom = this;
	dia._currentZoom.toFront();
	dia._currentZoom.removeToolbox();

	var bb = rect(dia._currentZoom.origBBox);
	dia._currentZoom.dx = e.clientX;
	dia._currentZoom.dy = e.clientY;
	dia._currentZoom.dWidth = bb.width * dia._currentZoom.lastScaleX;
	dia._currentZoom.dHeight = bb.height * dia._currentZoom.lastScaleY;

	e.preventDefault && e.preventDefault();
    },
    /**
     * Move the element by offsets.
     * @methodOf Joint.dia.Element#
     * @param {Number} dx Offset in x-axis.
     * @param {Number} dy Offset in y-axis.
     */
    translate: function(dx, dy){
	// save translation
	this.properties.dx += dx;
	this.properties.dy += dy;
	// translate wrapper, all inner and toolbox
	this.wrapper.translate(dx, dy);
	this.shadow && this.shadow.translate(dx, dy);
	for (var i = this.inner.length - 1; i >= 0; --i){
	    this.inner[i].translate(dx, dy);
	}
	this.translateToolbox(dx, dy);
        this.paper.safari();
    },

    /**
     * Add wrapper.
     * @methodOf Joint.dia.Element#
     * @param {RaphaelObject} s Vector object specifying a wrapper.
     */
    setWrapper: function(s){
	this.wrapper = s;			// set wrapper
	this.wrapper.wholeShape = this;		// set wrapper's reference to me
	this.type = this.wrapper.type;		// set my type
	this.origBBox = this.wrapper.getBBox();	// save original bounding box
	// if dragging enabled, register mouse down event handler
	if (this._opt && this._opt.draggable){
	    this.wrapper.mousedown(this.dragger);
	    this.wrapper.node.style.cursor = "move";
	}
	// make sure wrapper has the joints method
	if (!this.wrapper.joints){
	    this.wrapper._joints = [];
	    this.wrapper.joints = function(){ return this._joints; };
	}
	// add toolbox if enabled
	this.addToolbox();
	return this;
    },

    /**
     * Add a subelement.
     * @methodOf Joint.dia.Element#
     * @param {Element} s The subelement to be added.
     * @return {Element} this
     */
    addInner: function(s){
	this.inner.push(s);
	// @remove one of them?
	s.wholeShape = this;
	s.parentElement = this;
	if (s._isElement) s.properties.parent = this.euid();
	// if dragging enabled, register mouse down event handler
	if (!s._isElement && this._opt && this._opt.draggable){
	    s.mousedown(this.dragger);
	    s.node.style.cursor = "move";
	}
	s.toFront();	// always push new inner to the front
	return this;
    },

    /**
     * Remove a subelement.
     * @methodOf Joint.dia.Element#
     * @param {Element} s The subelement to be removed.
     * @return {Element} this
     */
    delInner: function(s){
	var
	i = 0,
	len = this.inner.length;
	for (; i < len; i++)
	    if (this.inner[i] == s)
		break;
	if (i < len){
	    this.inner.splice(i, 1);
	    s.parentElement = null;
	    if (s._isElement) s.properties.parent = undefined;
	}
	return this;
    },

    /**
     * Show toolbox.
     * @private
     */
    addToolbox: function(){
	// do not show toolbox if it is not enabled
	if (!this._opt.toolbox){
	    return this;
	}

	var
	self = this,
	bb = this.wrapper.getBBox(),	// wrapper bounding box
	tx = bb.x - 10,	// toolbox x position
	ty = bb.y - 22;	// toolbox y position

	this.toolbox = [];
	this.toolbox.push(this.paper.rect(tx, ty, 33, 22, 5).attr({fill: "white"}));
	// zoom in/out (mint icon: search.png)
	this.toolbox.push(this.paper.image("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAYAAACprHcmAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAE5SURBVHjaYvz//z8DsQAggFhARGRkpBETE1M/kGkOxIz//v078+HDh4odO3acBPJ//4eaCBBADCA6Kirq4JlzJ978/vPrNwifOHX4fUhIyFmgvDQQs4HUgDBAALFAbTDX1zNiZmFmBfONDM14WFlZdYFMCSD+AsS/QOIAAcQEVcyIw5m8IJNhHIAAAisGufHMuZNfgE74A8Knzx7/LiLO91tfXx9kOgsjEIDUAQQQ2FqQZ3q7Jk6AWs2gqCbOkZDn8l9AiLuNi4vrxfHjx7cC1X8HCCCwYqiv/aBu5NXQ0FD9+/dfr4uf/te7N1/Mu337ttmbN2/uAwQQzIO/gfg11DNsN4BA/LD4n8f33swF8v8DFQoAaS6AAGLEFilQN3JCbQLhH0B8HyCAGHHFIFQDB1QTSNEXgAADAEQ2gYZ9CcycAAAAAElFTkSuQmCC", tx, ty, 11, 11));
	this.toolbox[this.toolbox.length-1].toFront();
	Joint.addEvent(this.toolbox[this.toolbox.length-1].node, "mousedown", function(e){
			   dia.Element.prototype.zoomer.apply(self, [e]);
		       });
	// embed (mint icon: page_spearmint_up.png)
	this.toolbox.push(this.paper.image("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAYAAACprHcmAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAEJSURBVHjaYvj//z8DFGOAnz9/rjl27Jg0AwMDExAzAAQQI0ghFPz/8usZjM3ACJTnYBEC0iyfmZmZZYBCXwECiAkm+evXL4bff34w/P33C4z//PvB8O33awYmJiZeoDQ/ELMBBBALSKGJiQkPOzs7AxsbC8OaTXMZWFhZoEb8g5nFDsTMAAHEBFIIZLwCuo/hy5dvDCF+yQx/fv+BuAvhRDAACCCQM0AO5YRJfv78lSE+Ko/h79+/DP8RJoMBQACheHDv4wYGdOAs28DAyMioCmS+AAggJgYSAEAAoZiMUxHUZIAAYkES4AJSQjD3o4HvQPwXIIDgJgMVM4PCEhREWBT/BUUFQIABAMuFbgea+o0EAAAAAElFTkSuQmCC", tx + 22, ty, 11, 11));
	this.toolbox[this.toolbox.length-1].toFront();
	this.toolbox[this.toolbox.length-1].node.onclick = function(){ self.embed(); };
	// unembed (mint icon: page_spearmint_down.png)
	this.toolbox.push(this.paper.image("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAYAAACprHcmAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAEJSURBVHjaYvj//z8DFGOAnz9/rjl27Jg0AwMDExAzAAQQI0ghFPz/8usZjM3ACJTnYBEC0iyfmZmZZYBCXwECiIkBCfz99wuO//z7wfDt92sGJiYmXqAUPxCzAQQQi4mJyX0gQwFZExcXJ8OaTXMYODmZYULsQMwMEEAgk9WB+D0jIyNElJ2NYdXG2QzsHOwMSE4EA4AAYjpz5swvIC3By8sLVrh2yzygiRwQTzD8Q1EMEEBwD/779+//7gcNDCysKN5gcJZtYADaqgpkvgAIILgM0CMYCtEBQAChBB1ORVCTAQKIBUmAC0gJATEnFvXfQSELEEBwk4GKQeHEBgoiLIr/AvEvgAADAH4mYO9cg5S2AAAAAElFTkSuQmCC", tx + 11, ty, 11, 11));
	this.toolbox[this.toolbox.length-1].toFront();
	this.toolbox[this.toolbox.length-1].node.onclick = function(){ self.unembed(); };
	// delete (icon: stop.png)
//	this.toolbox.push(this.paper.image("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAYAAACprHcmAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9oFEBQbDFwnwRsAAAE8SURBVBjTVZG9agJREEbP1TWL266wja2tWggipEhpIxh9gIUgiIW1vZWvkHJJHVLYig+ghWARbGzEYgMKrojr/t4UNwoZmGY4882BEfyVHA5HmOaEXA6khCSB83nK4fAmHOcAoAFI2+7RaIwxTQhDiGO1cLu1WK3egS6AkIPBiFptjGU9kc3Cfg++D4WCSg8CyWLxRRD0MxjGBMNQYLMJlQoUi9BuQ6kEx6PAMDrAs4aUcL3C5QLLJVSrUC6D68J8Duez0gIySKk8fV8ppCnoOux24HkQRUoH0EhTNTBNpeG6CqzX4XSC2eyRrBEEUzyvha7Deq1Oe54CXVcFxfE3sBXStgsYxjuW9UqaCsJQAfcOwx/i+EU4zkY8ntLrfZLPdwB1NklUYpJ0heNsHk8BIIr6RNEH/2t7BwF+AeKFndSgPkjIAAAAAElFTkSuQmCC", tx + 11, ty + 11, 11, 11));
        this.toolbox.push(this.paper.path("M24.778,21.419 19.276,15.917 24.777,10.415 21.949,7.585 16.447,13.087 10.945,7.585 8.117,10.415 13.618,15.917 8.116,21.419 10.946,24.248 16.447,18.746 21.948,24.248").attr({fill: "#000", stroke: "none"}).translate(tx, ty).scale(0.5));
	this.toolbox[this.toolbox.length-1].toFront();
	this.toolbox[this.toolbox.length-1].node.onclick = function(){ self.remove(); };
	// clone (mint icon: sound_grey.png)
	this.toolbox.push(this.paper.image("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAYAAACprHcmAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAEjSURBVHjaYvz//z8DsQAggJjwSaanpwsBMReMDxBATAQMO/zv379eRkZGdiBmAgggJiymqaWlpS0GSrIAFZ4A0h5AYR4gZgEIICaoAg6ggolACea/f/9aAulAoDD3169fNwPZ0kA2B0gxQADBTBYECuYCaa7bt2/vACkEYs4zZ84cA9KsQAwKBUaAAGIBqfzz5w8jExPTRiCTXUFBwQ9IfwP5x8TExAJI/4IpBgggsOJ58+Y9B1JRQMwGdOdjoFP2ghRwcnL6A4P2KUghiA8QQGDFQIH/QGf8BDJ/L1myZC8fHx/IeiZmZmbr379/H4ApBgggFlgoANX/A1L/gJoYP336BHIG47Nnz1zu3r0LUvgD5FqAAGLEF4Og0EHy4G+AAAMAho1gqqugDLgAAAAASUVORK5CYII=", tx, ty + 11, 11, 11));
	this.toolbox[this.toolbox.length-1].toFront();
	this.toolbox[this.toolbox.length-1].node.onmousedown = function(){ dia._currentDrag = self.clone()[0]; console.log(dia._currentDrag[0])};
	// toolbox wrapper
	return this;
    },

    /**
     * Hide (remove) toolbox.
     * @todo Will be public after it is properly tested.
     * @private
     */
    removeToolbox: function(){
	if (this.toolbox)
	    for (var i = this.toolbox.length - 1; i >= 0; --i)
		this.toolbox[i].remove();
	this.toolbox = null;
	return this;
    },

    /**
     * Show/hide toolbox.
     * @todo Will be public after it is properly tested.
     * @private
     */
    toggleToolbox: function(){
	this._opt.toolbox = !this._opt.toolbox;
	if (this._opt.toolbox){
	    this.addToolbox();
	} else {
	    this.removeToolbox();
	}
	return this;
    },

    /**
     * Move toolbox by offset (dx, dy).
     * @private
     */
    translateToolbox: function(dx, dy){
	if (this.toolbox)
	    for (var i = this.toolbox.length - 1; i >= 0; --i)
		this.toolbox[i].translate(dx, dy);
    },

    /**
     * Disconnects element from all joints. Empties the element joints array.
     * Note that it preserves registration of the element in its joints.
     * @methodOf Joint.dia.Element#
     */
    disconnect: function(){
	var joints = this.joints(), idx = joints.length, j;
	while (idx--){
	    j = joints[idx];

	    if (j.endObject().wholeShape === this){
		j.freeJoint(j.endObject());
		j.draw(["dummyEnd"]);
		j.update();
	    }
	    if (j.startObject().wholeShape === this){
		j.freeJoint(j.startObject());
		j.draw(["dummyStart"]);
		j.update();
	    }
	}
    },

    /**
     * Unregister the element from its joints registeredObjects.
     * After the call, the element is not registered in any of its joints.
     * @private
     */
    unregisterFromJoints: function(){
	var joints = this.joints(), idx = joints.length;
	while (idx--) joints[idx].unregister(this);
	return this;
    },

    /**
     * Remove element.
     * @methodOf Joint.dia.Element#
     * @return {null}
     */
    remove: function(){
	var inners = this.inner, idx = inners.length;
	this.unregisterFromJoints();
	this.disconnect();
	this.removeToolbox();
	this.unembed();
	while (idx--) inners[idx].remove();
	this.wrapper.remove();
        this.shadow && this.shadow.remove();
	dia.unregister(this);
        this.removed = true;
        return null;
    },

    /**
     * Remove element and all joints pointing from and to this element.
     * @methodOf Joint.dia.Element#
     * @return {null}
     */
    liquidate: function(){
	var joints = this.joints(), idx = joints.length, j, inners = this.inner;
	// remove joints
	while (idx--){
	    j = joints[idx];
	    j.freeJoint(j.startObject());
	    j.freeJoint(j.endObject());
	    j.clean(["connection", "startCap", "endCap", "handleStart", "handleEnd", "label"]);
	    dia.unregisterJoint(j);
	    j.unregister(this);
	}
	this.removeToolbox();
	this.unembed();
	// liquidate subelements
	idx = inners.length;
	while (idx--){
	    if (inners[idx].liquidate) inners[idx].liquidate();
	    else inners[idx].remove();
	}
	this.wrapper.remove();
        this.shadow && this.shadow.remove();
	dia.unregister(this);
        this.removed = true;
        return null;
    },

    /**
     * Enable/disable dragging of the element.
     * @methodOf Joint.dia.Element#
     * @param {boolean} enable True/false.
     * @return {Element} Return this.
     */
    draggable: function(enable){
	this._opt.draggable = enable;
        this.wrapper.node.style.cursor = enable ? "move" : null;
        var idx = this.inner.length;
        while (idx--) this.inner[idx].node.style.cursor = enable ? "move" : null;
	return this;
    },

    /**
     * Highlights the element.
     * Override in inherited objects or @todo set in options.
     * @methodOf Joint.dia.Element#
     * @return {Element} Return this.
     */
    highlight: function(){
	this.wrapper.attr("stroke", "red");
	return this;
    },

    /**
     * Unhighlights the element.
     * @methodOf Joint.dia.Element#
     * @return {Element} Return this.
     */
    unhighlight: function(){
	this.wrapper.attr("stroke", this.properties.attrs.stroke || "#000");
	return this;
    },

    /**
     * Embed me into the first registered dia.Element whos bounding box
     * contains my bounding box origin. Both elements will behave as a whole.
     * @todo It is probably out of date. Retest!!!
     * @methodOf Joint.dia.Element#
     * @return {Element}
     */
    embed: function(){
	var
	ros = dia._registeredObjects[this.paper.euid()],
	myBB = rect(this.wrapper.getBBox()),
	embedTo = null;

	// for all registered objects (sharing the same raphael paper)
	for (var i = 0, len = ros.length; i < len; i++){
	    var
	    shape = ros[i],
	    shapeBB = rect(shape.getBBox());

	    // does shape contain my origin point?
	    if (shapeBB.containsPoint(myBB.origin()))
		embedTo = shape;	// if yes, save the shape

	    if (shape == this.parentElement){
		shape.delInner(this);

		// just for optimization, a shape can be a subshape of
		// only one shape, so if I have been deleted from my parent,
		// I am free, and further, if I know where to embed -> do not search deeper
		if (embedTo) break;
	    }
	}

	// embed if possible
	embedTo && embedTo.addInner(this);
	return this;
    },

    /**
     * Decouple embedded element from its parent.
     * @methodOf Joint.dia.Element#
     * @return {Element}
     */
    unembed: function(){
	if (this.parentElement){
	    this.parentElement.delInner(this);
	    this.parentElement = null;
	    this.properties.parent = undefined;
	}
	return this;
    },

    /**
     * Scale element.
     * @methodOf Joint.dia.Element#
     * @param {Number} sx Scale in x-axis.
     * @param {Number} &optional sy Scale in y-axis.
     * @example e.scale(1.5);
     */
    scale: function(sx, sy){
	// save translation
	this.properties.sx = sx;
	this.properties.sy = sy;

	this.shadow && this.shadow.scale.apply(this.shadow, arguments);
	this.wrapper.scale.apply(this.wrapper, arguments);
	this.zoom.apply(this, arguments);
	// apply scale to all subshapes that are Elements (were embeded)
	for (var i = 0, len = this.inner.length; i < len; i++){
	    var inner = this.inner[i];
	    if (inner._isElement){
		inner.scale.apply(inner, arguments);
	    }
	}
	if (this._doNotRedrawToolbox) return;
	this.removeToolbox();
	this.addToolbox();
    },
    /**
     * This method should be overriden by inherited elements to implement
     * the desired scaling behaviour.
     * @methodOf Joint.dia.Element#
     * @param {Number} sx Scale in x-axis.
     * @param {Number} &optional sy Scale in y-axis.
     */
    zoom: function(sx, sy){
	// does nothing, overriden by specific elements
    },

    /**
     * @methodOf Joint.dia.Element#
     * @return {Object} Bounding box of the element.
     */
    getBBox: function(){
	return this.wrapper.getBBox();
    },

    /**
     * @see Joint
     * @methodOf Joint.dia.Element#
     */
    joint: function(to, opt){
	var toobj = (to._isElement) ? to.wrapper : to,
	    j = this.wrapper.joint.apply(this.wrapper, [toobj, opt]);
	Joint.dia.registerJoint(j);
	return j;
    },

    /**
     * Delegate attr message to my wrapper.
     * @private
     */
    attr: function(){
	return Raphael.el.attr.apply(this.wrapper, arguments);
    }
};


/**
 * Document mousemove event.
 * @private
 */
Element.mouseMove = function(e){
    e = e || window.event;
    // object dragging
    if (dia._currentDrag){
	if (dia._currentDrag._opt.ghosting)	// if ghosting, move ghost
	    dia._currentDrag.ghost.translate(e.clientX - dia._currentDrag.dx, e.clientY - dia._currentDrag.dy);
	else 	// otherwise, move the whole shape
	    dia._currentDrag.translate(e.clientX - dia._currentDrag.dx, e.clientY - dia._currentDrag.dy);
	
	dia._currentDrag.dx = e.clientX;
	dia._currentDrag.dy = e.clientY;
    }

    // object zooming
    if (dia._currentZoom){
	var
	dx = e.clientX - dia._currentZoom.dx,
	dy = e.clientY - dia._currentZoom.dy;

	dia._currentZoom.dWidth -= dx;
	dia._currentZoom.dHeight -= dy;
	// correction
	if (dia._currentZoom.dWidth < 1) dia._currentZoom.dWidth = 1;
	if (dia._currentZoom.dHeight < 1) dia._currentZoom.dHeight = 1;

	// scaling parameters
	var
	sx = dia._currentZoom.dWidth / dia._currentZoom.origBBox.width,
	sy = dia._currentZoom.dHeight / dia._currentZoom.origBBox.height;

	// do not redraw toolbox because it is not there
	dia._currentZoom._doNotRedrawToolbox = true;
	dia._currentZoom.scale(sx, sy);	// scale
	r.safari();

	// save for later usage
	dia._currentZoom.dx = e.clientX;
	dia._currentZoom.dy = e.clientY;
	dia._currentZoom.lastScaleX = sx;
	dia._currentZoom.lastScaleY = sy;
    }
};

/**
 * Document mouseup event.
 * @private
 */
Element.mouseUp = function(e){
    // if ghosting is enabled, translate whole shape to the position of
    // the ghost, then remove ghost and update joints
    if (dia._currentDrag && dia._currentDrag._opt.ghosting){
	var
	gPos = dia._currentDrag.ghostPos(),
	wPos = dia._currentDrag.wrapperPos();

	dia._currentDrag.translate(gPos.x - wPos.x, gPos.y - wPos.y);
	dia._currentDrag.ghost.remove();
	dia._currentDrag.updateJoints();
    }
    // add toolbar again when dragging is stopped
    if (dia._currentDrag){
	dia._currentDrag.addToolbox();
	dia._currentDrag.toFront();
	// small hack: change slightely the position to get the connections to front
	dia._currentDrag.translate(1,1);
	dia._currentDrag.translate(-1,-1);
    }

    // add toolbar again when zooming is stopped
    if (dia._currentZoom){
	// remove toolbox, because scale above may create one,
	// so there would be two toolboxes after addToolbox() below
	dia._currentZoom.removeToolbox();
	dia._currentZoom.addToolbox();
	dia._currentZoom.toFront();
    }

 // ---
 // OTRS    
 // ---
     Core.Agent.Admin.ProcessManagement.Canvas.UpdateElementPosition(dia._currentDrag);
 // ---
    dia._currentDrag = false;
    dia._currentZoom = false;
};

Joint.addEvent(document, "mousemove", Element.mouseMove);
Joint.addEvent(document, "mouseup", Element.mouseUp);


})(this);	// END CLOSURE