jsPlumb-0.0.4-RC3.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. /*
  2. * jsPlumb 0.0.4-RC2
  3. *
  4. * gradients are new in this version. and many things have been renamed to more pleasing looking words.
  5. *
  6. */
  7. if (!Array.prototype.indexOf) {
  8. Array.prototype.indexOf = function( v, b, s ) {
  9. for( var i = +b || 0, l = this.length; i < l; i++ ) {
  10. if( this[i]===v || s && this[i]==v ) { return i; }
  11. }
  12. return -1;
  13. };
  14. }
  15. (function() {
  16. var connections = {};
  17. var offsets = [];
  18. var sizes = [];
  19. /**
  20. * generic anchor - can be situated anywhere. params should contain three values, and may optionally have an 'offsets' argument:
  21. *
  22. * x - the x location of the anchor as a percentage of the total width.
  23. * y - the y location of the anchor as a percentage of the total height.
  24. * orientation - an [x,y] array indicating the general direction a connection from the anchor should go in.
  25. * offsets - an [x,y] array of fixed offsets that should be applied after the x,y position has been figured out. may be null.
  26. *
  27. */
  28. var Anchor = function(params) {
  29. var self = this;
  30. this.x = params.x || 0; this.y = params.y || 0; this.orientation = params.orientation || [0,0]; this.offsets = params.offsets || [0,0];
  31. this.compute = function(xy, wh, txy, twh) {
  32. return [ xy[0] + (self.x * wh[0]) + self.offsets[0], xy[1] + (self.y * wh[1]) + self.offsets[1] ];
  33. }
  34. };
  35. var jsPlumb = window.jsPlumb = {
  36. connectorClass : '_jsPlumb_connector',
  37. endpointClass : '_jsPlumb_endpoint',
  38. DEFAULT_PAINT_STYLE : { lineWidth : 10, strokeStyle : "red" },
  39. DEFAULT_ENDPOINT_STYLE : { fillStyle : null }, // meaning it will be derived from the stroke style of the connector.
  40. DEFAULT_ENDPOINT_STYLES : [ null, null ], // meaning it will be derived from the stroke style of the connector.
  41. DEFAULT_DRAG_OPTIONS : { },
  42. DEFAULT_CONNECTOR : null,
  43. DEFAULT_ENDPOINT : null,
  44. DEFAULT_ENDPOINTS : [null, null], // new in 0.0.4, the ability to specify diff. endpoints. DEFAULT_ENDPOINT is here for backwards compatibility.
  45. DEFAULT_NEW_CANVAS_SIZE : 1200, // only used for IE; a canvas needs a size before the init call to excanvas (for some reason. no idea why.)
  46. /**
  47. * Creates an anchor with the given params.
  48. * x - the x location of the anchor as a percentage of the total width.
  49. * y - the y location of the anchor as a percentage of the total height.
  50. * orientation - an [x,y] array indicating the general direction a connection from the anchor should go in.
  51. * offsets - an [x,y] array of fixed offsets that should be applied after the x,y position has been figured out. optional. defaults to [0,0].
  52. */
  53. makeAnchor : function(params) {
  54. return new Anchor(params);
  55. },
  56. /**
  57. * Places you can anchor a connection to. These are helpers for common locations; they all just return an instance
  58. * of Anchor that has been configured appropriately.
  59. *
  60. * You can write your own one of these; you
  61. * just need to provide a 'compute' method and an 'orientation'. so you'd say something like this:
  62. *
  63. * jsPlumb.Anchors.MY_ANCHOR = {
  64. * compute : function(xy, wh, txy, twh) { return some mathematics on those variables; },
  65. * orientation : [ox, oy]
  66. * };
  67. *
  68. * compute takes the [x,y] position of the top left corner of the anchored element,
  69. * and the element's [width,height] (all in pixels), as well as the location and dimension of the element it's plumbed to,
  70. * and returns where the anchor should be located.
  71. *
  72. * the 'orientation' array (returned here as [ox,oy]) indicates the general direction a connection from the anchor
  73. * should go in, if possible. it is an [x,y] matrix where a value of 0 means no preference,
  74. * -1 means go in a negative direction for the given axis, and 1 means go in a positive
  75. * direction. so consider a TOP_CENTER anchor: the orientation matrix for it is [0,-1],
  76. * meaning connections naturally want to go upwards on screen. in a Bezier implementation, for example,
  77. * the curve would start out going in that direction, before bending towards the target anchor.
  78. */
  79. Anchors :
  80. {
  81. TOP_CENTER : new Anchor({x:0.5, y:0, orientation:[0,-1] }),
  82. BOTTOM_CENTER : new Anchor({x:0.5, y:1, orientation:[0, 1] }),
  83. LEFT_MIDDLE: new Anchor({x:0, y:0.5, orientation:[-1,0] }),
  84. RIGHT_MIDDLE : new Anchor({x:1, y:0.5, orientation:[1,0] }),
  85. CENTER : new Anchor({x:0.5, y:0.5, orientation:[0,0] }),
  86. TOP_RIGHT : new Anchor({x:1, y:0, orientation:[0,-1] }),
  87. BOTTOM_RIGHT : new Anchor({x:1, y:1, orientation:[0,1] }),
  88. TOP_LEFT : new Anchor({x:0, y:0, orientation:[0,-1] }),
  89. BOTTOM_LEFT : new Anchor({x:0, y:1, orientation:[0,1] })
  90. },
  91. /**
  92. * Types of connectors, eg. Straight, Bezier.
  93. */
  94. Connectors :
  95. {
  96. /**
  97. * The Straight connector draws a simple straight line between the two anchor points.
  98. */
  99. Straight : function() {
  100. var self = this;
  101. /**
  102. * Computes the new size and position of the canvas.
  103. * @param sourceAnchor Absolute position on screen of the source object's anchor.
  104. * @param targetAnchor Absolute position on screen of the target object's anchor.
  105. * @param positionMatrix Indicates the relative positions of the left,top of the
  106. * two plumbed objects. so [0,0] indicates that the source is to the left of, and
  107. * above, the target. [1,0] means the source is to the right and above. [0,1] means
  108. * the source is to the left and below. [1,1] means the source is to the right
  109. * and below. this is used to figure out which direction to draw the connector in.
  110. * @returns an array of positioning information. the first two values are
  111. * the [left, top] absolute position the canvas should be placed on screen. the
  112. * next two values are the [width,height] the canvas should be. after that each
  113. * Connector can put whatever it likes into the array:it will be passed back in
  114. * to the paint call. This particular function stores the origin and destination of
  115. * the line it is going to draw. a more involved implementation, like a Bezier curve,
  116. * would store the control point info in this array too.
  117. */
  118. this.compute = function(sourcePos, targetPos, sourceAnchor, targetAnchor, lineWidth) {
  119. $("#log").html("");
  120. var s = "sourcePos, targetPos : " + sourcePos + " : " + targetPos + "<br/>";
  121. var w = Math.abs(sourcePos[0] - targetPos[0]);
  122. var h = Math.abs(sourcePos[1] - targetPos[1]);
  123. var widthAdjusted = false, heightAdjusted = false;
  124. s += "original w = " + w + "; h = " + h + "<br/>";
  125. if (w < lineWidth) { w = lineWidth; widthAdjusted = true; }
  126. if (h < lineWidth) { h = lineWidth; heightAdjusted = true; }
  127. s += " w = " + w + "; h = " + h + "<br/>";
  128. // these are padding to ensure the whole connector line appears
  129. var xo = 0.45 * w, yo = 0.45 * h;
  130. s += "xoffset = " + xo + "; yoffset = " + yo + "<br/>";
  131. // these are padding to ensure the whole connector line appears
  132. w *= 1.9; h *=1.9;
  133. s += "adjusted w, h " + w + "," + h + "<br/>";
  134. var x = Math.min(sourcePos[0], targetPos[0]) - xo;
  135. var y = Math.min(sourcePos[1], targetPos[1]) - yo;
  136. // here we check to see if the delta was very small and so the line in
  137. // one direction can be considered straight.
  138. var sx = widthAdjusted ? (w - lineWidth) / 2 : sourcePos[0] < targetPos[0] ? w-xo : xo;
  139. var sy = heightAdjusted ? (h - lineWidth) / 2 : sourcePos[1] < targetPos[1] ? h-yo : yo;
  140. var tx = widthAdjusted ? (w - lineWidth) / 2 : sourcePos[0] < targetPos[0] ? xo : w-xo;
  141. var ty = heightAdjusted ? (h - lineWidth) / 2 : sourcePos[1] < targetPos[1] ? yo : h-yo;
  142. var retVal = [ x, y, w, h, sx, sy, tx, ty ];
  143. s += " lineWidth : " + lineWidth + "<br/>";
  144. s += " retVal : " + retVal;
  145. $("#log").html(s);
  146. // return [canvasX, canvasY, canvasWidth, canvasHeight,
  147. // sourceX, sourceY, targetX, targetY]
  148. return retVal;
  149. };
  150. this.paint = function(dimensions, ctx)
  151. {
  152. ctx.beginPath();
  153. ctx.moveTo(dimensions[4], dimensions[5]);
  154. ctx.lineTo(dimensions[6], dimensions[7]);
  155. ctx.stroke();
  156. };
  157. },
  158. /**
  159. * The Bezier connector draws a Bezier curve with two control points.
  160. */
  161. Bezier : function(curviness) {
  162. var self = this;
  163. this.majorAnchor = curviness || 150;
  164. this.minorAnchor = 10;
  165. this._findControlPoint = function(point, anchor1Position, anchor2Position, anchor1, anchor2) {
  166. var p = [];
  167. var ma = self.majorAnchor, mi = self.minorAnchor;
  168. if (anchor1.orientation[0] == 0) // X
  169. p.push(anchor1Position[0] < anchor2Position[0] ? point[0] + mi : point[0] - mi);
  170. else p.push(point[0] - (ma * anchor1.orientation[0]));
  171. if (anchor1.orientation[1] == 0) // Y
  172. p.push(anchor1Position[1] < anchor2Position[1] ? point[1] + mi : point[1] - mi);
  173. else p.push(point[1] + (ma * anchor2.orientation[1]));
  174. return p;
  175. };
  176. this.compute = function(sourcePos, targetPos, sourceAnchor, targetAnchor, lineWidth)
  177. {
  178. var w = Math.abs(sourcePos[0] - targetPos[0]), h = Math.abs(sourcePos[1] - targetPos[1]);
  179. var canvasX = Math.min(sourcePos[0], targetPos[0]), canvasY = Math.min(sourcePos[1], targetPos[1]);
  180. var sx = sourcePos[0] < targetPos[0] ? w : 0, sy = sourcePos[1] < targetPos[1] ? h : 0;
  181. var tx = sourcePos[0] < targetPos[0] ? 0 : w, ty = sourcePos[1] < targetPos[1] ? 0 : h;
  182. var CP = self._findControlPoint([sx,sy], sourcePos, targetPos, sourceAnchor, targetAnchor);
  183. var CP2 = self._findControlPoint([tx,ty], targetPos, sourcePos, targetAnchor, sourceAnchor);
  184. var minx1 = Math.min(sx,tx); var minx2 = Math.min(CP[0], CP2[0]); var minx = Math.min(minx1,minx2);
  185. var maxx1 = Math.max(sx,tx); var maxx2 = Math.max(CP[0], CP2[0]); var maxx = Math.max(maxx1,maxx2);
  186. if (maxx > w) w = maxx;
  187. if (minx < 0) {
  188. canvasX += minx; var ox = Math.abs(minx);
  189. w += ox; CP[0] += ox; sx += ox; tx +=ox; CP2[0] += ox;
  190. }
  191. var miny1 = Math.min(sy,ty); var miny2 = Math.min(CP[1], CP2[1]); var miny = Math.min(miny1,miny2);
  192. var maxy1 = Math.max(sy,ty); var maxy2 = Math.max(CP[1], CP2[1]); var maxy = Math.max(maxy1,maxy2);
  193. if (maxy > h) h = maxy;
  194. if (miny < 0) {
  195. canvasY += miny; var oy = Math.abs(miny);
  196. h += oy; CP[1] += oy; sy += oy; ty +=oy; CP2[1] += oy;
  197. }
  198. // return [ canvasx, canvasy, canvasWidth, canvasHeight,
  199. // sourceX, sourceY, targetX, targetY,
  200. // controlPoint1_X, controlPoint1_Y, controlPoint2_X, controlPoint2_Y
  201. return [canvasX, canvasY, w, h, sx, sy, tx, ty, CP[0], CP[1], CP2[0], CP2[1] ];
  202. };
  203. this.paint = function(d, ctx) {
  204. ctx.beginPath();
  205. ctx.moveTo(d[4],d[5]);
  206. ctx.bezierCurveTo(d[8],d[9],d[10],d[11],d[6],d[7]);
  207. ctx.stroke();
  208. }
  209. }
  210. },
  211. /**
  212. * Types of endpoint UIs. we supply two - a circle of default radius 10px, and a rectangle of
  213. * default size 20x20. you can supply others of these if you want to - see the documentation
  214. * for a howto.
  215. */
  216. Endpoints : {
  217. Dot : function(params) {
  218. params = params || { radius:10 };
  219. var self = this;
  220. this.radius = params.radius;
  221. this.paint = function(anchorPoint, canvas, endpointStyle, connectorPaintStyle) {
  222. var radius = endpointStyle.radius || self.radius;
  223. var x = anchorPoint[0] - radius;
  224. var y = anchorPoint[1] - radius;
  225. jsPlumb.sizeCanvas(canvas, x, y, radius * 2, radius * 2);
  226. var ctx = canvas.getContext('2d');
  227. var style = {};
  228. jsPlumb.applyPaintStyle(style, endpointStyle);
  229. if (style.fillStyle == null) style.fillStyle = connectorPaintStyle.strokeStyle;
  230. jsPlumb.applyPaintStyle(ctx, style);
  231. ctx.beginPath();
  232. ctx.arc(radius, radius, radius, 0, Math.PI*2, true);
  233. ctx.closePath();
  234. ctx.fill();
  235. };
  236. },
  237. Rectangle : function(params) {
  238. params = params || { width:20, height:20 };
  239. var self = this;
  240. this.width = params.width;
  241. this.height = params.height;
  242. this.paint = function(anchorPoint, canvas, endpointStyle, connectorPaintStyle) {
  243. var width = endpointStyle.width || self.width;
  244. var height = endpointStyle.height || self.height;
  245. var x = anchorPoint[0] - (width/2);
  246. var y = anchorPoint[1] - (height/2);
  247. jsPlumb.sizeCanvas(canvas, x, y, width, height);
  248. var ctx = canvas.getContext('2d');
  249. //todo: the fillStyle needs some thought. we want to support a few options:
  250. // 1. nothing supplied; use the stroke color or the default if no stroke color.
  251. // 2. a fill color supplied - use it
  252. // 3. a gradient supplied - use it
  253. // 4. setting the endpoint to the same color as the bg of the element it is attached to.
  254. var style = {};
  255. jsPlumb.applyPaintStyle(style, endpointStyle);
  256. if (style.fillStyle == null) style.fillStyle = connectorPaintStyle.strokeStyle;
  257. jsPlumb.applyPaintStyle(ctx, style);
  258. ctx.beginPath();
  259. ctx.rect(0, 0, width, height);
  260. ctx.closePath();
  261. ctx.fill();
  262. };
  263. }
  264. },
  265. connect : function(params) {
  266. var jpc = new jsPlumbConnection(params);
  267. var key = jpc.sourceId + "_" + jpc.targetId;
  268. connections[key] = jpc;
  269. var addToList = function(elId, jpc) {
  270. var l = connections[elId];
  271. if (l == null) {
  272. l = [];
  273. connections[elId] = l;
  274. }
  275. l.push(jpc);
  276. };
  277. // register this connection.
  278. addToList(jpc.sourceId, jpc);
  279. addToList(jpc.targetId, jpc);
  280. },
  281. getConnections : function(elId) {
  282. return connections[elId];
  283. },
  284. drag : function(element, ui) {
  285. var id = element.attr("id");
  286. var l = jsPlumb.getConnections(id);
  287. for (var i = 0; i < l.length; i++)
  288. l[i].paint(id, ui);
  289. },
  290. detach : function(sourceId, targetId) {
  291. var jpcs = connections[sourceId];
  292. var idx = -1;
  293. for (var i = 0; i < jpcs.length; i++) {
  294. if ((jpcs[i].sourceId == sourceId && jpcs[i].targetId == targetId) || (jpcs[i].targetId == sourceId && jpcs[i].sourceId == targetId)) {
  295. jsPlumb.removeCanvas(jpcs[i].canvas);
  296. if (jpcs[i].drawEndpoints) {
  297. jsPlumb.removeCanvas(jpcs[i].targetEndpointCanvas);
  298. jsPlumb.removeCanvas(jpcs[i].sourceEndpointCanvas);
  299. }
  300. idx = i;
  301. break;
  302. }
  303. }
  304. if (idx != -1)
  305. jpcs.splice(idx, 1);
  306. // todo - dragging? if no more connections for an object turn off dragging by default, but
  307. // allow an override on it?
  308. },
  309. detachAll : function(elId) {
  310. var jpcs = connections[elId];
  311. for (var i = 0; i < jpcs.length; i++) {
  312. jsPlumb.removeCanvas(jpcs[i].canvas);
  313. if (jpcs[i].drawEndpoints) {
  314. jsPlumb.removeCanvas(jpcs[i].targetEndpointCanvas);
  315. jsPlumb.removeCanvas(jpcs[i].sourceEndpointCanvas);
  316. }
  317. }
  318. connections[elId] = [];
  319. },
  320. hide : function(elId) {
  321. jsPlumb._setVisible(elId, "none");
  322. },
  323. show : function(elId) {
  324. jsPlumb._setVisible(elId, "block");
  325. },
  326. toggle : function(elId) {
  327. var jpcs = connections[elId];
  328. if (jpcs.length > 0)
  329. jsPlumb._setVisible(elId, "none" == jpcs[0].canvas.style.display ? "block" : "none");
  330. },
  331. _setVisible : function(elId, state) {
  332. var jpcs = connections[elId];
  333. for (var i = 0; i < jpcs.length; i++) {
  334. jpcs[i].canvas.style.display=state;
  335. if (jpcs[i].drawEndpoints) {
  336. jpcs[i].sourceEndpointCanvas.style.display=state;
  337. jpcs[i].targetEndpointCanvas.style.display=state;
  338. }
  339. }
  340. },
  341. /**
  342. * helper to create a canvas.
  343. * @param clazz optional class name for the canvas.
  344. */
  345. newCanvas : function(clazz) {
  346. var canvas = document.createElement("canvas");
  347. document.body.appendChild(canvas);
  348. canvas.style.position="absolute";
  349. if (clazz) { canvas.className=clazz; }
  350. if (/MSIE/.test(navigator.userAgent) && !window.opera) {
  351. // for IE we have to set a big canvas size. actually you can override this, too, if 1200 pixels
  352. // is not big enough for the biggest connector/endpoint canvas you have at startup.
  353. jsPlumb.sizeCanvas(canvas, 0, 0, jsPlumb.DEFAULT_NEW_CANVAS_SIZE, jsPlumb.DEFAULT_NEW_CANVAS_SIZE);
  354. canvas = G_vmlCanvasManager.initElement(canvas);
  355. }
  356. return canvas;
  357. },
  358. /**
  359. * helper to remove a canvas from the DOM.
  360. */
  361. removeCanvas : function(canvas) {
  362. if (canvas != null) document.body.removeChild(canvas);
  363. },
  364. /**
  365. * helper to size a canvas.
  366. */
  367. sizeCanvas : function(canvas, x, y, w, h) {
  368. canvas.style.height = h + "px"; canvas.height = h;
  369. canvas.style.width = w + "px"; canvas.width = w;
  370. canvas.style.left = x + "px"; canvas.style.top = y + "px";
  371. },
  372. /**
  373. * applies all the styles to the given context.
  374. * @param canvas
  375. * @param styles
  376. */
  377. applyPaintStyle : function(context, styles) {
  378. for (var i in styles) {
  379. context[i] = styles[i];
  380. }
  381. }
  382. };
  383. // ************** connection
  384. // ****************************************
  385. /**
  386. * allowed params:
  387. * source: source element (string or a jQuery element) (required)
  388. * target: target element (string or a jQuery element) (required)
  389. * anchors: optional array of anchor placements. defaults to BOTTOM_CENTER for source
  390. * and TOP_CENTER for target.
  391. */
  392. var jsPlumbConnection = window.jsPlumbConnection = function(params) {
  393. // ************** get the source and target and register the connection. *******************
  394. var self = this;
  395. // get source and target as jQuery objects
  396. this.source = (typeof params.source == 'string') ? $("#" + params.source) : params.source;
  397. this.target = (typeof params.target == 'string') ? $("#" + params.target) : params.target;
  398. this.sourceId = $(this.source).attr("id");
  399. this.targetId = $(this.target).attr("id");
  400. this.drawEndpoints = params.drawEndpoints != null ? params.drawEndpoints : true;
  401. this.endpointsOnTop = params.endpointsOnTop != null ? params.endpointsOnTop : true;
  402. // get anchor
  403. this.anchors = params.anchors || jsPlumb.DEFAULT_ANCHORS || [jsPlumb.Anchors.BOTTOM_CENTER, jsPlumb.Anchors.TOP_CENTER];
  404. // make connector
  405. this.connector = params.connector || jsPlumb.DEFAULT_CONNECTOR || new jsPlumb.Connectors.Bezier();
  406. this.paintStyle = params.paintStyle || jsPlumb.DEFAULT_PAINT_STYLE;
  407. // init endpoints
  408. this.endpoints = [];
  409. if(!params.endpoints) params.endpoints = [null,null];
  410. this.endpoints[0] = params.endpoints[0] || params.endpoint || jsPlumb.DEFAULT_ENDPOINTS[0] || jsPlumb.DEFAULT_ENDPOINT || new jsPlumb.Endpoints.Dot();
  411. this.endpoints[1] = params.endpoints[1] || params.endpoint || jsPlumb.DEFAULT_ENDPOINTS[1] ||jsPlumb.DEFAULT_ENDPOINT || new jsPlumb.Endpoints.Dot();
  412. this.endpointStyles = [];
  413. if (!params.endpointStyles) params.endpointStyles = [null,null];
  414. this.endpointStyles[0] = params.endpointStyles[0] || params.endpointStyle || jsPlumb.DEFAULT_ENDPOINT_STYLES[0] || jsPlumb.DEFAULT_ENDPOINT_STYLE;
  415. this.endpointStyles[1] = params.endpointStyles[1] || params.endpointStyle || jsPlumb.DEFAULT_ENDPOINT_STYLES[1] || jsPlumb.DEFAULT_ENDPOINT_STYLE;
  416. offsets[this.sourceId] = this.source.offset();
  417. sizes[this.sourceId] = [this.source.outerWidth(), this.source.outerHeight()];
  418. offsets[this.targetId] = this.target.offset();
  419. sizes[this.targetId] = [this.target.outerWidth(), this.target.outerHeight()];
  420. // *************** create canvases on which the connection will be drawn ************
  421. var canvas = jsPlumb.newCanvas(jsPlumb.connectorClass);
  422. this.canvas = canvas;
  423. // create endpoint canvases
  424. if (this.drawEndpoints) {
  425. this.sourceEndpointCanvas = jsPlumb.newCanvas(jsPlumb.endpointClass);
  426. this.targetEndpointCanvas = jsPlumb.newCanvas(jsPlumb.endpointClass);
  427. // sit them on top of the underlying element?
  428. if (this.endpointsOnTop) {
  429. $(this.sourceEndpointCanvas).css("zIndex", this.source.css("zIndex") + 1);
  430. $(this.targetEndpointCanvas).css("zIndex", this.target.css("zIndex") + 1);
  431. }
  432. }
  433. // ************** store the anchors
  434. this.paint = function(elId, ui) {
  435. // if the moving object is not the source we must transpose the two references.
  436. var swap = !(elId == this.sourceId);
  437. var tId = swap ? this.sourceId : this.targetId, sId = swap ? this.targetId : this.sourceId;
  438. var tIdx = swap ? 0 : 1, sIdx = swap ? 1 : 0;
  439. if (this.canvas.getContext) {
  440. var myOffset = ui.absolutePosition;
  441. offsets[elId] = myOffset;
  442. var myWH = sizes[elId];
  443. var ctx = canvas.getContext('2d');
  444. var otherOffset = offsets[tId];
  445. var otherWH = sizes[tId];
  446. var sAnchorP = this.anchors[sIdx].compute([myOffset.left, myOffset.top], myWH, [otherOffset.left, otherOffset.top], otherWH);
  447. var tAnchorP = this.anchors[tIdx].compute([otherOffset.left, otherOffset.top], otherWH, [myOffset.left, myOffset.top], myWH);
  448. var dim = this.connector.compute(sAnchorP, tAnchorP, this.anchors[sIdx], this.anchors[tIdx], this.paintStyle.lineWidth);
  449. jsPlumb.sizeCanvas(canvas, dim[0], dim[1], dim[2], dim[3]);
  450. jsPlumb.applyPaintStyle(ctx, this.paintStyle);
  451. // gradient experiment.
  452. var ie = (/MSIE/.test(navigator.userAgent) && !window.opera);
  453. if (!ie && this.paintStyle.gradient) {
  454. var g = swap ? ctx.createLinearGradient(dim[4], dim[5], dim[6], dim[7]) : ctx.createLinearGradient(dim[6], dim[7], dim[4], dim[5]);
  455. for (var i = 0; i < this.paintStyle.gradient.stops.length; i++)
  456. g.addColorStop(this.paintStyle.gradient.stops[i][0],this.paintStyle.gradient.stops[i][1]);
  457. ctx.strokeStyle = g;
  458. }
  459. this.connector.paint(dim, ctx);
  460. if (this.drawEndpoints) {
  461. // todo support endpoints here.
  462. var style = this.endpointStyle || this.paintStyle;
  463. var sourceCanvas = swap ? this.targetEndpointCanvas : this.sourceEndpointCanvas;
  464. var targetCanvas = swap ? this.sourceEndpointCanvas : this.targetEndpointCanvas;
  465. this.endpoints[swap ? 1 : 0].paint(sAnchorP, sourceCanvas, this.endpointStyles[swap ? 1 : 0] || this.paintStyle, this.paintStyle);
  466. this.endpoints[swap ? 0 : 1].paint(tAnchorP, targetCanvas, this.endpointStyles[swap ? 0 : 1] || this.paintStyle, this.paintStyle);
  467. }
  468. }
  469. };
  470. var draggable = params.draggable == null ? true : params.draggable;
  471. if (draggable) {
  472. var dragOptions = params.dragOptions || jsPlumb.DEFAULT_DRAG_OPTIONS;
  473. var dragCascade = dragOptions.drag || function(e,u) {};
  474. var initDrag = function(element, dragFunc) {
  475. var opts = {};
  476. for (var i in dragOptions) {
  477. opts[i] = dragOptions[i];
  478. }
  479. opts.drag = dragFunc;
  480. element.draggable(opts);
  481. };
  482. initDrag(this.source, function(event, ui) {
  483. jsPlumb.drag(self.source, ui);
  484. dragCascade(event, ui);
  485. });
  486. initDrag(this.target, function(event, ui) {
  487. jsPlumb.drag(self.target, ui);
  488. dragCascade(event, ui);
  489. });
  490. }
  491. var o = this.source.offset();
  492. this.paint(this.sourceId, {'absolutePosition': this.source.offset()});
  493. };
  494. })();
  495. // jQuery plugin code
  496. (function($){
  497. $.fn.plumb = function(options) {
  498. var defaults = { };
  499. var options = $.extend(defaults, options);
  500. return this.each(function()
  501. {
  502. var obj = $(this);
  503. var params = {};
  504. params.source = obj;
  505. for (var i in options) {
  506. params[i] = options[i];
  507. }
  508. jsPlumb.connect(params);
  509. });
  510. };
  511. $.fn.detach = function(options) {
  512. return this.each(function()
  513. {
  514. var id = $(this).attr("id");
  515. if (typeof options == 'string') options = [options];
  516. for (var i = 0; i < options.length; i++)
  517. jsPlumb.detach(id, options[i]);
  518. });
  519. };
  520. $.fn.detachAll = function(options) {
  521. return this.each(function()
  522. {
  523. var id = $(this).attr("id");
  524. jsPlumb.detachAll(id);
  525. });
  526. };
  527. })(jQuery);