X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=portal%2Fstatic%2Funbound_reservation_static%2Fsrc%2Frenderers-svg.js;fp=portal%2Fstatic%2Funbound_reservation_static%2Fsrc%2Frenderers-svg.js;h=6d7f601d5fbdb3ecdd1b824def377b6f4961cf77;hb=729a9dbb380b51a217194ba2a4e5978186fe50b0;hp=0000000000000000000000000000000000000000;hpb=c4bd5da6e2630eddf1262aa8d808dbb48b097d53;p=unfold.git 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 index 00000000..6d7f601d --- /dev/null +++ b/portal/static/unbound_reservation_static/src/renderers-svg.js @@ -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