reservation plugin - unbound request (unclean
[unfold.git] / portal / static / unbound_reservation_static / src / connector-editors.js
diff --git a/portal/static/unbound_reservation_static/src/connector-editors.js b/portal/static/unbound_reservation_static/src/connector-editors.js
new file mode 100644 (file)
index 0000000..901ea4c
--- /dev/null
@@ -0,0 +1,369 @@
+/*
+ * 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 jsPlumb connector editors.  It is not deployed wth the released versions of jsPlumb; you need to
+ * include it as an extra script.
+ *
+ * Copyright (c) 2010 - 2013 Simon Porritt (simon.porritt@gmail.com)
+ * 
+ * http://jsplumb.org
+ * http://github.com/sporritt/jsplumb
+ * http://code.google.com/p/jsplumb
+ * 
+ * Dual licensed under the MIT and GPL2 licenses.
+ */
+;(function() {
+    
+    var AbstractEditor = function(params) {
+        var self = this;        
+    };
+
+    var isTouchDevice = "ontouchstart" in document.documentElement,
+        downEvent = isTouchDevice ? "touchstart" : "mousedown",
+        upEvent = isTouchDevice ? "touchend" : "mouseup",
+        moveEvent = isTouchDevice ? "touchmove" : "mousemove";
+    
+    // TODO: this is for a Straight segment.it would be better to have these all available somewjere, keyed
+    // by segment type
+    var findClosestPointOnPath = function(seg, x, y, i, bounds) {
+        var m = seg[0] == seg[2] ? Infinity : 0,
+            m2 = -1 / m,
+            out = { s:seg, m:m, i:i, x:-1, y:-1, d:Infinity };
+        
+        if (m == 0) {
+            // a horizontal line. if x is in the range of this line then distance is delta y. otherwise we consider it to be
+            // infinity.
+            if ( (seg[0] <= x && x <= seg[2]) || (seg[2] <= x && x <= seg[0])) {
+                out.x = x,
+                out.y = seg[1];
+                out.d = Math.abs(y - seg[1]);
+            }
+        }
+        else if (m == Infinity || m == -Infinity) {
+            // a vertical line. if y is in the range of this line then distance is delta x. otherwise we consider it to be
+            // infinity.
+            if ((seg[1] <= y && y <= seg[3]) || (seg[3] <= y && y <= seg[1])){
+                out.x = seg[0];
+                out.y = y;
+                out.d = Math.abs(x - seg[0]);
+            }                        
+        }
+        else {
+            // closest point lies on normal from given point to this line.  
+            var b = seg[1] - (m * seg[0]),
+                b2 = y - (m2 * x),
+            // now we know that
+            // y1 = m.x1 + b   and   y1 = m2.x1 + b2
+            // so:  m.x1 + b = m2.x1 + b2
+            //      x1(m - m2) = b2 - b
+            //      x1 = (b2 - b) / (m - m2)
+                _x1 = (b2 -b) / (m - m2),
+                _y1 = (m * _x1) + b,
+                d = jsPlumbGeom.lineLength([ x, y ], [ _x1, _y1 ]),
+                fractionInSegment = jsPlumbGeom.lineLength([ _x1, _y1 ], [ seg[0], seg[1] ]);
+            
+            out.d = d;
+            out.x = _x1;
+            out.y = _y1;
+            out.l = fractionInSegment / length;
+        }
+        return out;
+    };
+    
+    /**
+    * @namespace jsPlumb.ConnectorEditors
+    * @desc These are editors for the various connector types. They are not included in the
+    * main jsPlumb release. To use them you have to build a custom version of jsPlumb - see
+    * the Gruntfile for information on how to do that. 
+    *
+    * Currently there is only an editor for the Flowchart connector.
+    */
+    jsPlumb.ConnectorEditors = {
+        /**
+        * @name jsPlumb.ConnectorEditors.FlowchartConnectorEditor
+        * @class
+        * @classdesc Lets you drag the segments of a flowchart connection around. If you subsequently
+        * drag an element, your edits are lost.
+        */
+        "Flowchart":function(params) {
+            AbstractEditor.apply(this, arguments);            
+            
+            var jpcl = jsPlumb.CurrentLibrary,
+                clickConsumer = function(conn) {                     
+                    conn._jsPlumb.afterEditClick = function() {
+                        console.log("after edit click");
+                        conn.unbind("click", conn._jsPlumb.afterEditClick);
+                        conn._jsPlumb.afterEditClick = null;
+                        return false;
+                    }; 
+                    conn.bind("click", conn._jsPlumb.afterEditClick, true);                    
+                },
+                documentMouseUp = function(e) {       
+
+                    // an attempt at consuming the click that occurs after this mouseup
+                    // it's not reliable though, as we dont always get a click fired, for some
+                    // reason.
+                    //if (editing)
+                    //    clickConsumer(params.connection);
+
+                    jpcl.removeClass(document.body, params.connection._jsPlumb.instance.dragSelectClass);
+                    params.connection._jsPlumb.instance.setConnectionBeingDragged(false);
+                    e.stopPropagation();
+                    e.preventDefault();
+                    jpcl.unbind(document, upEvent, documentMouseUp);
+                    jpcl.unbind(document, moveEvent, documentMouseMove);                    
+                    downAt = null;
+                    currentSegments = null;
+                    selectedSegment = null; 
+                    segmentCoords = null;
+                    params.connection.setHover(false);                    
+                    params.connector.setSuspendEvents(false); 
+                    params.connection.endpoints[0].setSuspendEvents(false);                
+                    params.connection.endpoints[1].setSuspendEvents(false);
+                    params.connection.editCompleted();
+                    params.connector.justEdited = editing;
+                    editing = false;            
+                },
+                downAt = null,
+                currentSegments = null,
+                selectedSegment = null,
+                segmentCoords = null,
+                editing = false,
+                anchorsMoveable = params.params.anchorsMoveable,
+                sgn = function(p1, p2) {
+                    if (p1[0] == p2[0])
+                        return p1[1] < p2[1]  ? 1 : -1;
+                    else
+                        return p1[0] < p2[0]  ? 1 : -1;
+                },
+                // collapses currentSegments by joining subsequent segments that are in the
+                // same axis. we do this because it doesn't matter about stubs any longer once a user
+                // is editing a connector. so it is best to reduce the number of segments to the 
+                // minimum.
+                _collapseSegments = function() {                       
+                    var _last = null, _lastAxis = null, s = [];
+                    for (var i = 0; i < currentSegments.length; i++) {
+                        var seg = currentSegments[i], axis = seg[4], axisIndex = (axis == "v" ? 3 : 2);
+                        if (_last != null && _lastAxis === axis) {
+                            _last[axisIndex] = seg[axisIndex];                            
+                        }
+                        else {
+                            s.push(seg);
+                            _last = seg;
+                            _lastAxis = seg[4];
+                        }
+                    }
+                    currentSegments = s;                   
+                },
+                // attempt to shift anchor
+                _shiftAnchor = function(endpoint, horizontal, value) {                    
+                    var elementSize = jpcl.getSize(endpoint.element),
+                        sizeValue = elementSize[horizontal ? 1 : 0],
+                        ee = jpcl.getElementObject(endpoint.element),
+                        off = jpcl.getOffset(ee), 
+                        cc = jpcl.getElementObject(params.connector.canvas.parentNode),
+                        co = jpcl.getOffset(cc),
+                        offValue = off[horizontal ? "top" : "left"] - co[horizontal ? "top" : "left"], 
+                        ap = endpoint.anchor.getCurrentLocation({element:endpoint}),
+                        desiredLoc = horizontal ? params.connector.y + value : params.connector.x + value;
+                    
+                    if (anchorsMoveable) {                        
+                        
+                        if (offValue < desiredLoc && desiredLoc < offValue + sizeValue) {
+                            // if still on the element, okay to move.
+                            var udl = [ ap[0], ap[1] ];
+                            ap[horizontal ? 1 : 0] = desiredLoc;
+                            endpoint.anchor.setUserDefinedLocation(ap);
+                            return value;
+                        }
+                        else {                        
+                            // otherwise, clamp to element edge
+                            var edgeVal = desiredLoc < offValue ? offValue : offValue + sizeValue;
+                            return edgeVal - (horizontal ? params.connector.y: params.connector.x);                         
+                        }                    
+                    }
+                    else {
+                        // otherwise, return the current anchor point.
+                        return ap[horizontal ? 1 : 0] - params.connector[horizontal ? "y" : "x"];
+                    }
+                },
+                _updateSegmentOrientation = function(seg) {
+                    if (seg[0] != seg[2]) seg[5] = (seg[0] < seg[2]) ? 1 : -1;
+                    if (seg[1] != seg[3]) seg[6] = (seg[1] < seg[3]) ? 1 : -1;
+                },
+                documentMouseMove = function(e) {
+                    if (selectedSegment != null) {
+                        // suspend events on first move.
+                        if (!editing) {
+                            params.connection.setHover(true);
+                            params.connector.setSuspendEvents(true);
+                            params.connection.endpoints[0].setSuspendEvents(true);                
+                            params.connection.endpoints[1].setSuspendEvents(true);
+                        }
+                        editing = true;
+                        var m = selectedSegment.m, s = selectedSegment.s,
+                            x = (e.pageX || e.page.x), y = (e.pageY || e.page.y),
+                            dx = m == 0 ? 0 : x - downAt[0], dy = m == 0 ? y - downAt[1] : 0,
+                            newX1 = segmentCoords[0] + dx,
+                            newY1 = segmentCoords[1] + dy,
+                            newX2 = segmentCoords[2] + dx,
+                            newY2 = segmentCoords[3] + dy,
+                            horizontal = s[4] == "h";
+                        
+                        // so here we know the new x,y values we would like to set for the start
+                        // and end of this segment. but we may not be able to set these values: if this
+                        // is the first segment, for example, then we are constrained by how far the anchor
+                        // can move (before it slides off its element). same thing goes if this is the last
+                        // segment. if this is not the first or last segment then there are other considerations.
+                        // we know, from having run collapse segments, that there will never be two
+                        // consecutive segments that are not at right angles to each other, so what we need to
+                        // know is whether we can adjust the endpoint of the previous segment to the values we
+                        // want, and the same question for the start values of the next segment.  the answer to
+                        // that is whether or not the segment in question would be rendered too small by such
+                        // a change. if that is the case (and the same goes for anchors) then we want to know
+                        // what an agreeable value is, and we use that.
+                        
+                        if (selectedSegment.i == 0) {
+                                                        
+                            var anchorLoc = _shiftAnchor(params.connection.endpoints[0], horizontal, horizontal ? newY1 : newX1);                            
+                            if (horizontal) 
+                                newY1 = newY2 = anchorLoc; 
+                            else
+                                newX1 = newX2 = anchorLoc;
+                        
+                            currentSegments[1][0] = newX2;
+                            currentSegments[1][1] = newY2;
+                            _updateSegmentOrientation(currentSegments[1]);                                                                                            
+                        }
+                        else if (selectedSegment.i == currentSegments.length - 1) {
+                            var anchorLoc = _shiftAnchor(params.connection.endpoints[1], horizontal, horizontal ? newY1 : newX1);                          
+                            if (horizontal) 
+                                newY1 = newY2 = anchorLoc; 
+                            else
+                                newX1 = newX2 = anchorLoc;
+                            
+                            currentSegments[currentSegments.length - 2][2] = newX1;
+                            currentSegments[currentSegments.length - 2][3] = newY1;
+                            _updateSegmentOrientation(currentSegments[currentSegments.length - 2]);
+                        }
+                        else {
+                            if (!horizontal) {
+                                currentSegments[selectedSegment.i - 1][2] = newX1;
+                                currentSegments[selectedSegment.i + 1][0] = newX2;                                                                
+                            }
+                            else {
+                                currentSegments[selectedSegment.i - 1][3] = newY1;                            
+                                currentSegments[selectedSegment.i + 1][1] = newY2;
+                            }
+                            _updateSegmentOrientation(currentSegments[selectedSegment.i + 1]);
+                            _updateSegmentOrientation(currentSegments[selectedSegment.i - 1]);                            
+                        }
+                                                                                                
+                        s[0] = newX1;
+                        s[1] = newY1;
+                        s[2] = newX2;
+                        s[3] = newY2;                                              
+                        
+                        params.connector.setSegments(currentSegments);
+                        params.connection.repaint();                        
+                        params.connection.endpoints[0].repaint();
+                        params.connection.endpoints[1].repaint();
+                        params.connector.setEdited(true);                        
+                    }
+                    else
+                        editing = false;
+                };
+                        
+            //bind to mousedown and mouseup, for editing
+            params.connector.bind(downEvent, function(c, e) {
+                var x = (e.pageX || e.page.x),
+                    y = (e.pageY || e.page.y),
+                    oe = jpcl.getElementObject(params.connection.getConnector().canvas),
+                    o = jpcl.getOffset(oe),                    
+                    minD = Infinity;
+
+                // TODO this is really the way we want to go: get the segment from the connector.
+                // for now it's just here to remind me what to change.
+                var __seg = params.connector.findSegmentForPoint(x-o.left, y-o.top);
+                console.log(__seg);
+                
+                currentSegments = params.connector.getOriginalSegments();
+                _collapseSegments();
+                for (var i = 0; i < currentSegments.length; i++) {                    
+                    var _s = findClosestPointOnPath(currentSegments[i], (x - o.left) , (y - o.top), i, params.connector.bounds);
+                    
+                    //var _s = currentSegments[i].findClosestPointOnPath(x - o.left, y - o.top);
+                    
+                    if (_s.d < minD) {
+                        selectedSegment = _s;
+                        segmentCoords = [ _s.s[0], _s.s[1], _s.s[2], _s.s[3] ]; // copy the coords at mousedown
+                        minD = _s.d;
+                    }
+                }
+                
+                downAt = [ x, y ];
+                
+                if (selectedSegment != null) {                    
+                    jpcl.bind(document, upEvent, documentMouseUp);
+                    jpcl.bind(document, moveEvent, documentMouseMove);                                      
+                    jpcl.addClass(document.body, params.connection._jsPlumb.instance.dragSelectClass);
+                    params.connection._jsPlumb.instance.setConnectionBeingDragged(true);
+                    params.connection.editStarted();                
+                    return false;
+                }
+            }, true);
+        }
+    };
+
+    jsPlumb.Connectors.AbstractConnector.prototype.shouldFireEvent = function(type, value, originalEvent) {
+        var out = !this.justEdited;
+        if (type == "click") {            
+            this.justEdited = false;
+        }
+        return out;
+    };
+
+// ------------------ augment the Connection prototype with the editing stuff --------------------------
+
+    var EDIT_STARTED = "editStarted", EDIT_COMPLETED = "editCompleted", EDIT_CANCELED = "editCanceled";
+
+    jsPlumb.Connection.prototype.setEditable = function(e) {
+        if (this.connector && this.connector.isEditable())
+            this._jsPlumb.editable = e;
+        
+        return this._jsPlumb.editable;
+    };
+
+    jsPlumb.Connection.prototype.isEditable = function() { return this._jsPlumb.editable; };
+
+    jsPlumb.Connection.prototype.editStarted = function() {  
+        this.setSuspendEvents(true);
+        this.fire(EDIT_STARTED, {
+            path:this.connector.getPath()
+        });            
+        this._jsPlumb.instance.setHoverSuspended(true);
+    };
+
+    jsPlumb.Connection.prototype.editCompleted = function() {            
+        this.fire(EDIT_COMPLETED, {
+            path:this.connector.getPath()
+        });       
+        this.setSuspendEvents(false);        
+        this._jsPlumb.instance.setHoverSuspended(false);
+        this.setHover(false);
+    };
+
+    jsPlumb.Connection.prototype.editCanceled = function() {
+        this.fire(EDIT_CANCELED, {
+            path:this.connector.getPath()
+        });        
+        this._jsPlumb.instance.setHoverSuspended(false);
+        this.setHover(false);
+    };
+        
+})();
\ No newline at end of file