mirror of
				https://git.code.sf.net/p/seeddms/code
				synced 2025-10-30 20:51:22 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1277 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			1277 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
 | |
| module.exports = function (cytoscape, $) {
 | |
|     
 | |
|     // Needed because parent nodes cannot be moved!
 | |
|     function moveTopDown(node, dx, dy) {
 | |
|         var nodes = node.union(node.descendants());
 | |
| 
 | |
|         nodes.positions(function (i, node) {
 | |
|             var pos = node.position();
 | |
|             return {
 | |
|                 x: pos.x + dx,
 | |
|                 y: pos.y + dy
 | |
|             };
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     function getTopMostNodes(nodes) {
 | |
|         var nodesMap = {};
 | |
|         for (var i = 0; i < nodes.length; i++) {
 | |
|             nodesMap[nodes[i].id()] = true;
 | |
|         }
 | |
|         var roots = nodes.filter(function (i, ele) {
 | |
|             var parent = ele.parent()[0];
 | |
|             while(parent != null){
 | |
|                 if(nodesMap[parent.id()]){
 | |
|                     return false;
 | |
|                 }
 | |
|                 parent = parent.parent()[0];
 | |
|             }
 | |
|             return true;
 | |
|         });
 | |
| 
 | |
|         return roots;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     cytoscape( "collection", "align", function (horizontal, vertical, alignTo) {
 | |
| 
 | |
|         var eles = getTopMostNodes(this.nodes(":visible"));
 | |
| 
 | |
|         var modelNode = alignTo ? alignTo : eles[0];
 | |
| 
 | |
|         eles = eles.not(modelNode);
 | |
| 
 | |
|         horizontal = horizontal ? horizontal : "none";
 | |
|         vertical = vertical ? vertical : "none";
 | |
| 
 | |
| 
 | |
|         // 0 for center
 | |
|         var xFactor = 0;
 | |
|         var yFactor = 0;
 | |
| 
 | |
|         if (vertical == "left")
 | |
|             xFactor = -1;
 | |
|         else if (vertical == "right")
 | |
|             xFactor = 1;
 | |
| 
 | |
|         if (horizontal == "top")
 | |
|             yFactor = -1;
 | |
|         else if (horizontal == "bottom")
 | |
|             yFactor = 1;
 | |
| 
 | |
| 
 | |
|         for (var i = 0; i < eles.length; i++) {
 | |
|             var node = eles[i];
 | |
|             var oldPos = $.extend({}, node.position());
 | |
|             var newPos = $.extend({}, node.position());
 | |
| 
 | |
|             if (vertical != "none")
 | |
|                 newPos.x = modelNode.position("x") + xFactor * (modelNode.width() - node.width()) / 2;
 | |
| 
 | |
| 
 | |
|             if (horizontal != "none")
 | |
|                 newPos.y = modelNode.position("y") + yFactor * (modelNode.height() - node.height()) / 2;
 | |
| 
 | |
|             moveTopDown(node, newPos.x - oldPos.x, newPos.y - oldPos.y);
 | |
|         }
 | |
| 
 | |
|         return this;
 | |
|     });
 | |
| 
 | |
|     if (cy.undoRedo) {
 | |
|         function getNodePositions() {
 | |
|             var positionsAndSizes = {};
 | |
|             var nodes = cy.nodes();
 | |
| 
 | |
|             for (var i = 0; i < nodes.length; i++) {
 | |
|                 var ele = nodes[i];
 | |
|                 positionsAndSizes[ele.id()] = {
 | |
|                     x: ele.position("x"),
 | |
|                     y: ele.position("y")
 | |
|                 };
 | |
|             }
 | |
| 
 | |
|             return positionsAndSizes;
 | |
|         }
 | |
| 
 | |
|         function returnToPositions(nodesData) {
 | |
|             var currentPositions = {};
 | |
|             cy.nodes().positions(function (i, ele) {
 | |
|                 currentPositions[ele.id()] = {
 | |
|                     x: ele.position("x"),
 | |
|                     y: ele.position("y")
 | |
|                 };
 | |
|                 var data = nodesData[ele.id()];
 | |
|                 return {
 | |
|                     x: data.x,
 | |
|                     y: data.y
 | |
|                 };
 | |
|             });
 | |
| 
 | |
|             return currentPositions
 | |
|         }
 | |
| 
 | |
|         var ur = cy.undoRedo();
 | |
| 
 | |
|         ur.action("align", function (args) {
 | |
| 
 | |
|             var nodesData;
 | |
|             if (args.firstTime){
 | |
|                 nodesData = getNodePositions();
 | |
|                 args.nodes.align(args.horizontal, args.vertical, args.alignTo);
 | |
|             }
 | |
|             else
 | |
|                 nodesData = returnToPositions(args);
 | |
| 
 | |
|             return nodesData;
 | |
| 
 | |
|         }, function (nodesData) {
 | |
|             return returnToPositions(nodesData);
 | |
|         });
 | |
| 
 | |
|     }
 | |
| 
 | |
| 
 | |
| 
 | |
| };
 | |
| },{}],2:[function(require,module,exports){
 | |
| 
 | |
| var debounce = (function(){
 | |
|     /**
 | |
|      * lodash 3.1.1 (Custom Build) <https://lodash.com/>
 | |
|      * Build: `lodash modern modularize exports="npm" -o ./`
 | |
|      * Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/>
 | |
|      * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
 | |
|      * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
 | |
|      * Available under MIT license <https://lodash.com/license>
 | |
|      */
 | |
|     /** Used as the `TypeError` message for "Functions" methods. */
 | |
|     var FUNC_ERROR_TEXT = 'Expected a function';
 | |
| 
 | |
|     /* Native method references for those with the same name as other `lodash` methods. */
 | |
|     var nativeMax = Math.max,
 | |
|         nativeNow = Date.now;
 | |
| 
 | |
|     /**
 | |
|      * Gets the number of milliseconds that have elapsed since the Unix epoch
 | |
|      * (1 January 1970 00:00:00 UTC).
 | |
|      *
 | |
|      * @static
 | |
|      * @memberOf _
 | |
|      * @category Date
 | |
|      * @example
 | |
|      *
 | |
|      * _.defer(function(stamp) {
 | |
|      *   console.log(_.now() - stamp);
 | |
|      * }, _.now());
 | |
|      * // => logs the number of milliseconds it took for the deferred function to be invoked
 | |
|      */
 | |
|     var now = nativeNow || function() {
 | |
|             return new Date().getTime();
 | |
|         };
 | |
| 
 | |
|     /**
 | |
|      * Creates a debounced function that delays invoking `func` until after `wait`
 | |
|      * milliseconds have elapsed since the last time the debounced function was
 | |
|      * invoked. The debounced function comes with a `cancel` method to cancel
 | |
|      * delayed invocations. Provide an options object to indicate that `func`
 | |
|      * should be invoked on the leading and/or trailing edge of the `wait` timeout.
 | |
|      * Subsequent calls to the debounced function return the result of the last
 | |
|      * `func` invocation.
 | |
|      *
 | |
|      * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
 | |
|      * on the trailing edge of the timeout only if the the debounced function is
 | |
|      * invoked more than once during the `wait` timeout.
 | |
|      *
 | |
|      * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
 | |
|      * for details over the differences between `_.debounce` and `_.throttle`.
 | |
|      *
 | |
|      * @static
 | |
|      * @memberOf _
 | |
|      * @category Function
 | |
|      * @param {Function} func The function to debounce.
 | |
|      * @param {number} [wait=0] The number of milliseconds to delay.
 | |
|      * @param {Object} [options] The options object.
 | |
|      * @param {boolean} [options.leading=false] Specify invoking on the leading
 | |
|      *  edge of the timeout.
 | |
|      * @param {number} [options.maxWait] The maximum time `func` is allowed to be
 | |
|      *  delayed before it's invoked.
 | |
|      * @param {boolean} [options.trailing=true] Specify invoking on the trailing
 | |
|      *  edge of the timeout.
 | |
|      * @returns {Function} Returns the new debounced function.
 | |
|      * @example
 | |
|      *
 | |
|      * // avoid costly calculations while the window size is in flux
 | |
|      * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
 | |
|      *
 | |
|      * // invoke `sendMail` when the click event is fired, debouncing subsequent calls
 | |
|      * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
 | |
|      *   'leading': true,
 | |
|      *   'trailing': false
 | |
|      * }));
 | |
|      *
 | |
|      * // ensure `batchLog` is invoked once after 1 second of debounced calls
 | |
|      * var source = new EventSource('/stream');
 | |
|      * jQuery(source).on('message', _.debounce(batchLog, 250, {
 | |
|      *   'maxWait': 1000
 | |
|      * }));
 | |
|      *
 | |
|      * // cancel a debounced call
 | |
|      * var todoChanges = _.debounce(batchLog, 1000);
 | |
|      * Object.observe(models.todo, todoChanges);
 | |
|      *
 | |
|      * Object.observe(models, function(changes) {
 | |
|      *   if (_.find(changes, { 'user': 'todo', 'type': 'delete'})) {
 | |
|      *     todoChanges.cancel();
 | |
|      *   }
 | |
|      * }, ['delete']);
 | |
|      *
 | |
|      * // ...at some point `models.todo` is changed
 | |
|      * models.todo.completed = true;
 | |
|      *
 | |
|      * // ...before 1 second has passed `models.todo` is deleted
 | |
|      * // which cancels the debounced `todoChanges` call
 | |
|      * delete models.todo;
 | |
|      */
 | |
|     function debounce(func, wait, options) {
 | |
|         var args,
 | |
|             maxTimeoutId,
 | |
|             result,
 | |
|             stamp,
 | |
|             thisArg,
 | |
|             timeoutId,
 | |
|             trailingCall,
 | |
|             lastCalled = 0,
 | |
|             maxWait = false,
 | |
|             trailing = true;
 | |
| 
 | |
|         if (typeof func != 'function') {
 | |
|             throw new TypeError(FUNC_ERROR_TEXT);
 | |
|         }
 | |
|         wait = wait < 0 ? 0 : (+wait || 0);
 | |
|         if (options === true) {
 | |
|             var leading = true;
 | |
|             trailing = false;
 | |
|         } else if (isObject(options)) {
 | |
|             leading = !!options.leading;
 | |
|             maxWait = 'maxWait' in options && nativeMax(+options.maxWait || 0, wait);
 | |
|             trailing = 'trailing' in options ? !!options.trailing : trailing;
 | |
|         }
 | |
| 
 | |
|         function cancel() {
 | |
|             if (timeoutId) {
 | |
|                 clearTimeout(timeoutId);
 | |
|             }
 | |
|             if (maxTimeoutId) {
 | |
|                 clearTimeout(maxTimeoutId);
 | |
|             }
 | |
|             lastCalled = 0;
 | |
|             maxTimeoutId = timeoutId = trailingCall = undefined;
 | |
|         }
 | |
| 
 | |
|         function complete(isCalled, id) {
 | |
|             if (id) {
 | |
|                 clearTimeout(id);
 | |
|             }
 | |
|             maxTimeoutId = timeoutId = trailingCall = undefined;
 | |
|             if (isCalled) {
 | |
|                 lastCalled = now();
 | |
|                 result = func.apply(thisArg, args);
 | |
|                 if (!timeoutId && !maxTimeoutId) {
 | |
|                     args = thisArg = undefined;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         function delayed() {
 | |
|             var remaining = wait - (now() - stamp);
 | |
|             if (remaining <= 0 || remaining > wait) {
 | |
|                 complete(trailingCall, maxTimeoutId);
 | |
|             } else {
 | |
|                 timeoutId = setTimeout(delayed, remaining);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         function maxDelayed() {
 | |
|             complete(trailing, timeoutId);
 | |
|         }
 | |
| 
 | |
|         function debounced() {
 | |
|             args = arguments;
 | |
|             stamp = now();
 | |
|             thisArg = this;
 | |
|             trailingCall = trailing && (timeoutId || !leading);
 | |
| 
 | |
|             if (maxWait === false) {
 | |
|                 var leadingCall = leading && !timeoutId;
 | |
|             } else {
 | |
|                 if (!maxTimeoutId && !leading) {
 | |
|                     lastCalled = stamp;
 | |
|                 }
 | |
|                 var remaining = maxWait - (stamp - lastCalled),
 | |
|                     isCalled = remaining <= 0 || remaining > maxWait;
 | |
| 
 | |
|                 if (isCalled) {
 | |
|                     if (maxTimeoutId) {
 | |
|                         maxTimeoutId = clearTimeout(maxTimeoutId);
 | |
|                     }
 | |
|                     lastCalled = stamp;
 | |
|                     result = func.apply(thisArg, args);
 | |
|                 }
 | |
|                 else if (!maxTimeoutId) {
 | |
|                     maxTimeoutId = setTimeout(maxDelayed, remaining);
 | |
|                 }
 | |
|             }
 | |
|             if (isCalled && timeoutId) {
 | |
|                 timeoutId = clearTimeout(timeoutId);
 | |
|             }
 | |
|             else if (!timeoutId && wait !== maxWait) {
 | |
|                 timeoutId = setTimeout(delayed, wait);
 | |
|             }
 | |
|             if (leadingCall) {
 | |
|                 isCalled = true;
 | |
|                 result = func.apply(thisArg, args);
 | |
|             }
 | |
|             if (isCalled && !timeoutId && !maxTimeoutId) {
 | |
|                 args = thisArg = undefined;
 | |
|             }
 | |
|             return result;
 | |
|         }
 | |
|         debounced.cancel = cancel;
 | |
|         return debounced;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`.
 | |
|      * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
 | |
|      *
 | |
|      * @static
 | |
|      * @memberOf _
 | |
|      * @category Lang
 | |
|      * @param {*} value The value to check.
 | |
|      * @returns {boolean} Returns `true` if `value` is an object, else `false`.
 | |
|      * @example
 | |
|      *
 | |
|      * _.isObject({});
 | |
|      * // => true
 | |
|      *
 | |
|      * _.isObject([1, 2, 3]);
 | |
|      * // => true
 | |
|      *
 | |
|      * _.isObject(1);
 | |
|      * // => false
 | |
|      */
 | |
|     function isObject(value) {
 | |
|         // Avoid a V8 JIT bug in Chrome 19-20.
 | |
|         // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
 | |
|         var type = typeof value;
 | |
|         return !!value && (type == 'object' || type == 'function');
 | |
|     }
 | |
| 
 | |
|     return debounce;
 | |
| 
 | |
| })();
 | |
| 
 | |
| module.exports = debounce;
 | |
| },{}],3:[function(require,module,exports){
 | |
| module.exports = function (cy, snap) {
 | |
| 
 | |
|     var discreteDrag = {};
 | |
| 
 | |
|     var attachedNode;
 | |
|     var draggedNodes;
 | |
| 
 | |
|     var startPos;
 | |
|     var endPos;
 | |
| 
 | |
| 
 | |
|     discreteDrag.onTapStartNode = function (e) {
 | |
|         if (e.cyTarget.selected())
 | |
|             draggedNodes = e.cy.$(":selected");
 | |
|         else
 | |
|             draggedNodes = e.cyTarget;
 | |
| 
 | |
|         startPos = e.cyPosition;
 | |
| 
 | |
|         attachedNode = e.cyTarget;
 | |
|         attachedNode.lock();
 | |
|         attachedNode.trigger("grab");
 | |
|         cy.on("tapdrag", onTapDrag);
 | |
|         cy.on("tapend", onTapEndNode);
 | |
| 
 | |
|     };
 | |
| 
 | |
|     var onTapEndNode = function (e) {
 | |
|         //attachedNode.trigger("free");
 | |
|         cy.off("tapdrag", onTapDrag);
 | |
|         cy.off("tapend", onTapEndNode);
 | |
|         attachedNode.unlock();
 | |
|         e.preventDefault();
 | |
|     };
 | |
| 
 | |
|     var getDist = function () {
 | |
|         return {
 | |
|             x: endPos.x - startPos.x,
 | |
|             y: endPos.y - startPos.y
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     function getTopMostNodes(nodes) {
 | |
|         var nodesMap = {};
 | |
| 
 | |
|         for (var i = 0; i < nodes.length; i++) {
 | |
|             nodesMap[nodes[i].id()] = true;
 | |
|         }
 | |
| 
 | |
|         var roots = nodes.filter(function (i, ele) {
 | |
|             var parent = ele.parent()[0];
 | |
|             while (parent != null) {
 | |
|                 if (nodesMap[parent.id()]) {
 | |
|                     return false;
 | |
|                 }
 | |
|                 parent = parent.parent()[0];
 | |
|             }
 | |
|             return true;
 | |
|         });
 | |
| 
 | |
|         return roots;
 | |
|     }
 | |
| 
 | |
|     var moveNodesTopDown = function (nodes, dx, dy) {
 | |
| 
 | |
| /*
 | |
|         console.log(nodes.map(function (e) {
 | |
|             return e.id();
 | |
|         }));
 | |
|         for (var i = 0; i < nodes.length; i++) {
 | |
|             var node = nodes[i];
 | |
|             var pos = node.position();
 | |
| 
 | |
|             if (!node.isParent()) {
 | |
|                 node.position({
 | |
|                     x: pos.x + dx,
 | |
|                     y: pos.y + dy
 | |
|                 });
 | |
|                 console.log(node.id() + " " + dx + " " + dy);
 | |
|             }
 | |
| 
 | |
|             moveNodesTopDown(nodes.children(), dx, dy);
 | |
|         }
 | |
| */
 | |
|     };
 | |
| 
 | |
|     var onTapDrag = function (e) {
 | |
| 
 | |
|         var nodePos = attachedNode.position();
 | |
|         endPos = e.cyPosition;
 | |
|         endPos = snap.snapPos(endPos);
 | |
|         var dist = getDist();
 | |
|         if (dist.x != 0 || dist.y != 0) {
 | |
|             attachedNode.unlock();
 | |
|             //var topMostNodes = getTopMostNodes(draggedNodes);
 | |
|             var nodes = draggedNodes.union(draggedNodes.descendants());
 | |
| 
 | |
|             nodes.positions(function (i, node) {
 | |
|                 var pos = node.position();
 | |
|                 return snap.snapPos({
 | |
|                     x: pos.x + dist.x,
 | |
|                     y: pos.y + dist.y
 | |
|                 });
 | |
|             });
 | |
| 
 | |
|             startPos = endPos;
 | |
|             attachedNode.lock();
 | |
|             attachedNode.trigger("drag");
 | |
|         }
 | |
| 
 | |
|     };
 | |
| 
 | |
|     return discreteDrag;
 | |
| 
 | |
| 
 | |
| };
 | |
| },{}],4:[function(require,module,exports){
 | |
| module.exports = function (opts, cy, $, debounce) {
 | |
| 
 | |
|     var options = opts;
 | |
| 
 | |
|     var changeOptions = function (opts) {
 | |
|       options = opts;
 | |
|     };
 | |
| 
 | |
| 
 | |
|     var $canvas = $( '<canvas></canvas>' );
 | |
|     var $container = $( cy.container() );
 | |
|     var ctx = $canvas[ 0 ].getContext( '2d' );
 | |
|     $container.append( $canvas );
 | |
| 
 | |
|     var drawGrid = function() {
 | |
|         clearDrawing();
 | |
| 
 | |
|         var zoom = cy.zoom();
 | |
|         var canvasWidth = $container.width();
 | |
|         var canvasHeight = $container.height();
 | |
|         var increment = options.gridSpacing*zoom;
 | |
|         var pan = cy.pan();
 | |
|         var initialValueX = pan.x%increment;
 | |
|         var initialValueY = pan.y%increment;
 | |
| 
 | |
|         ctx.strokeStyle = options.strokeStyle;
 | |
|         ctx.lineWidth = options.lineWidth;
 | |
| 
 | |
|         if(options.zoomDash) {
 | |
|             var zoomedDash = options.lineDash.slice();
 | |
| 
 | |
|             for(var i = 0; i < zoomedDash.length; i++) {
 | |
|                 zoomedDash[ i ] = options.lineDash[ i ]*zoom;
 | |
|             }
 | |
|             ctx.setLineDash( zoomedDash );
 | |
|         } else {
 | |
|             ctx.setLineDash( options.lineDash );
 | |
|         }
 | |
| 
 | |
|         if(options.panGrid) {
 | |
|             ctx.lineDashOffset = -pan.y;
 | |
|         } else {
 | |
|             ctx.lineDashOffset = 0;
 | |
|         }
 | |
| 
 | |
|         for(var i = initialValueX; i < canvasWidth; i += increment) {
 | |
|             ctx.beginPath();
 | |
|             ctx.moveTo( i, 0 );
 | |
|             ctx.lineTo( i, canvasHeight );
 | |
|             ctx.stroke();
 | |
|         }
 | |
| 
 | |
|         if(options.panGrid) {
 | |
|             ctx.lineDashOffset = -pan.x;
 | |
|         } else {
 | |
|             ctx.lineDashOffset = 0;
 | |
|         }
 | |
| 
 | |
|         for(var i = initialValueY; i < canvasHeight; i += increment) {
 | |
|             ctx.beginPath();
 | |
|             ctx.moveTo( 0, i );
 | |
|             ctx.lineTo( canvasWidth, i );
 | |
|             ctx.stroke();
 | |
|         }
 | |
|     };
 | |
|     var clearDrawing = function() {
 | |
|         var width = $container.width();
 | |
|         var height = $container.height();
 | |
| 
 | |
|         ctx.clearRect( 0, 0, width, height );
 | |
|     };
 | |
| 
 | |
|     var resizeCanvas = debounce(function() {
 | |
|             $canvas
 | |
|                 .attr( 'height', $container.height() )
 | |
|                 .attr( 'width', $container.width() )
 | |
|                 .css( {
 | |
|                     'position': 'absolute',
 | |
|                     'top': 0,
 | |
|                     'left': 0,
 | |
|                     'z-index': options.gridStackOrder
 | |
|                 } );
 | |
| 
 | |
|             setTimeout( function() {
 | |
|                 var canvasBb = $canvas.offset();
 | |
|                 var containerBb = $container.offset();
 | |
| 
 | |
|                 $canvas
 | |
|                     .attr( 'height', $container.height() )
 | |
|                     .attr( 'width', $container.width() )
 | |
|                     .css( {
 | |
|                         'top': -( canvasBb.top - containerBb.top ),
 | |
|                         'left': -( canvasBb.left - containerBb.left )
 | |
|                     } );
 | |
|                 drawGrid();
 | |
|             }, 0 );
 | |
| 
 | |
|     }, 250);
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
|     return {
 | |
|         initCanvas: resizeCanvas,
 | |
|         resizeCanvas: resizeCanvas,
 | |
|         clearCanvas: clearDrawing,
 | |
|         drawGrid: drawGrid,
 | |
|         changeOptions: changeOptions,
 | |
|         sizeCanvas: drawGrid
 | |
|     };
 | |
| };
 | |
| },{}],5:[function(require,module,exports){
 | |
| module.exports = function (cy, snap, resize, discreteDrag, drawGrid, guidelines, parentPadding, $) {
 | |
| 
 | |
|     var feature = function (func) {
 | |
|         return function (enable) {
 | |
|             func(enable);
 | |
|         };
 | |
|     };
 | |
| 
 | |
|     var controller = {
 | |
|         discreteDrag: new feature(setDiscreteDrag),
 | |
|         resize: new feature(setResize),
 | |
|         snapToGrid: new feature(setSnapToGrid),
 | |
|         drawGrid: new feature(setDrawGrid),
 | |
|         guidelines: new feature(setGuidelines),
 | |
|         parentPadding: new feature(setParentPadding)
 | |
|     };
 | |
| 
 | |
|     function applyToCyTarget(func, allowParent) {
 | |
|         return function (e) {
 | |
|             if (!e.cyTarget.is(":parent") || allowParent)
 | |
|                 func(e.cyTarget);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     function applyToActiveNodes(func, allowParent) {
 | |
|         return function (e) {
 | |
|             if (!e.cyTarget.is(":parent") || allowParent)
 | |
|                 if (e.cyTarget.selected())
 | |
|                     func(e.cyTarget, e.cy.$(":selected"));
 | |
|                 else
 | |
|                     func(e.cyTarget, e.cyTarget);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     function applyToAllNodesButNoParent(func) {
 | |
|         return function () {
 | |
|             cy.nodes().not(":parent").each(function (i, ele) {
 | |
|                 func(ele);
 | |
|             });
 | |
|         };
 | |
|     }
 | |
|     function applyToAllNodes(func) {
 | |
|         return function () {
 | |
|             cy.nodes().each(function (i, ele) {
 | |
|                 func(ele);
 | |
|             });
 | |
|         };
 | |
|     }
 | |
| 
 | |
|     function eventStatus(enable) {
 | |
|         return enable ? "on" : "off";
 | |
|     }
 | |
| 
 | |
| 
 | |
|     // Discrete Drag
 | |
|     function setDiscreteDrag(enable) {
 | |
|         cy[eventStatus(enable)]("tapstart", "node", discreteDrag.onTapStartNode);
 | |
|     }
 | |
| 
 | |
|     // Resize
 | |
|     var resizeAllNodes = applyToAllNodesButNoParent(resize.resizeNode);
 | |
|     var resizeNode = applyToCyTarget(resize.resizeNode);
 | |
|     var recoverAllNodeDimensions = applyToAllNodesButNoParent(resize.recoverNodeDimensions);
 | |
| 
 | |
|     function setResize(enable) {
 | |
|         cy[eventStatus(enable)]("ready", resizeAllNodes);
 | |
|       //  cy[eventStatus(enable)]("style", "node", resizeNode);
 | |
|         enable ? resizeAllNodes() : recoverAllNodeDimensions();
 | |
|     }
 | |
| 
 | |
|     // Snap To Grid
 | |
|     var snapAllNodes = applyToAllNodes(snap.snapNodesTopDown);
 | |
|     var recoverSnapAllNodes = applyToAllNodes(snap.recoverSnapNode);
 | |
|     var snapCyTarget = applyToCyTarget(snap.snapNode, true);
 | |
| 
 | |
|     function setSnapToGrid(enable) {
 | |
|         cy[eventStatus(enable)]("add", "node", snapCyTarget);
 | |
|         cy[eventStatus(enable)]("ready", snapAllNodes);
 | |
| 
 | |
|         cy[eventStatus(enable)]("free", "node", snap.onFreeNode);
 | |
| 
 | |
|         if (enable) {
 | |
|             snapAllNodes();
 | |
|         } else {
 | |
|             recoverSnapAllNodes();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Draw Grid
 | |
|     var drawGridOnZoom = function () {
 | |
|         if (currentOptions.zoomDash) drawGrid.drawGrid()
 | |
|     };
 | |
|     var drawGridOnPan = function () {
 | |
|         if (currentOptions.panGrid) drawGrid.drawGrid()
 | |
|     };
 | |
| 
 | |
|     function setDrawGrid(enable) {
 | |
|         cy[eventStatus(enable)]('zoom', drawGridOnZoom);
 | |
|         cy[eventStatus(enable)]('pan', drawGridOnPan);
 | |
|         cy[eventStatus(enable)]('ready', drawGrid.resizeCanvas);
 | |
| 
 | |
|         if (enable) {
 | |
|             drawGrid.initCanvas();
 | |
|             $(window).on('resize', drawGrid.resizeCanvas);
 | |
|         } else {
 | |
|             drawGrid.clearCanvas();
 | |
|             $(window).off('resize', drawGrid.resizeCanvas);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Guidelines
 | |
| 
 | |
|     function setGuidelines(enable) {
 | |
|         cy[eventStatus(enable)]('zoom', guidelines.onZoom);
 | |
|         cy[eventStatus(enable)]('drag', "node", guidelines.onDragNode);
 | |
|         cy[eventStatus(enable)]('grab', "node", guidelines.onGrabNode);
 | |
|         cy[eventStatus(enable)]('free', "node", guidelines.onFreeNode);
 | |
| 
 | |
|     }
 | |
| 
 | |
|     // Parent Padding
 | |
|     var setAllParentPaddings = function (enable) {
 | |
|         parentPadding.setPaddingOfParent(cy.nodes(":parent"), enable);
 | |
|     };
 | |
|     var enableParentPadding = function (node) {
 | |
|         parentPadding.setPaddingOfParent(node, true);
 | |
|     };
 | |
| 
 | |
| 
 | |
|     function setParentPadding(enable) {
 | |
| 
 | |
|         setAllParentPaddings(enable);
 | |
| 
 | |
|         cy[eventStatus(enable)]('ready', setAllParentPaddings);
 | |
|         cy[eventStatus(enable)]("add", "node:parent", applyToCyTarget(enableParentPadding, true));
 | |
|     }
 | |
| 
 | |
|     // Sync with options: Enables/disables changed via options.
 | |
|     var latestOptions = {};
 | |
|     var currentOptions;
 | |
| 
 | |
|     var specialOpts = {
 | |
|         drawGrid: ["gridSpacing", "zoomDash", "panGrid", "gridStackOrder", "strokeStyle", "lineWidth", "lineDash"],
 | |
|         guidelines: ["gridSpacing", "guidelinesStackOrder", "guidelinesTolerance", "guidelinesStyle"],
 | |
|         resize: ["gridSpacing"],
 | |
|         parentPadding: ["gridSpacing", "parentSpacing"],
 | |
|         snapToGrid: ["gridSpacing"]
 | |
|     };
 | |
| 
 | |
|     function syncWithOptions(options) {
 | |
|         currentOptions = $.extend(true, {}, options);
 | |
|         for (var key in options)
 | |
|             if (latestOptions[key] != options[key])
 | |
|                 if (controller.hasOwnProperty(key)) {
 | |
|                     controller[key](options[key]);
 | |
|                 } else {
 | |
|                     for (var optsKey in specialOpts) {
 | |
|                         var opts = specialOpts[optsKey];
 | |
|                         if (opts.indexOf(key) >= 0) {
 | |
|                             if(optsKey == "drawGrid") {
 | |
|                                 drawGrid.changeOptions(options);
 | |
|                                 if (options.drawGrid)
 | |
|                                     drawGrid.resizeCanvas();
 | |
|                             }
 | |
| 
 | |
|                             if (optsKey == "snapToGrid"){
 | |
|                                 snap.changeOptions(options);
 | |
|                                 if (options.snapToGrid)
 | |
|                                     snapAllNodes();
 | |
|                             }
 | |
| 
 | |
|                             if(optsKey == "guidelines")
 | |
|                                 guidelines.changeOptions(options);
 | |
| 
 | |
|                             if (optsKey == "resize") {
 | |
|                                 resize.changeOptions(options);
 | |
|                                 if (options.resize)
 | |
|                                     resizeAllNodes();
 | |
|                             }
 | |
| 
 | |
|                             if (optsKey == "parentPadding")
 | |
|                                 parentPadding.changeOptions(options);
 | |
| 
 | |
|                                 
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|         latestOptions = $.extend(true, latestOptions, options);
 | |
|     }
 | |
| 
 | |
|     return {
 | |
|         init: syncWithOptions,
 | |
|         syncWithOptions: syncWithOptions
 | |
|     };
 | |
| 
 | |
| };
 | |
| },{}],6:[function(require,module,exports){
 | |
| module.exports = function (opts, cy, $, debounce) {
 | |
| 
 | |
|     var options = opts;
 | |
| 
 | |
|     var changeOptions = function (opts) {
 | |
|         options = opts;
 | |
|     };
 | |
| 
 | |
|     function calcDistance(p1, p2) {
 | |
|         return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
 | |
|     }
 | |
|     
 | |
|     function getExtraDim(node, paddingDim) {
 | |
| 
 | |
|     }
 | |
| 
 | |
|     var dims = function (node) {
 | |
| 
 | |
|         var pos = node.renderedPosition();
 | |
|         var width = node.renderedWidth();
 | |
|         var height = node.renderedHeight();
 | |
|         var padding = {
 | |
|             left: Number(node.renderedStyle("padding-left").replace("px", "")),
 | |
|             right: Number(node.renderedStyle("padding-right").replace("px", "")),
 | |
|             top: Number(node.renderedStyle("padding-top").replace("px", "")),
 | |
|             bottom: Number(node.renderedStyle("padding-bottom").replace("px", ""))
 | |
|         };
 | |
| 
 | |
|         this.horizontal = {
 | |
|             center: pos.x,
 | |
|             left: pos.x - (padding.left + width / 2),
 | |
|             right: pos.x + (padding.right + width / 2)
 | |
|         };
 | |
| 
 | |
|         this.vertical = {
 | |
|             center: pos.y,
 | |
|             top: pos.y - (padding.top + height / 2),
 | |
|             bottom: pos.y + (padding.bottom + height / 2)
 | |
|         };
 | |
| 
 | |
|         return this;
 | |
|     };
 | |
| 
 | |
|     var $canvas = $('<canvas></canvas>');
 | |
|     var $container = $(cy.container());
 | |
|     var ctx = $canvas[0].getContext('2d');
 | |
|     $container.append($canvas);
 | |
| 
 | |
|     $canvas
 | |
|         .attr('height', $container.height())
 | |
|         .attr('width', $container.width())
 | |
|         .css({
 | |
|             'position': 'absolute',
 | |
|             'top': 0,
 | |
|             'left': 0,
 | |
|             'z-index': options.guidelinesStackOrder
 | |
|         });
 | |
| 
 | |
|     var canvasBb = $canvas.offset();
 | |
|     var containerBb = $container.offset();
 | |
| 
 | |
|     $canvas
 | |
|         .attr( 'height', $container.height() )
 | |
|         .attr( 'width', $container.width() )
 | |
|         .css( {
 | |
|             'top': -( canvasBb.top - containerBb.top ),
 | |
|             'left': -( canvasBb.left - containerBb.left )
 | |
|         } );
 | |
|     var clearDrawing = function () {
 | |
|         var width = $container.width();
 | |
|         var height = $container.height();
 | |
| 
 | |
|         ctx.clearRect(0, 0, width, height);
 | |
|     };
 | |
| 
 | |
| 
 | |
|     var pickedNode;
 | |
| 
 | |
|     function onGrabNode(e) {
 | |
|         pickedNode = e.cyTarget;
 | |
|         onDragNode(e);
 | |
|     }
 | |
| 
 | |
|     var onDragNode = debounce(function(e) {
 | |
|         if (pickedNode) {
 | |
|             var node = pickedNode;
 | |
| 
 | |
|             var mainDims = new dims(node);
 | |
| 
 | |
|             var cy = e.cy;
 | |
|             var nearests = {
 | |
|                 horizontal: {
 | |
|                     distance: Number.MAX_VALUE
 | |
|                 },
 | |
|                 vertical: {
 | |
|                     distance: Number.MAX_VALUE
 | |
|                 }
 | |
|             };
 | |
| 
 | |
|             cy.nodes(":visible").not(node.ancestors()).not(node.descendants()).not(node).each(function (i, ele) {
 | |
|                 var nodeDims = new dims(ele);
 | |
| 
 | |
| 
 | |
|                 for (var dim in mainDims) {
 | |
|                     var mainDim = mainDims[dim];
 | |
|                     var nodeDim = nodeDims[dim];
 | |
|                     var otherDim = dim == "horizontal" ? "y" : "x";
 | |
|                     var eitherDim = otherDim == "x" ? "y" : "x";
 | |
|                     for (var key in mainDim) {
 | |
|                         for (var key2 in nodeDim) {
 | |
|                             if (Math.abs(mainDim[key] - nodeDim[key2]) < options.guidelinesTolerance) {
 | |
|                                 var distance = calcDistance(node.renderedPosition(), ele.renderedPosition());
 | |
|                                 if (nearests[dim].distance > distance) {
 | |
| 
 | |
|                                     nearests[dim] = {
 | |
|                                         to: ele.id(),
 | |
|                                         toPos: {},
 | |
|                                         from: node.id(),
 | |
|                                         fromPos: {},
 | |
|                                         distance: distance
 | |
|                                     };
 | |
|                                     nearests[dim].fromPos[eitherDim] = mainDim[key];
 | |
|                                     nearests[dim].fromPos[otherDim] = node.renderedPosition(otherDim);
 | |
|                                     nearests[dim].toPos[eitherDim] = nodeDim[key2];
 | |
|                                     nearests[dim].toPos[otherDim] = ele.renderedPosition(otherDim);
 | |
|                                 }
 | |
|                             }
 | |
|                             // console.log(key + " of " + node.id() + " -> " + key2 + " of " + ele.id())
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             });
 | |
| 
 | |
|             clearDrawing();
 | |
|             for (var key in nearests) {
 | |
|                 var item = nearests[key];
 | |
|                 if (item.from) {
 | |
|                     ctx.beginPath();
 | |
|                     ctx.moveTo(item.fromPos.x, item.fromPos.y);
 | |
|                     ctx.lineTo(item.toPos.x, item.toPos.y);
 | |
| 
 | |
|                     ctx.setLineDash(options.guidelinesStyle.lineDash);
 | |
|                     for (var styleKey in options.guidelinesStyle)
 | |
|                         ctx[styleKey] = options.guidelinesStyle[styleKey];
 | |
| 
 | |
|                     ctx.stroke();
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|         }
 | |
|     }, 0, true);
 | |
| 
 | |
|     function onFreeNode() {
 | |
|         pickedNode = undefined;
 | |
|         clearDrawing();
 | |
|     }
 | |
| 
 | |
|     return {
 | |
|         onDragNode: onDragNode,
 | |
|         onZoom: onDragNode,
 | |
|         onGrabNode: onGrabNode,
 | |
|         onFreeNode: onFreeNode,
 | |
|         changeOptions: changeOptions
 | |
|     }
 | |
| 
 | |
| };
 | |
| },{}],7:[function(require,module,exports){
 | |
| ;(function(){ 'use strict';
 | |
| 
 | |
|     // registers the extension on a cytoscape lib ref
 | |
|     var register = function( cytoscape ){
 | |
| 
 | |
|         if( !cytoscape ){ return; } // can't register if cytoscape unspecified
 | |
| 
 | |
| 
 | |
|         var options = {
 | |
|             // On/Off Modules
 | |
|             snapToGrid: true, // Snap to grid functionality
 | |
|             discreteDrag: true, // Discrete Drag
 | |
|             guidelines: true, // Guidelines on dragging nodes
 | |
|             resize: true, // Adjust node sizes to cell sizes
 | |
|             parentPadding: true, // Adjust parent sizes to cell sizes by padding
 | |
|             drawGrid: true, // Draw grid background
 | |
| 
 | |
|             // Other settings
 | |
| 
 | |
|             // General
 | |
|             gridSpacing: 20, // Distance between the lines of the grid.
 | |
| 
 | |
|             // Draw Grid
 | |
|             zoomDash: true, // Determines whether the size of the dashes should change when the drawing is zoomed in and out if grid is drawn.
 | |
|             panGrid: true, // Determines whether the grid should move then the user moves the graph if grid is drawn.
 | |
|             gridStackOrder: -1, // Namely z-index
 | |
|             strokeStyle: '#dedede', // Color of grid lines
 | |
|             lineWidth: 1.0, // Width of grid lines
 | |
|             lineDash: [2.5, 4], // Defines style of dash. Read: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash
 | |
| 
 | |
|             // Guidelines
 | |
|             guidelinesStackOrder: 4, // z-index of guidelines
 | |
|             guidelinesTolerance: 2.00, // Tolerance distance for rendered positions of nodes' interaction.
 | |
|             guidelinesStyle: { // Set ctx properties of line. Properties are here:
 | |
|                 strokeStyle: "#8b7d6b",
 | |
|                 lineDash: [3, 5]
 | |
|             },
 | |
| 
 | |
|             // Parent Padding
 | |
|             parentSpacing: -1 // -1 to set paddings of parents to gridSpacing
 | |
|         };
 | |
| 
 | |
|         var _snap = require("./snap");
 | |
|         var _discreteDrag = require("./discrete_drag");
 | |
|         var _drawGrid = require("./draw_grid");
 | |
|         var _resize = require("./resize");
 | |
|         var _eventsController = require("./events_controller");
 | |
|         var _guidelines = require("./guidelines");
 | |
|         var _parentPadding = require("./parentPadding");
 | |
|         var _alignment = require("./alignment");
 | |
|         var debounce = require("./debounce");
 | |
|         var snap, resize, discreteDrag, drawGrid, eventsController, guidelines, parentPadding, alignment;
 | |
| 
 | |
|         function getScratch() {
 | |
|             if (!cy.scratch("_gridGuide")) {
 | |
|                 cy.scratch("_gridGuide", { });
 | |
| 
 | |
|             }
 | |
|             return cy.scratch("_gridGuide");
 | |
|         }
 | |
| 
 | |
|         cytoscape( 'core', 'gridGuide', function(opts){
 | |
|             var cy = this;
 | |
|             $.extend(true, options, opts);
 | |
| 
 | |
|             if (!getScratch().initialized) {
 | |
|                 snap = _snap(options.gridSpacing);
 | |
|                 resize = _resize(options.gridSpacing);
 | |
|                 discreteDrag = _discreteDrag(cy, snap);
 | |
|                 drawGrid = _drawGrid(options, cy, $, debounce);
 | |
|                 guidelines = _guidelines(options, cy, $, debounce);
 | |
|                 parentPadding = _parentPadding(options, cy);
 | |
| 
 | |
|                 eventsController = _eventsController(cy, snap, resize, discreteDrag, drawGrid, guidelines, parentPadding, $);
 | |
| 
 | |
|                 alignment = _alignment(cytoscape, $);
 | |
| 
 | |
|                 eventsController.init(options);
 | |
|                 getScratch().initialized = true;
 | |
|             } else
 | |
|                 eventsController.syncWithOptions(options);
 | |
| 
 | |
| 
 | |
|             return this; // chainability
 | |
|         } ) ;
 | |
| 
 | |
| 
 | |
|     };
 | |
| 
 | |
|     if( typeof module !== 'undefined' && module.exports ){ // expose as a commonjs module
 | |
|         module.exports = register;
 | |
|     }
 | |
| 
 | |
|     if( typeof define !== 'undefined' && define.amd ){ // expose as an amd/requirejs module
 | |
|         define('cytoscape-grid-guide', function(){
 | |
|             return register;
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     if( typeof cytoscape !== 'undefined' ){ // expose to global cytoscape (i.e. window.cytoscape)
 | |
|         register( cytoscape );
 | |
|     }
 | |
| 
 | |
| })();
 | |
| 
 | |
| },{"./alignment":1,"./debounce":2,"./discrete_drag":3,"./draw_grid":4,"./events_controller":5,"./guidelines":6,"./parentPadding":8,"./resize":9,"./snap":10}],8:[function(require,module,exports){
 | |
| module.exports = function (opts, cy) {
 | |
| 
 | |
|     var options = opts;
 | |
|     var ppClass = "_gridParentPadding";
 | |
| 
 | |
|     function initPadding() {
 | |
|         var padding = options.parentSpacing < 0 ? options.gridSpacing : options.parentSpacing;
 | |
|         cy.style()
 | |
|             .selector('.' + ppClass)
 | |
|             .style("compound-sizing-wrt-labels", "exclude")
 | |
|             .style("padding-left", padding)
 | |
|             .style("padding-right", padding)
 | |
|             .style("padding-top", padding)
 | |
|             .style("padding-bottom", padding)
 | |
|             .update();
 | |
| 
 | |
|     }
 | |
| 
 | |
|     function changeOptions(opts) {
 | |
|         options = opts;
 | |
|         padding = options.parentSpacing < 0 ? options.gridSpacing : options.parentSpacing;
 | |
|         initPadding();
 | |
|     }
 | |
| 
 | |
|     function setPaddingOfParent(node, enable) {
 | |
|         if (enable)
 | |
|             node.addClass(ppClass);
 | |
|         else
 | |
|             node.removeClass(ppClass);
 | |
|     }
 | |
| 
 | |
|     return {
 | |
|         changeOptions: changeOptions,
 | |
|         setPaddingOfParent: setPaddingOfParent
 | |
|     };
 | |
| };
 | |
| },{}],9:[function(require,module,exports){
 | |
| module.exports = function (gridSpacing) {
 | |
| 
 | |
| 
 | |
|     var changeOptions = function (opts) {
 | |
|         gridSpacing = Number(opts.gridSpacing);
 | |
|     };
 | |
| 
 | |
|     var getScratch = function (node) {
 | |
|         if (!node.scratch("_gridGuide"))
 | |
|             node.scratch("_gridGuide", {});
 | |
| 
 | |
|         return node.scratch("_gridGuide");
 | |
|     };
 | |
| 
 | |
|     function resizeNode(node) {
 | |
|         var width = node.width();
 | |
|         var height = node.height();
 | |
| 
 | |
|         var newWidth = Math.round((width - gridSpacing) / (gridSpacing * 2)) * (gridSpacing * 2);
 | |
|         var newHeight = Math.round((height - gridSpacing) / (gridSpacing * 2)) * (gridSpacing * 2);
 | |
|         newWidth = newWidth > 0 ? newWidth + gridSpacing : gridSpacing;
 | |
|         newHeight = newHeight > 0 ? newHeight + gridSpacing : gridSpacing;
 | |
| 
 | |
|         if (width != newWidth || height != newHeight) {
 | |
|             node.style({
 | |
|                 "width": newWidth,
 | |
|                 "height": newHeight
 | |
|             });
 | |
|             getScratch(node).resize = {
 | |
|                 oldWidth: width,
 | |
|                 oldHeight: height
 | |
|             };
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     function recoverNodeDimensions(node) {
 | |
|         var oldSizes = getScratch(node).resize;
 | |
|         if (oldSizes) 
 | |
|             node.style({
 | |
|                 "width": oldSizes.oldWidth,
 | |
|                 "height": oldSizes.oldHeight
 | |
|             });
 | |
| 
 | |
| 
 | |
|     }
 | |
| 
 | |
| 
 | |
|     return {
 | |
|         resizeNode: resizeNode,
 | |
|         recoverNodeDimensions: recoverNodeDimensions,
 | |
|         changeOptions: changeOptions
 | |
|     };
 | |
| 
 | |
| };
 | |
| },{}],10:[function(require,module,exports){
 | |
| module.exports = function (gridSpacing) {
 | |
| 
 | |
|     var snap = { };
 | |
| 
 | |
|     snap.changeOptions = function (opts) {
 | |
|         gridSpacing = opts.gridSpacing;
 | |
|     };
 | |
| 
 | |
|     var getScratch = function (node) {
 | |
|         if (!node.scratch("_gridGuide"))
 | |
|             node.scratch("_gridGuide", {});
 | |
| 
 | |
|         return node.scratch("_gridGuide");
 | |
|     };
 | |
| 
 | |
| 
 | |
|     function getTopMostNodes(nodes) {
 | |
|         var nodesMap = {};
 | |
| 
 | |
|         for (var i = 0; i < nodes.length; i++) {
 | |
|             nodesMap[nodes[i].id()] = true;
 | |
|         }
 | |
| 
 | |
|         var roots = nodes.filter(function (i, ele) {
 | |
|             var parent = ele.parent()[0];
 | |
|             while(parent != null){
 | |
|                 if(nodesMap[parent.id()]){
 | |
|                     return false;
 | |
|                 }
 | |
|                 parent = parent.parent()[0];
 | |
|             }
 | |
|             return true;
 | |
|         });
 | |
| 
 | |
|         return roots;
 | |
|     }
 | |
| 
 | |
|     snap.snapPos = function (pos) {
 | |
|         var newPos = {
 | |
|             x: (Math.floor(pos.x / gridSpacing) + 0.5) * gridSpacing,
 | |
|             y: (Math.floor(pos.y / gridSpacing) + 0.5) * gridSpacing
 | |
|         };
 | |
| 
 | |
|         return newPos;
 | |
|     };
 | |
| 
 | |
|     snap.snapNode = function (node) {
 | |
| 
 | |
|         var pos = node.position();
 | |
|         var newPos = snap.snapPos(pos);
 | |
| 
 | |
|         node.position(newPos);
 | |
|     };
 | |
| 
 | |
|     function snapTopDown(nodes) {
 | |
| 
 | |
|         nodes.union(nodes.descendants()).positions(function (i, node) {
 | |
|             var pos = node.position();
 | |
|             return snap.snapPos(pos);
 | |
|         });
 | |
|         /*
 | |
|         for (var i = 0; i < nodes.length; i++) {
 | |
| 
 | |
|             if (!nodes[i].isParent())
 | |
|                 snap.snapNode(nodes[i]);
 | |
| 
 | |
|             snapTopDown(nodes.children());
 | |
|         }*/
 | |
| 
 | |
|     }
 | |
| 
 | |
|     snap.snapNodesTopDown = function (nodes) {
 | |
|         // getTOpMostNodes -> nodes
 | |
|         cy.startBatch();
 | |
|         nodes.union(nodes.descendants()).positions(function (i, node) {
 | |
|             var pos = node.position();
 | |
|             return snap.snapPos(pos);
 | |
|         });
 | |
|         cy.endBatch();
 | |
|     };
 | |
| 
 | |
|     snap.onFreeNode = function (e) {
 | |
|         var nodes;
 | |
|         if (e.cyTarget.selected())
 | |
|             nodes = e.cy.$(":selected");
 | |
|         else
 | |
|             nodes = e.cyTarget;
 | |
| 
 | |
|         snap.snapNodesTopDown(nodes);
 | |
| 
 | |
|     };
 | |
| 
 | |
| 
 | |
|     snap.recoverSnapNode = function (node) {
 | |
|         var snapScratch = getScratch(node).snap;
 | |
|         if (snapScratch) {
 | |
|             node.position(snapScratch.oldPos);
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     return snap;
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| };
 | |
| },{}]},{},[7]);
 | 
