reservation plugin - unbound request (unclean
[unfold.git] / portal / static / unbound_reservation_static / src / renderers-svg.js
diff --git a/portal/static/unbound_reservation_static/src/renderers-svg.js b/portal/static/unbound_reservation_static/src/renderers-svg.js
new file mode 100644 (file)
index 0000000..6d7f601
--- /dev/null
@@ -0,0 +1,628 @@
+/*
+ * jsPlumb
+ * 
+ * Title:jsPlumb 1.5.5
+ * 
+ * Provides a way to visually connect elements on an HTML page, using either SVG, Canvas
+ * elements, or VML.  
+ * 
+ * This file contains the SVG renderers.
+ *
+ * Copyright (c) 2010 - 2013 Simon Porritt (http://jsplumb.org)
+ * 
+ * http://jsplumb.org
+ * http://github.com/sporritt/jsplumb
+ * http://code.google.com/p/jsplumb
+ * 
+ * Dual licensed under the MIT and GPL2 licenses.
+ */
+
+/**
+ * SVG support for jsPlumb.
+ * 
+ * things to investigate:
+ * 
+ * gradients:  https://developer.mozilla.org/en/svg_in_html_introduction
+ * css:http://tutorials.jenkov.com/svg/svg-and-css.html
+ * text on a path: http://www.w3.org/TR/SVG/text.html#TextOnAPath
+ * pointer events: https://developer.mozilla.org/en/css/pointer-events
+ *
+ * IE9 hover jquery: http://forum.jquery.com/topic/1-6-2-broke-svg-hover-events
+ *
+ */
+;(function() {
+       
+// ************************** SVG utility methods ******************************************** 
+       
+       var svgAttributeMap = {
+               "joinstyle":"stroke-linejoin",
+               "stroke-linejoin":"stroke-linejoin",            
+               "stroke-dashoffset":"stroke-dashoffset",
+               "stroke-linecap":"stroke-linecap"
+       },
+       STROKE_DASHARRAY = "stroke-dasharray",
+       DASHSTYLE = "dashstyle",
+       LINEAR_GRADIENT = "linearGradient",
+       RADIAL_GRADIENT = "radialGradient",
+       FILL = "fill",
+       STOP = "stop",
+       STROKE = "stroke",
+       STROKE_WIDTH = "stroke-width",
+       STYLE = "style",
+       NONE = "none",
+       JSPLUMB_GRADIENT = "jsplumb_gradient_",
+       LINE_WIDTH = "lineWidth",
+       ns = {
+               svg:"http://www.w3.org/2000/svg",
+               xhtml:"http://www.w3.org/1999/xhtml"
+       },
+       _attr = function(node, attributes) {
+               for (var i in attributes)
+                       node.setAttribute(i, "" + attributes[i]);
+       },      
+       _node = function(name, attributes) {
+               var n = document.createElementNS(ns.svg, name);
+               attributes = attributes || {};
+               attributes.version = "1.1";
+               attributes.xmlns = ns.xhtml;
+               _attr(n, attributes);
+               return n;
+       },
+       _pos = function(d) { return "position:absolute;left:" + d[0] + "px;top:" + d[1] + "px"; },      
+       _clearGradient = function(parent) {
+               for (var i = 0; i < parent.childNodes.length; i++) {
+                       if (parent.childNodes[i].tagName == LINEAR_GRADIENT || parent.childNodes[i].tagName == RADIAL_GRADIENT)
+                               parent.removeChild(parent.childNodes[i]);
+               }
+       },              
+       _updateGradient = function(parent, node, style, dimensions, uiComponent) {
+               var id = JSPLUMB_GRADIENT + uiComponent._jsPlumb.instance.idstamp();
+               // first clear out any existing gradient
+               _clearGradient(parent);
+               // this checks for an 'offset' property in the gradient, and in the absence of it, assumes
+               // we want a linear gradient. if it's there, we create a radial gradient.
+               // it is possible that a more explicit means of defining the gradient type would be
+               // better. relying on 'offset' means that we can never have a radial gradient that uses
+               // some default offset, for instance.
+               // issue 244 suggested the 'gradientUnits' attribute; without this, straight/flowchart connectors with gradients would
+               // not show gradients when the line was perfectly horizontal or vertical.
+               var g;
+               if (!style.gradient.offset) {
+                       g = _node(LINEAR_GRADIENT, {id:id, gradientUnits:"userSpaceOnUse"});
+               }
+               else {
+                       g = _node(RADIAL_GRADIENT, {
+                               id:id
+                       });                     
+               }
+               
+               parent.appendChild(g);
+               
+               // the svg radial gradient seems to treat stops in the reverse 
+               // order to how canvas does it.  so we want to keep all the maths the same, but
+               // iterate the actual style declarations in reverse order, if the x indexes are not in order.
+               for (var i = 0; i < style.gradient.stops.length; i++) {
+                       var styleToUse = uiComponent.segment == 1 ||  uiComponent.segment == 2 ? i: style.gradient.stops.length - 1 - i,                        
+                               stopColor = jsPlumbUtil.convertStyle(style.gradient.stops[styleToUse][1], true),
+                               s = _node(STOP, {"offset":Math.floor(style.gradient.stops[i][0] * 100) + "%", "stop-color":stopColor});
+
+                       g.appendChild(s);
+               }
+               var applyGradientTo = style.strokeStyle ? STROKE : FILL;
+        //document.location.toString()
+               //node.setAttribute(STYLE, applyGradientTo + ":url(#" + id + ")");
+        node.setAttribute(STYLE, applyGradientTo + ":url(" + document.location.toString() + "#" + id + ")");
+       },
+       _applyStyles = function(parent, node, style, dimensions, uiComponent) {
+               
+               if (style.gradient) {
+                       _updateGradient(parent, node, style, dimensions, uiComponent);                  
+               }
+               else {
+                       // make sure we clear any existing gradient
+                       _clearGradient(parent);
+                       node.setAttribute(STYLE, "");
+               }
+               
+               node.setAttribute(FILL, style.fillStyle ? jsPlumbUtil.convertStyle(style.fillStyle, true) : NONE);
+               node.setAttribute(STROKE, style.strokeStyle ? jsPlumbUtil.convertStyle(style.strokeStyle, true) : NONE);                
+               if (style.lineWidth) {
+                       node.setAttribute(STROKE_WIDTH, style.lineWidth);
+               }
+       
+               // in SVG there is a stroke-dasharray attribute we can set, and its syntax looks like
+               // the syntax in VML but is actually kind of nasty: values are given in the pixel
+               // coordinate space, whereas in VML they are multiples of the width of the stroked
+               // line, which makes a lot more sense.  for that reason, jsPlumb is supporting both
+               // the native svg 'stroke-dasharray' attribute, and also the 'dashstyle' concept from
+               // VML, which will be the preferred method.  the code below this converts a dashstyle
+               // attribute given in terms of stroke width into a pixel representation, by using the
+               // stroke's lineWidth. 
+               if (style[DASHSTYLE] && style[LINE_WIDTH] && !style[STROKE_DASHARRAY]) {
+                       var sep = style[DASHSTYLE].indexOf(",") == -1 ? " " : ",",
+                       parts = style[DASHSTYLE].split(sep),
+                       styleToUse = "";
+                       parts.forEach(function(p) {
+                               styleToUse += (Math.floor(p * style.lineWidth) + sep);
+                       });
+                       node.setAttribute(STROKE_DASHARRAY, styleToUse);
+               }               
+               else if(style[STROKE_DASHARRAY]) {
+                       node.setAttribute(STROKE_DASHARRAY, style[STROKE_DASHARRAY]);
+               }
+               
+               // extra attributes such as join type, dash offset.
+               for (var i in svgAttributeMap) {
+                       if (style[i]) {
+                               node.setAttribute(svgAttributeMap[i], style[i]);
+                       }
+               }
+       },
+       _decodeFont = function(f) {
+               var r = /([0-9].)(p[xt])\s(.*)/, 
+                       bits = f.match(r);
+
+               return {size:bits[1] + bits[2], font:bits[3]};          
+       },
+       _classManip = function(el, add, clazz) {
+               var classesToAddOrRemove = clazz.split(" "),
+                       className = el.className,
+                       curClasses = className.baseVal.split(" ");
+                       
+               for (var i = 0; i < classesToAddOrRemove.length; i++) {
+                       if (add) {
+                               if (curClasses.indexOf(classesToAddOrRemove[i]) == -1)
+                                       curClasses.push(classesToAddOrRemove[i]);
+                       }
+                       else {
+                               var idx = curClasses.indexOf(classesToAddOrRemove[i]);
+                               if (idx != -1)
+                                       curClasses.splice(idx, 1);
+                       }
+               }
+               
+               el.className.baseVal = curClasses.join(" ");
+       },
+       _addClass = function(el, clazz) { _classManip(el, true, clazz); },
+       _removeClass = function(el, clazz) { _classManip(el, false, clazz); },
+       _appendAtIndex = function(svg, path, idx) {
+               if (svg.childNodes.length > idx) {
+                       svg.insertBefore(path, svg.childNodes[idx]);
+               }
+               else svg.appendChild(path);
+       };
+       
+       /**
+               utility methods for other objects to use.
+       */
+       jsPlumbUtil.svg = {
+               addClass:_addClass,
+               removeClass:_removeClass,
+               node:_node,
+               attr:_attr,
+               pos:_pos
+       };
+       
+ // ************************** / SVG utility methods ********************************************      
+       
+       /*
+        * Base class for SVG components.
+        */     
+       var SvgComponent = function(params) {
+               var pointerEventsSpec = params.pointerEventsSpec || "all", renderer = {};
+                       
+               jsPlumb.jsPlumbUIComponent.apply(this, params.originalArgs);
+               this.canvas = null;this.path = null;this.svg = null; 
+       
+               var clazz = params.cssClass + " " + (params.originalArgs[0].cssClass || ""),            
+                       svgParams = {
+                               "style":"",
+                               "width":0,
+                               "height":0,
+                               "pointer-events":pointerEventsSpec,
+                               "position":"absolute"
+                       };                              
+               this.svg = _node("svg", svgParams);
+               if (params.useDivWrapper) {
+                       this.canvas = document.createElement("div");
+                       this.canvas.style.position = "absolute";
+                       jsPlumbUtil.sizeElement(this.canvas,0,0,1,1);
+                       this.canvas.className = clazz;
+               }
+               else {
+                       _attr(this.svg, { "class":clazz });
+                       this.canvas = this.svg;
+               }
+                       
+               params._jsPlumb.appendElement(this.canvas, params.originalArgs[0].parent);
+               if (params.useDivWrapper) this.canvas.appendChild(this.svg);
+               
+               // TODO this displayElement stuff is common between all components, across all
+               // renderers.  would be best moved to jsPlumbUIComponent.
+               var displayElements = [ this.canvas ];
+               this.getDisplayElements = function() { 
+                       return displayElements; 
+               };
+               
+               this.appendDisplayElement = function(el) {
+                       displayElements.push(el);
+               };      
+               
+               this.paint = function(style, anchor, extents) {                         
+                       if (style != null) {
+                               
+                               var xy = [ this.x, this.y ], wh = [ this.w, this.h ], p;
+                               if (extents != null) {
+                                       if (extents.xmin < 0) xy[0] += extents.xmin;
+                                       if (extents.ymin < 0) xy[1] += extents.ymin;
+                                       wh[0] = extents.xmax + ((extents.xmin < 0) ? -extents.xmin : 0);
+                                       wh[1] = extents.ymax + ((extents.ymin < 0) ? -extents.ymin : 0);
+                               }
+
+                               if (params.useDivWrapper) {                                     
+                                       jsPlumbUtil.sizeElement(this.canvas, xy[0], xy[1], wh[0], wh[1]);
+                                       xy[0] = 0; xy[1] = 0;
+                                       p = _pos([ 0, 0 ]);
+                               }
+                               else
+                                       p = _pos([ xy[0], xy[1] ]);
+                
+                renderer.paint.apply(this, arguments);                                         
+                
+                       _attr(this.svg, {
+                               "style":p,
+                               "width": wh[0],
+                               "height": wh[1]
+                       });                                                     
+                       }
+           };
+               
+               return {
+                       renderer:renderer
+               };
+       };
+       jsPlumbUtil.extend(SvgComponent, jsPlumb.jsPlumbUIComponent, {
+               cleanup:function() {
+                       jsPlumbUtil.removeElement(this.canvas);            
+                       this.svg = null;
+                       this.canvas = null;
+                       this.path = null;                       
+               },
+               setVisible:function(v) {
+                       if (this.canvas) {
+                               this.canvas.style.display = v ? "block" : "none";
+                       }
+                       if (this.bgCanvas) {
+                               this.bgCanvas.style.display = v ? "block" : "none";
+                       }
+               }
+       });
+       
+       /*
+        * Base class for SVG connectors.
+        */ 
+       var SvgConnector = jsPlumb.ConnectorRenderers.svg = function(params) {
+               var self = this,
+                       _super = SvgComponent.apply(this, [ { 
+                               cssClass:params._jsPlumb.connectorClass, 
+                               originalArgs:arguments, 
+                               pointerEventsSpec:"none", 
+                               _jsPlumb:params._jsPlumb
+                       } ]);   
+
+               /*this.pointOnPath = function(location, absolute) {
+                       if (!self.path) return [0,0];
+                       var p = absolute ? location : location * self.path.getTotalLength();
+                       return self.path.getPointAtLength(p);
+               };*/                    
+
+               _super.renderer.paint = function(style, anchor, extents) {
+                       
+                       var segments = self.getSegments(), p = "", offset = [0,0];                      
+                       if (extents.xmin < 0) offset[0] = -extents.xmin;
+                       if (extents.ymin < 0) offset[1] = -extents.ymin;                        
+                       
+                       // create path from segments.   
+                       for (var i = 0; i < segments.length; i++) {
+                               p += jsPlumb.Segments.svg.SegmentRenderer.getPath(segments[i]);
+                               p += " ";
+                       }                       
+                       
+                       var a = { 
+                                       d:p,
+                                       transform:"translate(" + offset[0] + "," + offset[1] + ")",
+                                       "pointer-events":params["pointer-events"] || "visibleStroke"
+                               }, 
+                outlineStyle = null,
+                d = [self.x,self.y,self.w,self.h];                                             
+                       
+                       // outline style.  actually means drawing an svg object underneath the main one.
+                       if (style.outlineColor) {
+                               var outlineWidth = style.outlineWidth || 1,
+                                       outlineStrokeWidth = style.lineWidth + (2 * outlineWidth);
+                               outlineStyle = jsPlumb.CurrentLibrary.extend({}, style);
+                               outlineStyle.strokeStyle = jsPlumbUtil.convertStyle(style.outlineColor);
+                               outlineStyle.lineWidth = outlineStrokeWidth;
+                               
+                               if (self.bgPath == null) {
+                                       self.bgPath = _node("path", a);
+                               _appendAtIndex(self.svg, self.bgPath, 0);
+                               self.attachListeners(self.bgPath, self);
+                               }
+                               else {
+                                       _attr(self.bgPath, a);
+                               }
+                               
+                               _applyStyles(self.svg, self.bgPath, outlineStyle, d, self);
+                       }                       
+                       
+               if (self.path == null) {
+                       self.path = _node("path", a);
+                       _appendAtIndex(self.svg, self.path, style.outlineColor ? 1 : 0);
+                       self.attachListeners(self.path, self);                                  
+               }
+               else {
+                       _attr(self.path, a);
+               }
+                               
+               _applyStyles(self.svg, self.path, style, d, self);
+               };
+               
+               this.reattachListeners = function() {
+                       if (this.bgPath) this.reattachListenersForElement(this.bgPath, this);
+                       if (this.path) this.reattachListenersForElement(this.path, this);
+               };
+       };
+       jsPlumbUtil.extend(jsPlumb.ConnectorRenderers.svg, SvgComponent);
+
+// ******************************* svg segment renderer *****************************************************  
+               
+       jsPlumb.Segments.svg = {
+               SegmentRenderer : {             
+                       getPath : function(segment) {
+                               return ({
+                                       "Straight":function() {
+                                               var d = segment.getCoordinates();
+                                               return "M " + d.x1 + " " + d.y1 + " L " + d.x2 + " " + d.y2;    
+                                       },
+                                       "Bezier":function() {
+                                               var d = segment.params;
+                                               return "M " + d.x1 + " " + d.y1 + 
+                                                       " C " + d.cp1x + " " + d.cp1y + " " + d.cp2x + " " + d.cp2y + " " + d.x2 + " " + d.y2;                  
+                                       },
+                                       "Arc":function() {
+                                               var d = segment.params,
+                                                       laf = segment.sweep > Math.PI ? 1 : 0,
+                                                       sf = segment.anticlockwise ? 0 : 1;                     
+
+                                               return "M" + segment.x1 + " " + segment.y1 + " A " + segment.radius + " " + d.r + " 0 " + laf + "," + sf + " " + segment.x2 + " " + segment.y2;
+                                       }
+                               })[segment.type]();     
+                       }
+               }
+       };
+       
+// ******************************* /svg segments *****************************************************
+   
+    /*
+        * Base class for SVG endpoints.
+        */
+       var SvgEndpoint = window.SvgEndpoint = function(params) {
+               var _super = SvgComponent.apply(this, [ {
+                               cssClass:params._jsPlumb.endpointClass, 
+                               originalArgs:arguments, 
+                               pointerEventsSpec:"all",
+                               useDivWrapper:true,
+                               _jsPlumb:params._jsPlumb
+                       } ]);
+                       
+               _super.renderer.paint = function(style) {
+                       var s = jsPlumb.extend({}, style);
+                       if (s.outlineColor) {
+                               s.strokeWidth = s.outlineWidth;
+                               s.strokeStyle = jsPlumbUtil.convertStyle(s.outlineColor, true);
+                       }
+                       
+                       if (this.node == null) {
+                               this.node = this.makeNode(s);
+                               this.svg.appendChild(this.node);
+                               this.attachListeners(this.node, this);
+                       }
+                       else if (this.updateNode != null) {
+                               this.updateNode(this.node);
+                       }
+                       _applyStyles(this.svg, this.node, s, [ this.x, this.y, this.w, this.h ], this);
+                       _pos(this.node, [ this.x, this.y ]);
+               }.bind(this);
+                               
+       };
+       jsPlumbUtil.extend(SvgEndpoint, SvgComponent, {
+               reattachListeners : function() {
+                       if (this.node) this.reattachListenersForElement(this.node, this);
+               }
+       });
+       
+       /*
+        * SVG Dot Endpoint
+        */
+       jsPlumb.Endpoints.svg.Dot = function() {
+               jsPlumb.Endpoints.Dot.apply(this, arguments);
+               SvgEndpoint.apply(this, arguments);             
+               this.makeNode = function(style) { 
+                       return _node("circle", {
+                "cx"   :       this.w / 2,
+                "cy"   :       this.h / 2,
+                "r"            :       this.radius
+            });                        
+               };
+               this.updateNode = function(node) {
+                       _attr(node, {
+                               "cx":this.w / 2,
+                               "cy":this.h  / 2,
+                               "r":this.radius
+                       });
+               };
+       };
+       jsPlumbUtil.extend(jsPlumb.Endpoints.svg.Dot, [jsPlumb.Endpoints.Dot, SvgEndpoint]);
+       
+       /*
+        * SVG Rectangle Endpoint 
+        */
+       jsPlumb.Endpoints.svg.Rectangle = function() {
+               jsPlumb.Endpoints.Rectangle.apply(this, arguments);
+               SvgEndpoint.apply(this, arguments);             
+               this.makeNode = function(style) {
+                       return _node("rect", {
+                               "width"     :   this.w,
+                               "height"    :   this.h
+                       });
+               };
+               this.updateNode = function(node) {
+                       _attr(node, {
+                               "width":this.w,
+                               "height":this.h
+                       });
+               };                      
+       };              
+       jsPlumbUtil.extend(jsPlumb.Endpoints.svg.Rectangle, [jsPlumb.Endpoints.Rectangle, SvgEndpoint]);
+       
+       /*
+        * SVG Image Endpoint is the default image endpoint.
+        */
+       jsPlumb.Endpoints.svg.Image = jsPlumb.Endpoints.Image;
+       /*
+        * Blank endpoint in svg renderer is the default Blank endpoint.
+        */
+       jsPlumb.Endpoints.svg.Blank = jsPlumb.Endpoints.Blank;  
+       /*
+        * Label overlay in svg renderer is the default Label overlay.
+        */
+       jsPlumb.Overlays.svg.Label = jsPlumb.Overlays.Label;
+       /*
+        * Custom overlay in svg renderer is the default Custom overlay.
+        */
+       jsPlumb.Overlays.svg.Custom = jsPlumb.Overlays.Custom;
+               
+       var AbstractSvgArrowOverlay = function(superclass, originalArgs) {
+       superclass.apply(this, originalArgs);
+       jsPlumb.jsPlumbUIComponent.apply(this, originalArgs);
+        this.isAppendedAtTopLevel = false;
+       var self = this;
+       this.path = null;
+       this.paint = function(params, containerExtents) {
+               // only draws on connections, not endpoints.
+               if (params.component.svg && containerExtents) {
+                       if (this.path == null) {
+                               this.path = _node("path", {
+                                       "pointer-events":"all"  
+                               });
+                               params.component.svg.appendChild(this.path);
+                               
+                               this.attachListeners(this.path, params.component);
+                               this.attachListeners(this.path, this);
+                       }
+                       var clazz = originalArgs && (originalArgs.length == 1) ? (originalArgs[0].cssClass || "") : "",
+                               offset = [0,0];
+
+                       if (containerExtents.xmin < 0) offset[0] = -containerExtents.xmin;
+                       if (containerExtents.ymin < 0) offset[1] = -containerExtents.ymin;
+                       
+                       _attr(this.path, { 
+                               "d"                     :       makePath(params.d),
+                               "class"         :       clazz,
+                               stroke          :       params.strokeStyle ? params.strokeStyle : null,
+                               fill            :       params.fillStyle ? params.fillStyle : null,
+                               transform       :       "translate(" + offset[0] + "," + offset[1] + ")"
+                       });             
+               }
+       };
+       var makePath = function(d) {
+               return "M" + d.hxy.x + "," + d.hxy.y +
+                               " L" + d.tail[0].x + "," + d.tail[0].y + 
+                               " L" + d.cxy.x + "," + d.cxy.y + 
+                               " L" + d.tail[1].x + "," + d.tail[1].y + 
+                               " L" + d.hxy.x + "," + d.hxy.y;
+       };
+       this.reattachListeners = function() {
+                       if (this.path) this.reattachListenersForElement(this.path, this);
+               };              
+    };
+    jsPlumbUtil.extend(AbstractSvgArrowOverlay, [jsPlumb.jsPlumbUIComponent, jsPlumb.Overlays.AbstractOverlay], {
+       cleanup : function() {
+               if (this.path != null) jsPlumb.CurrentLibrary.removeElement(this.path);
+       },
+       setVisible:function(v) {
+               if(this.path != null) (this.path.style.display = (v ? "block" : "none"));
+       }
+    });
+    
+    jsPlumb.Overlays.svg.Arrow = function() {
+       AbstractSvgArrowOverlay.apply(this, [jsPlumb.Overlays.Arrow, arguments]);       
+    };
+    jsPlumbUtil.extend(jsPlumb.Overlays.svg.Arrow, [ jsPlumb.Overlays.Arrow, AbstractSvgArrowOverlay ]);
+    
+    jsPlumb.Overlays.svg.PlainArrow = function() {
+       AbstractSvgArrowOverlay.apply(this, [jsPlumb.Overlays.PlainArrow, arguments]);          
+    };
+    jsPlumbUtil.extend(jsPlumb.Overlays.svg.PlainArrow, [ jsPlumb.Overlays.PlainArrow, AbstractSvgArrowOverlay ]);
+    
+    jsPlumb.Overlays.svg.Diamond = function() {
+       AbstractSvgArrowOverlay.apply(this, [jsPlumb.Overlays.Diamond, arguments]);     
+    };
+    jsPlumbUtil.extend(jsPlumb.Overlays.svg.Diamond, [ jsPlumb.Overlays.Diamond, AbstractSvgArrowOverlay ]);
+
+    // a test
+    jsPlumb.Overlays.svg.GuideLines = function() {
+        var path = null, self = this, p1_1, p1_2;        
+        jsPlumb.Overlays.GuideLines.apply(this, arguments);
+        this.paint = function(params, containerExtents) {
+               if (path == null) {
+                       path = _node("path");
+                       params.connector.svg.appendChild(path);
+                       self.attachListeners(path, params.connector);
+                       self.attachListeners(path, self);
+
+                p1_1 = _node("path");
+                       params.connector.svg.appendChild(p1_1);
+                       self.attachListeners(p1_1, params.connector);
+                       self.attachListeners(p1_1, self);
+
+                p1_2 = _node("path");
+                       params.connector.svg.appendChild(p1_2);
+                       self.attachListeners(p1_2, params.connector);
+                       self.attachListeners(p1_2, self);
+               }
+
+               var offset =[0,0];
+               if (containerExtents.xmin < 0) offset[0] = -containerExtents.xmin;
+               if (containerExtents.ymin < 0) offset[1] = -containerExtents.ymin;
+
+               _attr(path, {
+                       "d"             :       makePath(params.head, params.tail),
+                       stroke  :       "red",
+                       fill    :       null,
+                       transform:"translate(" + offset[0] + "," + offset[1] + ")"
+               });
+
+            _attr(p1_1, {
+                       "d"             :       makePath(params.tailLine[0], params.tailLine[1]),
+                       stroke  :       "blue",
+                       fill    :       null,
+                       transform:"translate(" + offset[0] + "," + offset[1] + ")"
+               });
+
+            _attr(p1_2, {
+                       "d"             :       makePath(params.headLine[0], params.headLine[1]),
+                       stroke  :       "green",
+                       fill    :       null,
+                       transform:"translate(" + offset[0] + "," + offset[1] + ")"
+               });
+       };
+
+        var makePath = function(d1, d2) {
+            return "M " + d1.x + "," + d1.y +
+                   " L" + d2.x + "," + d2.y;
+        };        
+    };
+    jsPlumbUtil.extend(jsPlumb.Overlays.svg.GuideLines, jsPlumb.Overlays.GuideLines);
+})();
\ No newline at end of file