1 (function(global){	// BEGIN CLOSURE
  2 
  3 var Joint = global.Joint;
  4 
  5 var point = Joint.point;
  6 var rect = Joint.rect;
  7 
  8 /**
  9  * @name Joint.dia
 10  * @namespace Holds functionality related to all diagrams and their elements.
 11  */
 12 var dia = Joint.dia = {
 13     /**
 14      * Current dragged object.
 15      * @private
 16      */
 17     _currentDrag: false,
 18     /**
 19      * Current zoomed object.
 20      * @private
 21      */
 22     _currentZoom: false,
 23     /**
 24      * Table with all registered objects.
 25      *  - registered objects can embed and can be embedded
 26      *  - the table is of the form: {RaphaelPaper1: [shape1, shape2, ...]}
 27      * @private
 28      */
 29     _registeredObjects: {},
 30     /**
 31      * Table whith all registered joints.
 32      *  - the table is of the form: {RaphaelPaper1: [joint1, joint2, ...]}
 33      * @private
 34      */
 35     _registeredJoints: {},
 36     /**
 37      * Create new joint and register it. All joints appearing in a diagram should
 38      * be created using this function. Otherwise they won't be registered and
 39      * therefore not serialized when needed.
 40      * @param {Object} args Joint parameters.
 41      * @see Joint
 42      * @return {Joint}
 43      */
 44     Joint: function(args){
 45 	var j = Joint.apply(null, arguments);
 46 	this.registerJoint(j);
 47 	return j;
 48     },
 49     /**
 50      * Returns registered elements of the current paper.
 51      * @return {array} Array of registered elements.
 52      */
 53     registeredElements: function(){
 54 	return (this._registeredObjects[Joint.paper().euid()] || (this._registeredObjects[Joint.paper().euid()] = []));
 55     },
 56     /**
 57      * Returns registered joints of the current paper.
 58      * @return {array} Array of registered joints.
 59      */
 60     registeredJoints: function(){
 61 	return (this._registeredJoints[Joint.paper().euid()] || (this._registeredJoints[Joint.paper().euid()] = []));
 62     },
 63     /**
 64      * Register object to the current paper.
 65      * You don't have to use this method unless you really know what you're doing.
 66      * @param {Element|Joint} obj Object to be registered.
 67      * @return {Element|Joint} Registered object.
 68      */
 69     register: function(obj){
 70 	(this._registeredObjects[Joint.paper().euid()] || (this._registeredObjects[Joint.paper().euid()] = [])).push(obj);
 71     },
 72     /**
 73      * Cancel registration of an element in the current paper.
 74      * @param {Element} obj Object to be unregistered.
 75      */
 76     unregister: function(obj){
 77 	var register = (this._registeredObjects[Joint.paper().euid()] || (this._registeredObjects[Joint.paper().euid()] = [])),
 78 	    idx = register.length;
 79 	while (idx--)
 80 	    if (register[idx] === obj)
 81 		register.splice(idx, 1);
 82     },
 83     /**
 84      * Register joint to the current paper. Avoid registering the the same joint twice.
 85      * You don't have to use this method unless you really know what you're doing.
 86      * @param {Joint} j Joint object to be registered.
 87      */
 88     registerJoint: function(j){
 89 	(this._registeredJoints[Joint.paper().euid()] || (this._registeredJoints[Joint.paper().euid()] = [])).push(j);
 90     },
 91     /**
 92      * Cancel registration of a joint in the current paper.
 93      * @param {Joint} j Joint to be unregistered.
 94      */
 95     unregisterJoint: function(j){
 96 	var register = (this._registeredJoints[Joint.paper().euid()] || (this._registeredJoints[Joint.paper().euid()] = [])),
 97 	    idx = register.length;
 98 	while (idx--)
 99 	    if (register[idx] === j)
100 		register.splice(idx, 1);
101     }
102 };
103 
104 /**
105  * Abstract object of all diagram elements.
106  * This object is never used directly, instead, specific diagram elements inherits from it.
107  * Allows easy creation of new specific diagram elements preserving all features that Joint library and Joint.dia plugin offer.
108  * <h3>Wrapper</h3>
109  *  All custom elements must have a wrapper set. Wrapper is the key object that Joint library counts with.
110  *  There cannot be any element without a wrapper. Usually it is an object which wraps all the subelements
111  *  that a specific diagram element contains. The wrapper must be set in init method.
112  *  To set a wrapper, use setWrapper(aWrapper) method. The single parameter to the method is a Raphaël vector object.
113  *  Later on, you can access this object using wrapper property.
114  * <h3>Inner</h3>
115  *  Inner objects are subelements of an element. Although they are optional, they are commonly used. To add a subelement
116  *  to the element, use addInner(anInner) method. It takes a Raphaël vector object as an argument. All inner objects are
117  *  placed to an array that you can access using inner property.
118  * <h3><i>init</i> method</h3>
119  *  The <i>init</i> method has to be part of every element you create. It takes all element options as an argument,
120  *  sets wrapper and adds inners.
121  * <h3><i>joint</i> method</h3>
122  *  If you have specific elements, in which connections are not controlled by wrapper, you can implement your own joint method.
123  * <h3><i>zoom</i> method</h3>
124  *  As Joint.dia library does not know how your specific element should behave after scaling, you can use zoom method to implement
125  *  the desired behaviour.
126  * @name Element
127  * @memberOf Joint.dia
128  * @constructor
129  * @example
130 var mydia = Joint.dia.mydia = {};
131 var Element = Joint.dia.Element;
132 
133 mydia.MyElement = Element.extend({
134   // init method must be always presented
135   init: function(properties){
136     var p = this.properties;
137     // parameters processing
138     p.position = properties.position;
139     p.radius = properties.radius || 30;
140     // every element must have a wrapper
141     this.setWrapper(this.paper.circle(p.position.x, p.position.y, p.radius));
142     // optional inner elements
143     this.addInner(this.paper.text(p.position.x, p.position.y, "my element"));
144   }
145 });
146 
147 // ...
148 
149 var e = mydia.MyElement.create({
150   position: {x: 50, y: 50},
151   radius: 20
152 });
153  */
154 var Element = dia.Element = function(){};
155 
156 /**
157  * Use this to instantiate particular elements.
158  * @private
159  */
160 Element.create = function(properties){
161     var instance = new this(properties);
162     if (instance.init) instance.init(properties);
163     instance.defaults(instance.properties);
164     instance.paper.safari();        // fix webkit bug
165     return instance;
166 };
167 
168 /**
169  * @private
170  */
171 Element.extend = function(prototype){
172     var C = prototype.constructor = function(properties){
173 	this.construct(properties);
174     };
175     C.base = this;
176     var proto = C.prototype = new this();
177     Joint.Mixin(proto, prototype);
178     Joint.Supplement(C, this);
179     return C;
180 };
181 
182 Element.prototype = {
183     parentElement: null,
184     toolbox: null,
185     _isElement: true,
186     // auxiliaries for scaling and translating
187     lastScaleX: 1.0,
188     lastScaleY: 1.0,
189     dx: undefined,
190     dy: undefined,
191     // original bounding box (before scaling a translating)
192     // set in setWrapper()
193     origBBox: undefined,
194 
195     construct: function(properties){
196 	this.properties = {
197 	    dx: 0, dy: 0,		// translation
198 	    rot: 0,			// rotation
199 	    sx: 1.0, sy: 1.0,		// scale
200 	    module: this.module,
201 	    object: this.object,
202 	    parent: properties.parent
203 	};
204 	this.wrapper = null;
205         this.shadow = null;
206         this.shadowAttrs = {
207             stroke: 'none', 
208             fill: '#999', 
209             translation: '7,7',
210             opacity: 0.5
211         };
212 	this.inner = [];
213 	// ghost attributes
214 	this.ghostAttrs = {
215 	    opacity: 0.5,
216 	    "stroke-dasharray": "-",
217 	    stroke: "black"
218 	};
219 	this._opt = {
220 	    draggable: true,	// enable dragging?
221 	    ghosting: false,		// enable ghosting?
222 	    toolbox: false		// enable toolbox?
223 	};
224 
225 	this.paper = Joint.paper();
226 	dia.register(this); // register me in the global table
227     },
228     defaults: function(properties) {
229         if (properties.shadow) {
230             Joint.Mixin(this.shadowAttrs, properties.shadow);
231             this.createShadow();
232         }
233     },
234     /**
235      * @methodOf Joint.dia.Element#
236      * @return Element unique id.
237      */
238     euid: function(){
239 	return Joint.generateEuid.call(this);
240     },
241     // this is needed in joint library when
242     // manipulating with a raphael object joints array
243     // - just delegate joints array methods to the wrapper
244     joints: function(){
245 	return this.wrapper.joints();
246     },
247 
248     /**
249      * Used in joint.js for unified access to the wrapper.
250      * For all RaphaelObjects returns just this.
251      * @private
252      * @return {RaphaelObject} Return wrapper.
253      */
254     yourself: function(){
255 	return this.wrapper;
256     },
257 
258     updateJoints: function(){
259 	var joints = this.wrapper.joints();
260 	if (joints){
261 	    for (var i = 0, l = joints.length; i < l; i++){
262 		joints[i].update();
263 	    }
264 	}
265     },
266 
267     /**
268      * Toggle ghosting of the element.
269      * Dragging a diagram object causes moving of the wrapper and all inners, and update
270      * of all correspondent connections. It can be sometimes expensive. If your elements
271      * are complex and you want to prevent all this rendering and computations,
272      * you can enable ghosting. It means that only a ghost of your wrapper will be dragged.
273      * @methodOf Joint.dia.Element#
274      * @return {Element}
275      */
276     toggleGhosting: function(){
277 	this._opt.ghosting = !this._opt.ghosting;
278 	return this;
279     },
280 
281     /**
282      * Create a ghost shape which is used when dragging.
283      * (in the case _opt.ghosting is enabled)
284      * @private
285      */
286     createGhost: function(){
287         this.ghost = this.cloneWrapper(this.ghostAttrs);
288     },
289 
290     /**
291      * Create a shadow.
292      * @private
293      */
294     createShadow: function(){
295         this.shadowAttrs.rotation = this.wrapper.attrs.rotation;
296         this.shadow = this.cloneWrapper(this.shadowAttrs);
297         this.shadow.toBack();
298     },
299 
300     /**
301      * Creates the same object as the wrapper is.
302      * Used for ghosting and shadows.
303      * @private
304      * @return {RaphaelObject} created clone
305      */
306     cloneWrapper: function(attrs) {
307 	var wa = this.wrapper.attrs,
308 	    paper = this.wrapper.paper,
309             clone;
310 
311 	switch (this.wrapper.type) {
312 	case "rect":
313 	    clone = paper.rect(wa.x, wa.y, wa.width, wa.height, wa.r);
314 	    break;
315 	case "circle":
316 	    clone = paper.circle(wa.cx, wa.cy, wa.r);
317 	    break;
318 	case "ellipse":
319 	    clone = paper.ellipse(wa.cx, wa.cy, wa.rx, wa.ry);
320 	    break;
321 	default:
322 	    break;
323 	}
324 	clone.attr(attrs);
325         return clone;
326     },
327 
328     /**
329      * Get object position.
330      * @private
331      * @return point
332      */
333     objPos: function(objname){
334 	switch (this[objname].type){
335 	case "rect":
336 	    return point(this[objname].attr("x"), this[objname].attr("y"));
337 	case "circle":
338 	case "ellipse":
339 	    return point(this[objname].attr("cx"), this[objname].attr("cy"));
340 	default:
341 	    break;
342 	}
343     },
344 
345     wrapperPos: function(){
346 	return this.objPos("wrapper");
347     },
348     ghostPos: function(){
349 	return this.objPos("ghost");
350     },
351 
352     /**
353      * Sends the wrapper and all inners to the front.
354      * @methodOf Joint.dia.Element#
355      * @return {Element}
356      */
357     toFront: function(){
358         this.shadow && this.shadow.toFront();
359 	this.wrapper && this.wrapper.toFront();
360 	for (var i = 0, len = this.inner.length; i < len; i++)
361 	    this.inner[i].toFront();
362 	return this;
363     },
364 
365     /**
366      * Sends the wrapper and all inners to the back.
367      * @methodOf Joint.dia.Element#
368      * @return {Element}
369      */
370     toBack: function(){
371 	for (var i = this.inner.length - 1; i >= 0; --i)
372 	    this.inner[i].toBack();
373 	this.wrapper && this.wrapper.toBack();
374         this.shadow && this.shadow.toBack();
375 	return this;
376     },
377 
378     /**
379      * dia.Element mousedown event.
380      * @private
381      */
382     dragger: function(e){
383 	if (!this.wholeShape._opt.draggable) return;
384 	dia._currentDrag = this.wholeShape;
385 	if (dia._currentDrag._opt.ghosting){
386 	    dia._currentDrag.createGhost();
387 	    dia._currentDrag.ghost.toFront();
388 	} else
389 	    dia._currentDrag.toFront();
390 
391 	dia._currentDrag.removeToolbox();
392 	// small hack to get the connections to front
393 	dia._currentDrag.translate(1,1);
394 
395 	dia._currentDrag.dx = e.clientX;
396 	dia._currentDrag.dy = e.clientY;
397 	e.preventDefault && e.preventDefault();
398     },
399 
400     /**
401      * dia.Element zoom tool mousedown event.
402      * @private
403      */
404     zoomer: function(e){
405 	dia._currentZoom = this;
406 	dia._currentZoom.toFront();
407 	dia._currentZoom.removeToolbox();
408 
409 	var bb = rect(dia._currentZoom.origBBox);
410 	dia._currentZoom.dx = e.clientX;
411 	dia._currentZoom.dy = e.clientY;
412 	dia._currentZoom.dWidth = bb.width * dia._currentZoom.lastScaleX;
413 	dia._currentZoom.dHeight = bb.height * dia._currentZoom.lastScaleY;
414 
415 	e.preventDefault && e.preventDefault();
416     },
417     /**
418      * Move the element by offsets.
419      * @methodOf Joint.dia.Element#
420      * @param {Number} dx Offset in x-axis.
421      * @param {Number} dy Offset in y-axis.
422      */
423     translate: function(dx, dy){
424 	// save translation
425 	this.properties.dx += dx;
426 	this.properties.dy += dy;
427 	// translate wrapper, all inner and toolbox
428 	this.wrapper.translate(dx, dy);
429 	this.shadow && this.shadow.translate(dx, dy);
430 	for (var i = this.inner.length - 1; i >= 0; --i){
431 	    this.inner[i].translate(dx, dy);
432 	}
433 	this.translateToolbox(dx, dy);
434         this.paper.safari();
435     },
436 
437     /**
438      * Add wrapper.
439      * @methodOf Joint.dia.Element#
440      * @param {RaphaelObject} s Vector object specifying a wrapper.
441      */
442     setWrapper: function(s){
443 	this.wrapper = s;			// set wrapper
444 	this.wrapper.wholeShape = this;		// set wrapper's reference to me
445 	this.type = this.wrapper.type;		// set my type
446 	this.origBBox = this.wrapper.getBBox();	// save original bounding box
447 	// if dragging enabled, register mouse down event handler
448 	if (this._opt && this._opt.draggable){
449 	    this.wrapper.mousedown(this.dragger);
450 	    this.wrapper.node.style.cursor = "move";
451 	}
452 	// make sure wrapper has the joints method
453 	if (!this.wrapper.joints){
454 	    this.wrapper._joints = [];
455 	    this.wrapper.joints = function(){ return this._joints; };
456 	}
457 	// add toolbox if enabled
458 	this.addToolbox();
459 	return this;
460     },
461 
462     /**
463      * Add a subelement.
464      * @methodOf Joint.dia.Element#
465      * @param {Element} s The subelement to be added.
466      * @return {Element} this
467      */
468     addInner: function(s){
469 	this.inner.push(s);
470 	// @remove one of them?
471 	s.wholeShape = this;
472 	s.parentElement = this;
473 	if (s._isElement) s.properties.parent = this.euid();
474 	// if dragging enabled, register mouse down event handler
475 	if (!s._isElement && this._opt && this._opt.draggable){
476 	    s.mousedown(this.dragger);
477 	    s.node.style.cursor = "move";
478 	}
479 	s.toFront();	// always push new inner to the front
480 	return this;
481     },
482 
483     /**
484      * Remove a subelement.
485      * @methodOf Joint.dia.Element#
486      * @param {Element} s The subelement to be removed.
487      * @return {Element} this
488      */
489     delInner: function(s){
490 	var
491 	i = 0,
492 	len = this.inner.length;
493 	for (; i < len; i++)
494 	    if (this.inner[i] == s)
495 		break;
496 	if (i < len){
497 	    this.inner.splice(i, 1);
498 	    s.parentElement = null;
499 	    if (s._isElement) s.properties.parent = undefined;
500 	}
501 	return this;
502     },
503 
504     /**
505      * Show toolbox.
506      * @private
507      */
508     addToolbox: function(){
509 	// do not show toolbox if it is not enabled
510 	if (!this._opt.toolbox){
511 	    return this;
512 	}
513 
514 	var
515 	self = this,
516 	bb = this.wrapper.getBBox(),	// wrapper bounding box
517 	tx = bb.x - 10,	// toolbox x position
518 	ty = bb.y - 22;	// toolbox y position
519 
520 	this.toolbox = [];
521 	this.toolbox.push(this.paper.rect(tx, ty, 33, 22, 5).attr({fill: "white"}));
522 	// zoom in/out (mint icon: search.png)
523 	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));
524 	this.toolbox[this.toolbox.length-1].toFront();
525 	Joint.addEvent(this.toolbox[this.toolbox.length-1].node, "mousedown", function(e){
526 			   dia.Element.prototype.zoomer.apply(self, [e]);
527 		       });
528 	// embed (mint icon: page_spearmint_up.png)
529 	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));
530 	this.toolbox[this.toolbox.length-1].toFront();
531 	this.toolbox[this.toolbox.length-1].node.onclick = function(){ self.embed(); };
532 	// unembed (mint icon: page_spearmint_down.png)
533 	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));
534 	this.toolbox[this.toolbox.length-1].toFront();
535 	this.toolbox[this.toolbox.length-1].node.onclick = function(){ self.unembed(); };
536 	// delete (icon: stop.png)
537 //	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));
538         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));
539 	this.toolbox[this.toolbox.length-1].toFront();
540 	this.toolbox[this.toolbox.length-1].node.onclick = function(){ self.remove(); };
541 	// clone (mint icon: sound_grey.png)
542 	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));
543 	this.toolbox[this.toolbox.length-1].toFront();
544 	this.toolbox[this.toolbox.length-1].node.onmousedown = function(){ dia._currentDrag = self.clone()[0]; console.log(dia._currentDrag[0])};
545 	// toolbox wrapper
546 	return this;
547     },
548 
549     /**
550      * Hide (remove) toolbox.
551      * @todo Will be public after it is properly tested.
552      * @private
553      */
554     removeToolbox: function(){
555 	if (this.toolbox)
556 	    for (var i = this.toolbox.length - 1; i >= 0; --i)
557 		this.toolbox[i].remove();
558 	this.toolbox = null;
559 	return this;
560     },
561 
562     /**
563      * Show/hide toolbox.
564      * @todo Will be public after it is properly tested.
565      * @private
566      */
567     toggleToolbox: function(){
568 	this._opt.toolbox = !this._opt.toolbox;
569 	if (this._opt.toolbox){
570 	    this.addToolbox();
571 	} else {
572 	    this.removeToolbox();
573 	}
574 	return this;
575     },
576 
577     /**
578      * Move toolbox by offset (dx, dy).
579      * @private
580      */
581     translateToolbox: function(dx, dy){
582 	if (this.toolbox)
583 	    for (var i = this.toolbox.length - 1; i >= 0; --i)
584 		this.toolbox[i].translate(dx, dy);
585     },
586 
587     /**
588      * Disconnects element from all joints. Empties the element joints array.
589      * Note that it preserves registration of the element in its joints.
590      * @methodOf Joint.dia.Element#
591      */
592     disconnect: function(){
593 	var joints = this.joints(), idx = joints.length, j;
594 	while (idx--){
595 	    j = joints[idx];
596 
597 	    if (j.endObject().wholeShape === this){
598 		j.freeJoint(j.endObject());
599 		j.draw(["dummyEnd"]);
600 		j.update();
601 	    }
602 	    if (j.startObject().wholeShape === this){
603 		j.freeJoint(j.startObject());
604 		j.draw(["dummyStart"]);
605 		j.update();
606 	    }
607 	}
608     },
609 
610     /**
611      * Unregister the element from its joints registeredObjects.
612      * After the call, the element is not registered in any of its joints.
613      * @private
614      */
615     unregisterFromJoints: function(){
616 	var joints = this.joints(), idx = joints.length;
617 	while (idx--) joints[idx].unregister(this);
618 	return this;
619     },
620 
621     /**
622      * Remove element.
623      * @methodOf Joint.dia.Element#
624      * @return {null}
625      */
626     remove: function(){
627 	var inners = this.inner, idx = inners.length;
628 	this.unregisterFromJoints();
629 	this.disconnect();
630 	this.removeToolbox();
631 	this.unembed();
632 	while (idx--) inners[idx].remove();
633 	this.wrapper.remove();
634 	dia.unregister(this);
635         this.removed = true;
636         return null;
637     },
638 
639     /**
640      * Remove element and all joints pointing from and to this element.
641      * @methodOf Joint.dia.Element#
642      * @return {null}
643      */
644     liquidate: function(){
645 	var joints = this.joints(), idx = joints.length, j, inners = this.inner;
646 	// remove joints
647 	while (idx--){
648 	    j = joints[idx];
649 	    j.freeJoint(j.startObject());
650 	    j.freeJoint(j.endObject());
651 	    j.clean(["connection", "startCap", "endCap", "handleStart", "handleEnd", "label"]);
652 	    dia.unregisterJoint(j);
653 	    j.unregister(this);
654 	}
655 	this.removeToolbox();
656 	this.unembed();
657 	// liquidate subelements
658 	idx = inners.length;
659 	while (idx--){
660 	    if (inners[idx].liquidate) inners[idx].liquidate();
661 	    else inners[idx].remove();
662 	}
663 	this.wrapper.remove();
664 	dia.unregister(this);
665         this.removed = true;
666         return null;
667     },
668 
669     /**
670      * Enable/disable dragging of the element.
671      * @methodOf Joint.dia.Element#
672      * @param {boolean} enable True/false.
673      * @return {Element} Return this.
674      */
675     draggable: function(enable){
676 	this._opt.draggable = enable;
677         this.wrapper.node.style.cursor = enable ? "move" : null;
678         var idx = this.inner.length;
679         while (idx--) this.inner[idx].node.style.cursor = enable ? "move" : null;
680 	return this;
681     },
682 
683     /**
684      * Highlights the element.
685      * Override in inherited objects or @todo set in options.
686      * @methodOf Joint.dia.Element#
687      * @return {Element} Return this.
688      */
689     highlight: function(){
690 	this.wrapper.attr("stroke", "red");
691 	return this;
692     },
693 
694     /**
695      * Unhighlights the element.
696      * @methodOf Joint.dia.Element#
697      * @return {Element} Return this.
698      */
699     unhighlight: function(){
700 	this.wrapper.attr("stroke", this.properties.attrs.stroke || "#000");
701 	return this;
702     },
703 
704     /**
705      * Embed me into the first registered dia.Element whos bounding box
706      * contains my bounding box origin. Both elements will behave as a whole.
707      * @todo It is probably out of date. Retest!!!
708      * @methodOf Joint.dia.Element#
709      * @return {Element}
710      */
711     embed: function(){
712 	var
713 	ros = dia._registeredObjects[this.paper.euid()],
714 	myBB = rect(this.wrapper.getBBox()),
715 	embedTo = null;
716 
717 	// for all registered objects (sharing the same raphael paper)
718 	for (var i = 0, len = ros.length; i < len; i++){
719 	    var
720 	    shape = ros[i],
721 	    shapeBB = rect(shape.getBBox());
722 
723 	    // does shape contain my origin point?
724 	    if (shapeBB.containsPoint(myBB.origin()))
725 		embedTo = shape;	// if yes, save the shape
726 
727 	    if (shape == this.parentElement){
728 		shape.delInner(this);
729 
730 		// just for optimization, a shape can be a subshape of
731 		// only one shape, so if I have been deleted from my parent,
732 		// I am free, and further, if I know where to embed -> do not search deeper
733 		if (embedTo) break;
734 	    }
735 	}
736 
737 	// embed if possible
738 	embedTo && embedTo.addInner(this);
739 	return this;
740     },
741 
742     /**
743      * Decouple embedded element from its parent.
744      * @methodOf Joint.dia.Element#
745      * @return {Element}
746      */
747     unembed: function(){
748 	if (this.parentElement){
749 	    this.parentElement.delInner(this);
750 	    this.parentElement = null;
751 	    this.properties.parent = undefined;
752 	}
753 	return this;
754     },
755 
756     /**
757      * Scale element.
758      * @methodOf Joint.dia.Element#
759      * @param {Number} sx Scale in x-axis.
760      * @param {Number} &optional sy Scale in y-axis.
761      * @example e.scale(1.5);
762      */
763     scale: function(sx, sy){
764 	// save translation
765 	this.properties.sx = sx;
766 	this.properties.sy = sy;
767 
768 	this.shadow && this.shadow.scale.apply(this.shadow, arguments);
769 	this.wrapper.scale.apply(this.wrapper, arguments);
770 	this.zoom.apply(this, arguments);
771 	// apply scale to all subshapes that are Elements (were embeded)
772 	for (var i = 0, len = this.inner.length; i < len; i++){
773 	    var inner = this.inner[i];
774 	    if (inner._isElement){
775 		inner.scale.apply(inner, arguments);
776 	    }
777 	}
778 	if (this._doNotRedrawToolbox) return;
779 	this.removeToolbox();
780 	this.addToolbox();
781     },
782     /**
783      * This method should be overriden by inherited elements to implement
784      * the desired scaling behaviour.
785      * @methodOf Joint.dia.Element#
786      * @param {Number} sx Scale in x-axis.
787      * @param {Number} &optional sy Scale in y-axis.
788      */
789     zoom: function(sx, sy){
790 	// does nothing, overriden by specific elements
791     },
792 
793     /**
794      * @methodOf Joint.dia.Element#
795      * @return {Object} Bounding box of the element.
796      */
797     getBBox: function(){
798 	return this.wrapper.getBBox();
799     },
800 
801     /**
802      * @see Joint
803      * @methodOf Joint.dia.Element#
804      */
805     joint: function(to, opt){
806 	var toobj = (to._isElement) ? to.wrapper : to,
807 	    j = this.wrapper.joint.apply(this.wrapper, [toobj, opt]);
808 	Joint.dia.registerJoint(j);
809 	return j;
810     },
811 
812     /**
813      * Delegate attr message to my wrapper.
814      * @private
815      */
816     attr: function(){
817 	return Raphael.el.attr.apply(this.wrapper, arguments);
818     }
819 };
820 
821 
822 /**
823  * Document mousemove event.
824  * @private
825  */
826 Element.mouseMove = function(e){
827     e = e || window.event;
828     // object dragging
829     if (dia._currentDrag){
830 	if (dia._currentDrag._opt.ghosting)	// if ghosting, move ghost
831 	    dia._currentDrag.ghost.translate(e.clientX - dia._currentDrag.dx, e.clientY - dia._currentDrag.dy);
832 	else	// otherwise, move the whole shape
833 	    dia._currentDrag.translate(e.clientX - dia._currentDrag.dx, e.clientY - dia._currentDrag.dy);
834 
835 	dia._currentDrag.dx = e.clientX;
836 	dia._currentDrag.dy = e.clientY;
837     }
838 
839     // object zooming
840     if (dia._currentZoom){
841 	var
842 	dx = e.clientX - dia._currentZoom.dx,
843 	dy = e.clientY - dia._currentZoom.dy;
844 
845 	dia._currentZoom.dWidth -= dx;
846 	dia._currentZoom.dHeight -= dy;
847 	// correction
848 	if (dia._currentZoom.dWidth < 1) dia._currentZoom.dWidth = 1;
849 	if (dia._currentZoom.dHeight < 1) dia._currentZoom.dHeight = 1;
850 
851 	// scaling parameters
852 	var
853 	sx = dia._currentZoom.dWidth / dia._currentZoom.origBBox.width,
854 	sy = dia._currentZoom.dHeight / dia._currentZoom.origBBox.height;
855 
856 	// do not redraw toolbox because it is not there
857 	dia._currentZoom._doNotRedrawToolbox = true;
858 	dia._currentZoom.scale(sx, sy);	// scale
859 	r.safari();
860 
861 	// save for later usage
862 	dia._currentZoom.dx = e.clientX;
863 	dia._currentZoom.dy = e.clientY;
864 	dia._currentZoom.lastScaleX = sx;
865 	dia._currentZoom.lastScaleY = sy;
866     }
867 };
868 
869 /**
870  * Document mouseup event.
871  * @private
872  */
873 Element.mouseUp = function(e){
874     // if ghosting is enabled, translate whole shape to the position of
875     // the ghost, then remove ghost and update joints
876     if (dia._currentDrag && dia._currentDrag._opt.ghosting){
877 	var
878 	gPos = dia._currentDrag.ghostPos(),
879 	wPos = dia._currentDrag.wrapperPos();
880 
881 	dia._currentDrag.translate(gPos.x - wPos.x, gPos.y - wPos.y);
882 	dia._currentDrag.ghost.remove();
883 	dia._currentDrag.updateJoints();
884     }
885     // add toolbar again when dragging is stopped
886     if (dia._currentDrag){
887 	dia._currentDrag.addToolbox();
888 	dia._currentDrag.toFront();
889 	// small hack: change slightely the position to get the connections to front
890 	dia._currentDrag.translate(1,1);
891     }
892 
893     // add toolbar again when zooming is stopped
894     if (dia._currentZoom){
895 	// remove toolbox, because scale above may create one,
896 	// so there would be two toolboxes after addToolbox() below
897 	dia._currentZoom.removeToolbox();
898 	dia._currentZoom.addToolbox();
899 	dia._currentZoom.toFront();
900     }
901 
902     dia._currentDrag = false;
903     dia._currentZoom = false;
904 };
905 
906 Joint.addEvent(document, "mousemove", Element.mouseMove);
907 Joint.addEvent(document, "mouseup", Element.mouseUp);
908 
909 
910 })(this);	// END CLOSURE