X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=portal%2Fstatic%2Funbound_reservation_static%2Fsrc%2Fconnection.js;fp=portal%2Fstatic%2Funbound_reservation_static%2Fsrc%2Fconnection.js;h=ea8bceb6b3f1aefcb07e1d06f604312e30c9516a;hb=729a9dbb380b51a217194ba2a4e5978186fe50b0;hp=0000000000000000000000000000000000000000;hpb=c4bd5da6e2630eddf1262aa8d808dbb48b097d53;p=unfold.git diff --git a/portal/static/unbound_reservation_static/src/connection.js b/portal/static/unbound_reservation_static/src/connection.js new file mode 100644 index 00000000..ea8bceb6 --- /dev/null +++ b/portal/static/unbound_reservation_static/src/connection.js @@ -0,0 +1,504 @@ +;(function() { + + var makeConnector = function(_jsPlumb, renderMode, connectorName, connectorArgs) { + if (!_jsPlumb.Defaults.DoNotThrowErrors && jsPlumb.Connectors[renderMode][connectorName] == null) + throw { msg:"jsPlumb: unknown connector type '" + connectorName + "'" }; + + return new jsPlumb.Connectors[renderMode][connectorName](connectorArgs); + }, + _makeAnchor = function(anchorParams, elementId, _jsPlumb) { + return (anchorParams) ? _jsPlumb.makeAnchor(anchorParams, elementId, _jsPlumb) : null; + }, + prepareEndpoint = function(_jsPlumb, _newEndpoint, conn, existing, index, params, element, elementId, connectorPaintStyle, connectorHoverPaintStyle) { + var e; + if (existing) { + conn.endpoints[index] = existing; + existing.addConnection(conn); + } else { + if (!params.endpoints) params.endpoints = [ null, null ]; + var ep = params.endpoints[index] || params.endpoint || _jsPlumb.Defaults.Endpoints[index] || jsPlumb.Defaults.Endpoints[index] || _jsPlumb.Defaults.Endpoint || jsPlumb.Defaults.Endpoint; + if (!params.endpointStyles) params.endpointStyles = [ null, null ]; + if (!params.endpointHoverStyles) params.endpointHoverStyles = [ null, null ]; + var es = params.endpointStyles[index] || params.endpointStyle || _jsPlumb.Defaults.EndpointStyles[index] || jsPlumb.Defaults.EndpointStyles[index] || _jsPlumb.Defaults.EndpointStyle || jsPlumb.Defaults.EndpointStyle; + // Endpoints derive their fillStyle from the connector's strokeStyle, if no fillStyle was specified. + if (es.fillStyle == null && connectorPaintStyle != null) + es.fillStyle = connectorPaintStyle.strokeStyle; + + // TODO: decide if the endpoint should derive the connection's outline width and color. currently it does: + //* + if (es.outlineColor == null && connectorPaintStyle != null) + es.outlineColor = connectorPaintStyle.outlineColor; + if (es.outlineWidth == null && connectorPaintStyle != null) + es.outlineWidth = connectorPaintStyle.outlineWidth; + //*/ + + var ehs = params.endpointHoverStyles[index] || params.endpointHoverStyle || _jsPlumb.Defaults.EndpointHoverStyles[index] || jsPlumb.Defaults.EndpointHoverStyles[index] || _jsPlumb.Defaults.EndpointHoverStyle || jsPlumb.Defaults.EndpointHoverStyle; + // endpoint hover fill style is derived from connector's hover stroke style. TODO: do we want to do this by default? for sure? + if (connectorHoverPaintStyle != null) { + if (ehs == null) ehs = {}; + if (ehs.fillStyle == null) { + ehs.fillStyle = connectorHoverPaintStyle.strokeStyle; + } + } + var a = params.anchors ? params.anchors[index] : + params.anchor ? params.anchor : + _makeAnchor(_jsPlumb.Defaults.Anchors[index], elementId, _jsPlumb) || + _makeAnchor(jsPlumb.Defaults.Anchors[index], elementId,_jsPlumb) || + _makeAnchor(_jsPlumb.Defaults.Anchor, elementId,_jsPlumb) || + _makeAnchor(jsPlumb.Defaults.Anchor, elementId, _jsPlumb), + u = params.uuids ? params.uuids[index] : null; + e = _newEndpoint({ + paintStyle : es, hoverPaintStyle:ehs, endpoint : ep, connections : [ conn ], + uuid : u, anchor : a, source : element, scope : params.scope, container:params.container, + reattach:params.reattach || _jsPlumb.Defaults.ReattachConnections, + detachable:params.detachable || _jsPlumb.Defaults.ConnectionsDetachable + }); + conn.endpoints[index] = e; + + if (params.drawEndpoints === false) e.setVisible(false, true, true); + + } + return e; + }; + + jsPlumb.Connection = function(params) { + var _newConnection = params.newConnection, + _newEndpoint = params.newEndpoint, + jpcl = jsPlumb.CurrentLibrary, + _att = jpcl.getAttribute, + _gel = jpcl.getElementObject, + _dom = jpcl.getDOMElement, + _ju = jsPlumbUtil, + _getOffset = jpcl.getOffset; + + this.connector = null; + this.idPrefix = "_jsplumb_c_"; + this.defaultLabelLocation = 0.5; + this.defaultOverlayKeys = ["Overlays", "ConnectionOverlays"]; + this.parent = params.parent; + // if a new connection is the result of moving some existing connection, params.previousConnection + // will have that Connection in it. listeners for the jsPlumbConnection event can look for that + // member and take action if they need to. + this.previousConnection = params.previousConnection; + this.source = _dom(params.source); + this.target = _dom(params.target); + // sourceEndpoint and targetEndpoint override source/target, if they are present. but + // source is not overridden if the Endpoint has declared it is not the final target of a connection; + // instead we use the source that the Endpoint declares will be the final source element. + if (params.sourceEndpoint) this.source = params.sourceEndpoint.endpointWillMoveTo || params.sourceEndpoint.getElement(); + if (params.targetEndpoint) this.target = params.targetEndpoint.getElement(); + + OverlayCapableJsPlumbUIComponent.apply(this, arguments); + + this.sourceId = this._jsPlumb.instance.getId(this.source); + this.targetId = this._jsPlumb.instance.getId(this.target); + this.scope = params.scope; // scope may have been passed in to the connect call. if it wasn't, we will pull it from the source endpoint, after having initialised the endpoints. + this.endpoints = []; + this.endpointStyles = []; + + var _jsPlumb = this._jsPlumb.instance; + this._jsPlumb.visible = true; + this._jsPlumb.editable = params.editable === true; + this._jsPlumb.params = { + parent:params.parent, + cssClass:params.cssClass, + container:params.container, + "pointer-events":params["pointer-events"], + editorParams:params.editorParams + }; + this._jsPlumb.lastPaintedAt = null; + this.getDefaultType = function() { + return { + parameters:{}, + scope:null, + detachable:this._jsPlumb.instance.Defaults.ConnectionsDetachable, + rettach:this._jsPlumb.instance.Defaults.ReattachConnections, + paintStyle:this._jsPlumb.instance.Defaults.PaintStyle || jsPlumb.Defaults.PaintStyle, + connector:this._jsPlumb.instance.Defaults.Connector || jsPlumb.Defaults.Connector, + hoverPaintStyle:this._jsPlumb.instance.Defaults.HoverPaintStyle || jsPlumb.Defaults.HoverPaintStyle, + overlays:this._jsPlumb.instance.Defaults.ConnectorOverlays || jsPlumb.Defaults.ConnectorOverlays + }; + }; + +// INITIALISATION CODE + + // wrapped the main function to return null if no input given. this lets us cascade defaults properly. + + var eS = prepareEndpoint(_jsPlumb, _newEndpoint, this, params.sourceEndpoint, 0, params, this.source, this.sourceId, params.paintStyle, params.hoverPaintStyle); + if (eS) _ju.addToList(params.endpointsByElement, this.sourceId, eS); + var eT = prepareEndpoint(_jsPlumb, _newEndpoint, this, params.targetEndpoint, 1, params, this.target, this.targetId, params.paintStyle, params.hoverPaintStyle); + if (eT) _ju.addToList(params.endpointsByElement, this.targetId, eT); + // if scope not set, set it to be the scope for the source endpoint. + if (!this.scope) this.scope = this.endpoints[0].scope; + + // if explicitly told to (or not to) delete endpoints on detach, override endpoint's preferences + if (params.deleteEndpointsOnDetach != null) { + this.endpoints[0]._deleteOnDetach = params.deleteEndpointsOnDetach; + this.endpoints[1]._deleteOnDetach = params.deleteEndpointsOnDetach; + } + else { + // otherwise, unless the endpoints say otherwise, mark them for deletion. + if (!this.endpoints[0]._doNotDeleteOnDetach) this.endpoints[0]._deleteOnDetach = true; + if (!this.endpoints[1]._doNotDeleteOnDetach) this.endpoints[1]._deleteOnDetach = true; + } + + // TODO these could surely be refactored into some method that tries them one at a time until something exists + this.setConnector(this.endpoints[0].connector || + this.endpoints[1].connector || + params.connector || + _jsPlumb.Defaults.Connector || + jsPlumb.Defaults.Connector, true); + + if (params.path) + this.connector.setPath(params.path); + + this.setPaintStyle(this.endpoints[0].connectorStyle || + this.endpoints[1].connectorStyle || + params.paintStyle || + _jsPlumb.Defaults.PaintStyle || + jsPlumb.Defaults.PaintStyle, true); + + this.setHoverPaintStyle(this.endpoints[0].connectorHoverStyle || + this.endpoints[1].connectorHoverStyle || + params.hoverPaintStyle || + _jsPlumb.Defaults.HoverPaintStyle || + jsPlumb.Defaults.HoverPaintStyle, true); + + this._jsPlumb.paintStyleInUse = this.getPaintStyle(); + + var _suspendedAt = _jsPlumb.getSuspendedAt(); + _jsPlumb.updateOffset( { elId : this.sourceId, timestamp:_suspendedAt }); + _jsPlumb.updateOffset( { elId : this.targetId, timestamp:_suspendedAt }); + +//* + if(!_jsPlumb.isSuspendDrawing()) { + // paint the endpoints + var myInfo = _jsPlumb.getCachedData(this.sourceId), + myOffset = myInfo.o, myWH = myInfo.s, + otherInfo = _jsPlumb.getCachedData(this.targetId), + otherOffset = otherInfo.o, + otherWH = otherInfo.s, + initialTimestamp = _suspendedAt || _jsPlumb.timestamp(), + anchorLoc = this.endpoints[0].anchor.compute( { + xy : [ myOffset.left, myOffset.top ], wh : myWH, element : this.endpoints[0], + elementId:this.endpoints[0].elementId, + txy : [ otherOffset.left, otherOffset.top ], twh : otherWH, tElement : this.endpoints[1], + timestamp:initialTimestamp + }); + + this.endpoints[0].paint( { anchorLoc : anchorLoc, timestamp:initialTimestamp }); + + anchorLoc = this.endpoints[1].anchor.compute( { + xy : [ otherOffset.left, otherOffset.top ], wh : otherWH, element : this.endpoints[1], + elementId:this.endpoints[1].elementId, + txy : [ myOffset.left, myOffset.top ], twh : myWH, tElement : this.endpoints[0], + timestamp:initialTimestamp + }); + this.endpoints[1].paint({ anchorLoc : anchorLoc, timestamp:initialTimestamp }); + } + //*/ + +// END INITIALISATION CODE + +// DETACHABLE + this._jsPlumb.detachable = _jsPlumb.Defaults.ConnectionsDetachable; + if (params.detachable === false) this._jsPlumb.detachable = false; + if(this.endpoints[0].connectionsDetachable === false) this._jsPlumb.detachable = false; + if(this.endpoints[1].connectionsDetachable === false) this._jsPlumb.detachable = false; +// REATTACH + this._jsPlumb.reattach = params.reattach || this.endpoints[0].reattachConnections || this.endpoints[1].reattachConnections || _jsPlumb.Defaults.ReattachConnections; +// COST + DIRECTIONALITY + // if cost not supplied, try to inherit from source endpoint + this._jsPlumb.cost = params.cost || this.endpoints[0].getConnectionCost(); + this._jsPlumb.directed = params.directed; + // inherit directed flag if set no source endpoint + if (params.directed == null) this._jsPlumb.directed = this.endpoints[0].areConnectionsDirected(); +// END COST + DIRECTIONALITY + +// PARAMETERS + // merge all the parameters objects into the connection. parameters set + // on the connection take precedence; then source endpoint params, then + // finally target endpoint params. + // TODO jsPlumb.extend could be made to take more than two args, and it would + // apply the second through nth args in order. + var _p = jsPlumb.extend({}, this.endpoints[1].getParameters()); + jsPlumb.extend(_p, this.endpoints[0].getParameters()); + jsPlumb.extend(_p, this.getParameters()); + this.setParameters(_p); +// END PARAMETERS + +// PAINTING + + // the very last thing we do is apply types, if there are any. + var _types = [params.type, this.endpoints[0].connectionType, this.endpoints[1].connectionType ].join(" "); + if (/[a-zA-Z]/.test(_types)) + this.addType(_types, params.data, true); + + +// END PAINTING + }; + + jsPlumbUtil.extend(jsPlumb.Connection, OverlayCapableJsPlumbUIComponent, { + applyType : function(t, doNotRepaint) { + if (t.detachable != null) this.setDetachable(t.detachable); + if (t.reattach != null) this.setReattach(t.reattach); + if (t.scope) this.scope = t.scope; + //editable = t.editable; // TODO + this.setConnector(t.connector, doNotRepaint); + }, + getTypeDescriptor : function() { return "connection"; }, + getAttachedElements : function() { + return this.endpoints; + }, + addClass : function(c, informEndpoints) { + if (informEndpoints) { + this.endpoints[0].addClass(c); + this.endpoints[1].addClass(c); + if (this.suspendedEndpoint) this.suspendedEndpoint.addClass(c); + } + if (this.connector) { + this.connector.addClass(c); + } + }, + removeClass : function(c, informEndpoints) { + if (informEndpoints) { + this.endpoints[0].removeClass(c); + this.endpoints[1].removeClass(c); + if (this.suspendedEndpoint) this.suspendedEndpoint.removeClass(c); + } + if (this.connector) { + this.connector.removeClass(c); + } + }, + isVisible : function() { return this._jsPlumb.visible; }, + setVisible : function(v) { + this._jsPlumb.visible = v; + //this[v ? "showOverlays" : "hideOverlays"](); + if (this.connector) + this.connector.setVisible(v); + this.repaint(); + }, + + /* TODO move to connecto editors; it should put these on the prototype. + + setEditable : function(e) { + if (this.connector && this.connector.isEditable()) + this._jsPlumb.editable = e; + + return this._jsPlumb.editable; + }, + isEditable : function() { return this._jsPlumb.editable; }, + editStarted : function() { + this.setSuspendEvents(true); + this.fire("editStarted", { + path:this.connector.getPath() + }); + this._jsPlumb.instance.setHoverSuspended(true); + }, + editCompleted : function() { + this.fire("editCompleted", { + path:this.connector.getPath() + }); + this.setSuspendEvents(false); + this.setHover(false); + this._jsPlumb.instance.setHoverSuspended(false); + }, + editCanceled : function() { + this.fire("editCanceled", { + path:this.connector.getPath() + }); + this.setHover(false); + this._jsPlumb.instance.setHoverSuspended(false); + }, + +*/ + + cleanup:function() { + //this.endpointsToDeleteOnDetach = null; + this.endpoints = null; + this.source = null; + this.target = null; + if (this.connector != null) { + this.connector.cleanup(); + this.connector.destroy(); + } + this.connector = null; + }, + isDetachable : function() { + return this._jsPlumb.detachable === true; + }, + setDetachable : function(detachable) { + this._jsPlumb.detachable = detachable === true; + }, + isReattach : function() { + return this._jsPlumb.reattach === true; + }, + setReattach : function(reattach) { + this._jsPlumb.reattach = reattach === true; + }, + setHover : function(state) { + if (this.connector && this._jsPlumb && !this._jsPlumb.instance.isConnectionBeingDragged()) { + this.connector.setHover(state); + jsPlumb.CurrentLibrary[state ? "addClass" : "removeClass"](this.source, this._jsPlumb.instance.hoverSourceClass); + jsPlumb.CurrentLibrary[state ? "addClass" : "removeClass"](this.target, this._jsPlumb.instance.hoverTargetClass); + } + }, + getCost : function() { return this._jsPlumb.cost; }, + setCost : function(c) { this._jsPlumb.cost = c; }, + isDirected : function() { return this._jsPlumb.directed === true; }, + // + // changes the parent element of this connection to newParent. not exposed for the public API. + // + // TODO ensure moveParent method still works (the overlay stuff in particular) + moveParent : function(newParent) { + var jpcl = jsPlumb.CurrentLibrary, curParent = jpcl.getParent(this.connector.canvas); + if (this.connector.bgCanvas) { + jpcl.removeElement(this.connector.bgCanvas); + jpcl.appendElement(this.connector.bgCanvas, newParent); + } + jpcl.removeElement(this.connector.canvas); + jpcl.appendElement(this.connector.canvas, newParent); + // this only applies for DOMOverlays + for (var i = 0; i < this._jsPlumb.overlays.length; i++) { + if (this._jsPlumb.overlays[i].isAppendedAtTopLevel) { + jpcl.removeElement(this._jsPlumb.overlays[i].canvas); + jpcl.appendElement(this._jsPlumb.overlays[i].canvas, newParent); + if (this._jsPlumb.overlays[i].reattachListeners) + this._jsPlumb.overlays[i].reattachListeners(this.connector); + } + } + if (this.connector.reattachListeners) // this is for SVG/VML; change an element's parent and you have to reinit its listeners. + this.connector.reattachListeners(); // the Canvas implementation doesn't have to care about this + }, + getConnector : function() { return this.connector; }, + setConnector : function(connectorSpec, doNotRepaint) { + var _ju = jsPlumbUtil; + if (this.connector != null) { + this.connector.cleanup(); + this.connector.destroy(); + } + + var connectorArgs = { + _jsPlumb:this._jsPlumb.instance, + parent:this._jsPlumb.params.parent, + cssClass:this._jsPlumb.params.cssClass, + container:this._jsPlumb.params.container, + "pointer-events":this._jsPlumb.params["pointer-events"] + }, + renderMode = this._jsPlumb.instance.getRenderMode(); + + if (_ju.isString(connectorSpec)) + this.connector = makeConnector(this._jsPlumb.instance, renderMode, connectorSpec, connectorArgs); // lets you use a string as shorthand. + else if (_ju.isArray(connectorSpec)) { + if (connectorSpec.length == 1) + this.connector = makeConnector(this._jsPlumb.instance, renderMode, connectorSpec[0], connectorArgs); + else + this.connector = makeConnector(this._jsPlumb.instance, renderMode, connectorSpec[0], _ju.merge(connectorSpec[1], connectorArgs)); + } + // binds mouse listeners to the current connector. + this.bindListeners(this.connector, this, function(state) { + this.setHover(state, false); + }.bind(this)); + + this.canvas = this.connector.canvas; + + if (this._jsPlumb.editable && jsPlumb.ConnectorEditors != null && jsPlumb.ConnectorEditors[this.connector.type] && this.connector.isEditable()) { + new jsPlumb.ConnectorEditors[this.connector.type]({ + connector:this.connector, + connection:this, + params:this._jsPlumb.params.editorParams || { } + }); + } + else { + this._jsPlumb.editable = false; + } + + if (!doNotRepaint) this.repaint(); + }, + paint : function(params) { + + if (!this._jsPlumb.instance.isSuspendDrawing() && this._jsPlumb.visible) { + + params = params || {}; + var elId = params.elId, ui = params.ui, recalc = params.recalc, timestamp = params.timestamp, + // if the moving object is not the source we must transpose the two references. + swap = false, + tId = swap ? this.sourceId : this.targetId, sId = swap ? this.targetId : this.sourceId, + tIdx = swap ? 0 : 1, sIdx = swap ? 1 : 0; + + if (timestamp == null || timestamp != this._jsPlumb.lastPaintedAt) { + var sourceInfo = this._jsPlumb.instance.updateOffset( { elId : sId, offset : ui, recalc : recalc, timestamp : timestamp }).o, + targetInfo = this._jsPlumb.instance.updateOffset( { elId : tId, timestamp : timestamp }).o, // update the target if this is a forced repaint. otherwise, only the source has been moved. + sE = this.endpoints[sIdx], tE = this.endpoints[tIdx]; + + if (params.clearEdits) { + this._jsPlumb.overlayPositions = null; + sE.anchor.clearUserDefinedLocation(); + tE.anchor.clearUserDefinedLocation(); + this.connector.setEdited(false); + } + + var sAnchorP = sE.anchor.getCurrentLocation({xy:[sourceInfo.left,sourceInfo.top], wh:[sourceInfo.width, sourceInfo.height], element:sE, timestamp:timestamp}), + tAnchorP = tE.anchor.getCurrentLocation({xy:[targetInfo.left,targetInfo.top], wh:[targetInfo.width, targetInfo.height], element:tE, timestamp:timestamp}); + + this.connector.resetBounds(); + + this.connector.compute({ + sourcePos:sAnchorP, + targetPos:tAnchorP, + sourceEndpoint:this.endpoints[sIdx], + targetEndpoint:this.endpoints[tIdx], + lineWidth:this._jsPlumb.paintStyleInUse.lineWidth, + sourceInfo:sourceInfo, + targetInfo:targetInfo, + clearEdits:params.clearEdits === true + }); + + var overlayExtents = { minX:Infinity, minY:Infinity, maxX:-Infinity, maxY:-Infinity }; + + // compute overlays. we do this first so we can get their placements, and adjust the + // container if needs be (if an overlay would be clipped) + for ( var i = 0; i < this._jsPlumb.overlays.length; i++) { + var o = this._jsPlumb.overlays[i]; + if (o.isVisible()) { + this._jsPlumb.overlayPlacements[i] = o.draw(this.connector, this._jsPlumb.paintStyleInUse, this.getAbsoluteOverlayPosition(o)); + overlayExtents.minX = Math.min(overlayExtents.minX, this._jsPlumb.overlayPlacements[i].minX); + overlayExtents.maxX = Math.max(overlayExtents.maxX, this._jsPlumb.overlayPlacements[i].maxX); + overlayExtents.minY = Math.min(overlayExtents.minY, this._jsPlumb.overlayPlacements[i].minY); + overlayExtents.maxY = Math.max(overlayExtents.maxY, this._jsPlumb.overlayPlacements[i].maxY); + } + } + + var lineWidth = parseFloat(this._jsPlumb.paintStyleInUse.lineWidth || 1) / 2, + outlineWidth = parseFloat(this._jsPlumb.paintStyleInUse.lineWidth || 0), + extents = { + xmin : Math.min(this.connector.bounds.minX - (lineWidth + outlineWidth), overlayExtents.minX), + ymin : Math.min(this.connector.bounds.minY - (lineWidth + outlineWidth), overlayExtents.minY), + xmax : Math.max(this.connector.bounds.maxX + (lineWidth + outlineWidth), overlayExtents.maxX), + ymax : Math.max(this.connector.bounds.maxY + (lineWidth + outlineWidth), overlayExtents.maxY) + }; + + // paint the connector. + this.connector.paint(this._jsPlumb.paintStyleInUse, null, extents); + // and then the overlays + for ( var j = 0; j < this._jsPlumb.overlays.length; j++) { + var p = this._jsPlumb.overlays[j]; + if (p.isVisible()) { + p.paint(this._jsPlumb.overlayPlacements[j], extents); + } + } + } + this._jsPlumb.lastPaintedAt = timestamp; + } + }, + /* + * Function: repaint + * Repaints the Connection. No parameters exposed to public API. + */ + repaint : function(params) { + params = params || {}; + this.paint({ elId : this.sourceId, recalc : !(params.recalc === false), timestamp:params.timestamp, clearEdits:params.clearEdits }); + } + + }); // END Connection class +})(); \ No newline at end of file