6 * Provides a way to visually connect elements on an HTML page, using either SVG, Canvas
9 * This file contains the YUI3 adapter.
11 * Copyright (c) 2010 - 2013 Simon Porritt (http://jsplumb.org)
14 * http://github.com/sporritt/jsplumb
15 * http://code.google.com/p/jsplumb
17 * Dual licensed under the MIT and GPL2 licenses.
21 * addClass adds a class to the given element
22 * animate calls the underlying library's animate functionality
23 * appendElement appends a child element to a parent element.
24 * bind binds some event to an element
25 * dragEvents a dictionary of event names
26 * extend extend some js object with another. probably not overly necessary; jsPlumb could just do this internally.
27 * getDragObject gets the object that is being dragged, by extracting it from the arguments passed to a drag callback
28 * getDragScope gets the drag scope for a given element.
29 * getElementObject turns an id or dom element into an element object of the underlying library's type.
30 * getOffset gets an element's offset
31 * getOriginalEvent gets the original browser event from some wrapper event.
32 * getScrollLeft gets an element's scroll left. TODO: is this actually used? will it be?
33 * getScrollTop gets an element's scroll top. TODO: is this actually used? will it be?
34 * getSize gets an element's size.
35 * getUIPosition gets the position of some element that is currently being dragged, by extracting it from the arguments passed to a drag callback.
36 * initDraggable initializes an element to be draggable
37 * initDroppable initializes an element to be droppable
38 * isDragSupported returns whether or not drag is supported for some element.
39 * isDropSupported returns whether or not drop is supported for some element.
40 * removeClass removes a class from a given element.
41 * removeElement removes some element completely from the DOM.
42 * setDraggable sets whether or not some element should be draggable.
43 * setDragScope sets the drag scope for a given element.
44 * setOffset sets the offset of some element.
48 if (!Array.prototype.indexOf) {
49 Array.prototype.indexOf = function( v, b, s ) {
50 for( var i = +b || 0, l = this.length; i < l; i++ ) {
51 if( this[i]===v || s && this[i]==v ) { return i; }
59 YUI().use('node', 'dd', 'dd-constrain', 'anim', 'node-event-simulate', function(_Y) {
61 Y.on("domready", function() { jsPlumb.init(); });
65 * adds the given value to the given list, with the given scope. creates the scoped list
67 * used by initDraggable and initDroppable.
69 var _add = function(list, scope, value) {
77 ddEvents = [ "drag:mouseDown", "drag:afterMouseDown", "drag:mouseup",
78 "drag:align", "drag:removeHandle", "drag:addHandle", "drag:removeInvalid", "drag:addInvalid",
79 "drag:start", "drag:end", "drag:drag", "drag:over", "drag:enter",
80 "drag:exit", "drag:drophit", "drag:dropmiss", "drop:over", "drop:enter", "drop:exit", "drop:hit"
82 animEvents = [ "tween" ],
84 * helper function to curry callbacks for some element.
86 _wrapper = function(fn) {
89 return fn.apply(this, arguments);
95 * extracts options from the given options object, leaving out event handlers.
97 _getDDOptions = function(options) {
99 for (var i in options) if (ddEvents.indexOf(i) == -1) o[i] = options[i];
103 * attaches all event handlers found in options to the given dragdrop object, and registering
104 * the given el as the element of interest.
106 _attachListeners = function(dd, options, eventList) {
107 for (var ev in options) {
108 if (eventList.indexOf(ev) != -1) {
109 var w = _wrapper(options[ev]);
115 _droppableOptions = {},
116 _draggablesByScope = {},
117 _draggablesById = {},
118 _droppableScopesById = {},
119 _checkHover = function(el, entering) {
121 var id = el.get("id");
123 var options = _droppableOptions[id];
125 if (options.hoverClass) {
126 if (entering) el.addClass(options.hoverClass);
127 else el.removeClass(options.hoverClass);
133 _lastDragObject = null,
134 _extend = function(o1, o2) {
139 _getAttribute = function(el, attributeId) {
140 return el.getAttribute(attributeId);
142 _getElementObject = function(el) {
143 if (el == null) return null;
145 eee = typeof el == 'string' ? Y.one('#' + el) : el._node ? el : Y.one(el);
149 jsPlumb.CurrentLibrary = {
151 addClass : function(el, clazz) {
152 jsPlumb.CurrentLibrary.getElementObject(el).addClass(clazz);
156 * animates the given element.
158 animate : function(el, properties, options) {
159 var o = _extend({node:el, to:properties}, options),
160 id = _getAttribute(el, "id");
161 o.tween = jsPlumbUtil.wrap(properties.tween, function() {
162 // TODO should use a current instance.
165 var a = new Y.Anim(o);
166 _attachListeners(a, o, animEvents);
170 appendElement : function(child, parent) {
171 _getElementObject (parent).append(child);
175 * event binding wrapper.
177 bind : function(el, event, callback) {
178 var els = jsPlumbUtil.isString(el) || typeof el.length == "undefined" ? [ _getElementObject(el) ] : Y.all(el)._nodes;
179 for (var i = 0; i < els.length; i++)
180 Y.one(els[i]).on(event, callback);
183 destroyDraggable : function(el) {
184 var id = jsPlumb.getId(el),
185 dd = _draggablesById[id];
189 delete _draggablesById[id];
193 destroyDroppable : function(el) {
198 "start":"drag:start", "stop":"drag:end", "drag":"drag:drag", "step":"step",
199 "over":"drop:enter", "out":"drop:exit", "drop":"drop:hit"
204 getClientXY : function(eventObject) {
205 return [eventObject.clientX, eventObject.clientY];
209 * takes the args passed to an event function and returns you an object representing that which is being dragged.
211 getDragObject : function(eventArgs) {
212 // this is a workaround for the unfortunate fact that in YUI3, the 'drop:exit' event does
213 // not contain a reference to the drag that just exited. single-threaded js to the
214 // rescue: we'll just keep it for ourselves.
215 if (eventArgs[0].drag) _lastDragObject = eventArgs[0].drag.el;
216 return _lastDragObject;
219 getDragScope : function(el) {
220 var id = jsPlumb.getId(el),
221 dd = _draggablesById[id];
225 getDropEvent : function(args) {
229 getDropScope : function(el) {
230 var id = jsPlumb.getId(el);
231 return _droppableScopesById[id];
234 getDOMElement : function(el) {
235 if (el == null) return null;
236 if (typeof(el) == "string")
237 return document.getElementById(el);
243 getElementObject : _getElementObject,
245 getOffset : function(el) {
246 var o = Y.DOM.getXY(el._node);
247 return {left:o[0], top:o[1]};
250 getOriginalEvent : function(e) {
254 getPageXY : function(eventObject) {
255 return [eventObject.pageX, eventObject.pageY];
258 getParent : function(el) {
259 return jsPlumb.CurrentLibrary.getElementObject(el).get("parentNode");
262 getScrollLeft : function(el) {
266 getScrollTop : function(el) {
270 getSelector : function(context, spec) {
271 var _convert = function(s) { return s && s ._nodes ? s._nodes : []; };
273 if (arguments.length == 2) {
274 return _convert(jsPlumb.CurrentLibrary.getElementObject(context).all(spec));
277 return _convert(Y.all(context));
281 getSize : function(el) {
282 return [ el._node.offsetWidth, el._node.offsetHeight ];
285 getTagName : function(el) {
286 var e = jsPlumb.CurrentLibrary.getElementObject(el);
287 return e != null && e._node != null ? e._node.tagName : null;
290 getUIPosition : function(args, zoom) {
292 var el = args[0].currentTarget.el._node || args[0].currentTarget.el;
293 var o = Y.DOM.getXY(el);
294 return {left:o[0] / zoom, top:o[1] / zoom };
297 hasClass : function(el, clazz) {
298 return el.hasClass(clazz);
301 initDraggable : function(el, options, isPlumbedComponent, _jsPlumb) {
302 var _opts = _getDDOptions(options),
303 id = _jsPlumb.getId(el);
304 _opts.node = "#" + id;
305 options["drag:start"] = jsPlumbUtil.wrap(options["drag:start"], function() {
306 Y.one(document.body).addClass(_jsPlumb.dragSelectClass);
308 options["drag:end"] = jsPlumbUtil.wrap(options["drag:end"], function() {
309 Y.one(document.body).removeClass(_jsPlumb.dragSelectClass);
311 var dd = new Y.DD.Drag(_opts),
312 containment = options.constrain2node || options.containment;
317 dd.plug(Y.Plugin.DDConstrained, {
318 constrain2node: containment
322 if (isPlumbedComponent) {
323 var scope = options.scope || _jsPlumb.Defaults.Scope;
325 _add(_draggablesByScope, scope, dd);
328 _draggablesById[id] = dd;
329 _attachListeners(dd, options, ddEvents);
332 initDroppable : function(el, options) {
333 var _opts = _getDDOptions(options),
334 id = jsPlumb.getId(el);
335 _opts.node = "#" + id;
336 var dd = new Y.DD.Drop(_opts);
338 _droppableOptions[id] = options;
340 options = _extend({}, options);
341 var scope = options.scope || jsPlumb.Defaults.Scope;
342 _droppableScopesById[id] = scope;
344 options["drop:enter"] = jsPlumbUtil.wrap(options["drop:enter"], function(e) {
345 if (e.drag.scope !== scope) return true;
346 _checkHover(el, true);
348 options["drop:exit"] = jsPlumbUtil.wrap(options["drop:exit"], function(e) {
349 _checkHover(el, false);
351 options["drop:hit"] = jsPlumbUtil.wrap(options["drop:hit"], function(e) {
352 if (e.drag.scope !== scope) return true;
353 _checkHover(el, false);
356 _attachListeners(dd, options, ddEvents);
359 isAlreadyDraggable : function(el) {
360 el = _getElementObject(el);
361 return el.hasClass("yui3-dd-draggable");
364 isDragSupported : function(el) { return true; },
365 isDropSupported : function(el) { return true; },
366 removeClass : function(el, clazz) {
367 jsPlumb.CurrentLibrary.getElementObject(el).removeClass(clazz);
369 removeElement : function(el) { _getElementObject(el).remove(); },
371 setDragFilter : function(el, filter) {
372 jsPlumb.log("NOT IMPLEMENTED: setDragFilter");
376 * sets the draggable state for the given element
378 setDraggable : function(el, draggable) {
379 var id = jsPlumb.getId(el),
380 dd = _draggablesById[id];
381 if (dd) dd.set("lock", !draggable);
384 setDragScope : function(el, scope) {
385 var id = jsPlumb.getId(el),
386 dd = _draggablesById[id];
387 if (dd) dd.scope = scope;
390 setOffset : function(el, o) {
391 el = _getElementObject(el);
392 el.set("top", o.top);
393 el.set("left", o.left);
396 stopDrag : function() {
400 trigger : function(el, event, originalEvent) {
401 originalEvent.stopPropagation();
402 _getElementObject(el).simulate(event, {
403 pageX:originalEvent.pageX,
404 pageY:originalEvent.pageY,
405 clientX:originalEvent.clientX,
406 clientY:originalEvent.clientY
411 * event unbinding wrapper.
413 unbind : function(el, event, callback) {
414 _getElementObject(el).detach(event, callback);