azkaban-uncached
Changes
src/web/js/graph/azkaban.context.menu.js 97(+97 -0)
src/web/js/graph/azkaban.svg.graph.view.js 15(+13 -2)
src/web/js/graph/svgNavigate.js 378(+378 -0)
Details
src/web/js/graph/azkaban.context.menu.js 97(+97 -0)
diff --git a/src/web/js/graph/azkaban.context.menu.js b/src/web/js/graph/azkaban.context.menu.js
new file mode 100644
index 0000000..7bca798
--- /dev/null
+++ b/src/web/js/graph/azkaban.context.menu.js
@@ -0,0 +1,97 @@
+$.namespace('azkaban');
+
+azkaban.ContextMenuView = Backbone.View.extend({
+ events : {
+ },
+ initialize : function(settings) {
+ var div = this.el;
+ $('body').click(function(e) {
+ $(".contextMenu").remove();
+ });
+ $('body').bind("contextmenu", function(e) {$(".contextMenu").remove()});
+ },
+ show : function(evt, menu) {
+ console.log("Show context menu");
+ $(".contextMenu").remove();
+ var x = evt.pageX;
+ var y = evt.pageY;
+
+ var contextMenu = this.setupMenu(menu);
+ $(contextMenu).css({top: y, left: x});
+
+ $(this.el).after(contextMenu);
+ },
+ hide : function(evt) {
+ console.log("Hide context menu");
+ $(".contextMenu").remove();
+ },
+ handleClick: function(evt) {
+ console.log("handling click");
+ },
+ setupMenu: function(menu) {
+ var contextMenu = document.createElement("div");
+ $(contextMenu).addClass("contextMenu");
+ var ul = document.createElement("ul");
+ $(contextMenu).append(ul);
+
+ for (var i = 0; i < menu.length; ++i) {
+ var menuItem = document.createElement("li");
+ if (menu[i].break) {
+ $(menuItem).addClass("break");
+ }
+ else {
+ var title = menu[i].title;
+ var callback = menu[i].callback;
+ $(menuItem).addClass("menuitem");
+ $(menuItem).text(title);
+ menuItem.callback = callback;
+ $(menuItem).click(function() {
+ $(contextMenu).hide();
+ this.callback.call();});
+
+ if (menu[i].submenu) {
+ var expandSymbol = document.createElement("div");
+ $(expandSymbol).addClass("expandSymbol");
+ $(menuItem).append(expandSymbol);
+
+ var subMenu = this.setupMenu(menu[i].submenu);
+ $(subMenu).addClass("subMenu");
+ subMenu.parent = contextMenu;
+ menuItem.subMenu = subMenu;
+ $(subMenu).hide();
+ $(this.el).after(subMenu);
+
+ $(menuItem).mouseenter(function() {
+ $(".subMenu").hide();
+ var menuItem = this;
+ menuItem.selected = true;
+ setTimeout(function() {
+ if (menuItem.selected) {
+ var offset = $(menuItem).offset();
+ var left = offset.left;
+ var top = offset.top;
+ var width = $(menuItem).width();
+ var subMenu = menuItem.subMenu;
+
+ var newLeft = left + width - 5;
+ $(subMenu).css({left: newLeft, top: top});
+ $(subMenu).show();
+ }
+ }, 500);
+ });
+ $(menuItem).mouseleave(function() {this.selected = false;});
+ }
+ }
+
+ $(ul).append(menuItem);
+ }
+
+ return contextMenu;
+ }
+});
+
+var contextMenuView;
+$(function() {
+ contextMenuView = new azkaban.ContextMenuView({el:$('#contextMenu')});
+ contextMenuView.hide();
+});
\ No newline at end of file
src/web/js/graph/azkaban.svg.graph.view.js 15(+13 -2)
diff --git a/src/web/js/graph/azkaban.svg.graph.view.js b/src/web/js/graph/azkaban.svg.graph.view.js
index 09d5228..ed1bb04 100644
--- a/src/web/js/graph/azkaban.svg.graph.view.js
+++ b/src/web/js/graph/azkaban.svg.graph.view.js
@@ -17,7 +17,8 @@ function addClass(el, name) {
function removeClass(el, name) {
if (hasClass(el, name)) {
var classes = el.getAttribute("class");
- el.setAttribute("class", classes.replace(new RegExp('(\\s|^)'+name+'(\\s|$)'),' ').replace(/^\s+|\s+$/g, ''));
+ el.setAttribute("class", classes.replace(
+ new RegExp('(\\s|^)'+name+'(\\s|$)'),' ').replace(/^\s+|\s+$/g, ''));
}
}
@@ -28,6 +29,7 @@ azkaban.SvgGraphView = Backbone.View.extend({
"contextmenu g" : "handleRightClick",
"contextmenu polyline": "handleRightClick"
},
+
initialize: function(settings) {
this.model.bind('change:selected', this.changeSelected, this);
this.model.bind('centerNode', this.centerNode, this);
@@ -58,6 +60,7 @@ azkaban.SvgGraphView = Backbone.View.extend({
this.render();
}
},
+
initializeDefs: function(self) {
var def = document.createElementNS(svgns, 'defs');
def.setAttribute("id", "buttonDefs");
@@ -80,6 +83,7 @@ azkaban.SvgGraphView = Backbone.View.extend({
this.svgGraph.appendChild(def);
},
+
render: function(self) {
console.log("graph render");
@@ -133,7 +137,11 @@ azkaban.SvgGraphView = Backbone.View.extend({
this.drawEdge(this, edges[i]);
}
- this.model.set({"flowId":data.flowId, "nodes": this.nodes, "edges": edges});
+ this.model.set({
+ "flowId": data.flowId,
+ "nodes": this.nodes,
+ "edges": edges
+ });
var margin = this.graphMargin;
bounds.minX = bounds.minX ? bounds.minX - margin : -margin;
@@ -152,6 +160,7 @@ azkaban.SvgGraphView = Backbone.View.extend({
this.graphBounds = bounds;
this.resetPanZoom(0);
},
+
handleDisabledChange: function(evt) {
var disabledMap = this.model.get("disabled");
@@ -167,6 +176,7 @@ azkaban.SvgGraphView = Backbone.View.extend({
}
}
},
+
assignInitialStatus: function(evt) {
var data = this.model.get("data");
for (var i = 0; i < data.nodes.length; ++i) {
@@ -180,6 +190,7 @@ azkaban.SvgGraphView = Backbone.View.extend({
}
}
},
+
changeSelected: function(self) {
console.log("change selected");
var selected = this.model.get("selected");
src/web/js/graph/svgNavigate.js 378(+378 -0)
diff --git a/src/web/js/graph/svgNavigate.js b/src/web/js/graph/svgNavigate.js
new file mode 100644
index 0000000..d1f7892
--- /dev/null
+++ b/src/web/js/graph/svgNavigate.js
@@ -0,0 +1,378 @@
+(function($) {
+
+ var mouseUp = function(evt) {
+ if (evt.button > 1) {
+ return;
+ }
+ var target = evt.target;
+ target.mx = evt.clientX;
+ target.my = evt.clientY;
+ target.mDown = false;
+ }
+
+ var mouseDown = function(evt) {
+ if (evt.button > 1) {
+ return;
+ }
+ //alert("mouseDown");
+ var target = evt.target;
+ target.mx = evt.clientX;
+ target.my = evt.clientY;
+ target.mDown = true;
+ }
+
+ var mouseOut = function(evt) {
+ var target = evt.target;
+ target.mx = evt.clientX;
+ target.my = evt.clientY;
+ target.mDown = false;
+ }
+
+ var mouseMove = function(evt) {
+ var target = evt.target;
+ if (target.mDown) {
+ var dx = evt.clientX - target.mx;
+ var dy = evt.clientY - target.my;
+
+ evt.dragX = dx;
+ evt.dragY = dy;
+ mouseDrag(evt);
+ }
+
+ target.mx = evt.clientX;
+ target.my = evt.clientY;
+ }
+
+ var mouseDrag = function(evt) {
+ //alert("mouseDragged ");
+ translateDeltaGraph(evt.target, evt.dragX, evt.dragY);
+ }
+
+ var mouseScrolled = function(evt) {
+ //alert("scroll");
+ if (!evt) {
+ evt = window.event;
+ }
+ var target = evt.target;
+
+
+ var leftOffset = 0;
+ var topOffset = 0;
+ if (!target.marker) {
+ while (!target.farthestViewportElement) {
+ target = target.parentNode;
+ }
+
+ target = target.farthestViewportElement;
+ }
+
+ // Trackball/trackpad vs wheel. Need to accommodate
+ var delta = 0;
+ if (evt.wheelDelta) {
+ if (evt.wheelDelta > 0) {
+ delta = Math.ceil(evt.wheelDelta / 120);
+ }
+ else {
+ delta = Math.floor(evt.wheelDelta / 120);
+ }
+ }
+ else if (evt.detail) {
+ if (evt.detail > 0) {
+ delta = -Math.ceil(evt.detail / 3);
+ }
+ else {
+ delta = -Math.floor(evt.detail / 3);
+ }
+ }
+
+ var zoomLevel = boundZoomLevel(target, target.zoomIndex + delta);
+ target.zoomIndex = zoomLevel;
+ var scale = target.zoomLevels[zoomLevel];
+
+ var y = evt.layerY;
+ var x = evt.layerX;
+
+ scaleGraph(target, scale, x, y);
+ }
+
+ this.boundZoomLevel = function(target, level) {
+ if (level >= target.settings.zoomNumLevels) {
+ return target.settings.zoomNumLevels - 1;
+ }
+ else if (level <= 0 ) {
+ return 0;
+ }
+
+ return level;
+ }
+
+ this.scaleGraph = function(target, scale, x, y) {
+ var sfactor = scale/target.scale;
+ target.scale = scale;
+
+ target.translateX = sfactor*target.translateX + x - sfactor*x;
+ target.translateY = sfactor*target.translateY + y - sfactor*y;
+
+ if (target.model) {
+ target.model.trigger("scaled");
+ }
+ retransform(target);
+ }
+
+ this.translateDeltaGraph = function(target, x, y) {
+ target.translateX += x;
+ target.translateY += y;
+ if (target.model) {
+ target.model.trigger("panned");
+ }
+ retransform(target);
+ }
+
+ this.retransform = function(target) {
+ //$(target).children('g').attr("transform", "translate(" + target.translateX + "," + target.translateY + ") scale(" + target.scale + ")");
+ var gs = target.childNodes;
+
+ var transformString = "translate(" + target.translateX + "," + target.translateY + ") scale(" + target.scale + ")";
+ for (var i = 0; i < gs.length; ++i) {
+ var g = gs[i];
+ if (g.nodeName == 'g') {
+ g.setAttribute("transform", transformString);
+ }
+ }
+
+ if (target.model) {
+ var obj = target.model.get("transform");
+ if (obj) {
+ obj.scale = target.scale;
+ obj.height = target.parentNode.clientHeight;
+ obj.width = target.parentNode.clientWidth;
+
+ obj.x1 = target.translateX;
+ obj.y1 = target.translateY;
+ obj.x2 = obj.x1 + obj.width*obj.scale;
+ obj.y2 = obj.y1 + obj.height*obj.scale;
+ }
+ }
+ }
+
+ this.resetTransform = function(target) {
+ var settings = target.settings;
+ target.translateX = settings.x;
+ target.translateY = settings.y;
+
+ if (settings.x < settings.x2) {
+ var factor = 0.90;
+
+ // Reset scale and stuff.
+ var divHeight = target.parentNode.clientHeight;
+ var divWidth = target.parentNode.clientWidth;
+
+ var width = settings.x2 - settings.x;
+ var height = settings.y2 - settings.y;
+ var aspectRatioGraph = height/width;
+ var aspectRatioDiv = divHeight/divWidth;
+
+ var scale = aspectRatioGraph > aspectRatioDiv ? (divHeight/height)*factor : (divWidth/width)*factor;
+ target.scale = scale;
+ }
+ else {
+ target.zoomIndex = boundZoomLevel(target, settings.zoomIndex);
+ target.scale = target.zoomLevels[target.zoomIndex];
+ }
+ }
+
+ this.animateTransform = function(target, scale, x, y, duration) {
+ var zoomLevel = calculateZoomLevel(scale, target.zoomLevels);
+ target.fromScaleLevel = target.zoomIndex;
+ target.toScaleLevel = zoomLevel;
+ target.fromX = target.translateX;
+ target.fromY = target.translateY;
+ target.fromScale = target.scale;
+ target.toScale = target.zoomLevels[zoomLevel];
+ target.toX = x;
+ target.toY = y;
+ target.startTime = new Date().getTime();
+ target.endTime = target.startTime + duration;
+
+ this.animateTick(target);
+ }
+
+ this.animateTick = function(target) {
+ var time = new Date().getTime();
+ if (time < target.endTime) {
+ var timeDiff = time - target.startTime;
+ var progress = timeDiff / (target.endTime - target.startTime);
+
+ //target.zoomIndex = Math.floor((target.toScaleLevel - target.fromScaleLevel)*progress) + target.fromScaleLevel;
+ //target.scale = target.zoomLevels[target.zoomIndex];
+ target.scale = (target.toScale - target.fromScale)*progress + target.fromScale;
+ target.translateX = (target.toX - target.fromX)*progress + target.fromX;
+ target.translateY = (target.toY - target.fromY)*progress + target.fromY;
+ retransform(target);
+ setTimeout(function() {this.animateTick(target)}, 1);
+ }
+ else {
+ target.zoomIndex = target.toScaleLevel;
+ target.scale = target.zoomLevels[target.zoomIndex];
+ target.translateX = target.toX;
+ target.translateY = target.toY;
+ retransform(target);
+ }
+ }
+
+ this.calculateZoomScale = function(scaleLevel, numLevels, points) {
+ if (scaleLevel <= 0) {
+ return points[0];
+ }
+ else if (scaleLevel >= numLevels) {
+ return points[points.length - 1];
+ }
+ var factor = (scaleLevel / numLevels) * (points.length - 1);
+ var floorIdx = Math.floor(factor);
+ var ceilingIdx = Math.ceil(factor);
+
+ var b = factor - floorIdx;
+
+ return b*(points[ceilingIdx] - points[floorIdx]) + points[floorIdx];
+ }
+
+ this.calculateZoomLevel = function(scale, zoomLevels) {
+ if (scale >= zoomLevels[zoomLevels.length - 1]) {
+ return zoomLevels.length - 1;
+ }
+ else if (scale <= zoomLevels[0]) {
+ return 0;
+ }
+
+ var i = 0;
+ // Plain old linear scan
+ for (; i < zoomLevels.length; ++i) {
+ if (scale < zoomLevels[i]) {
+ i--;
+ break;
+ }
+ }
+
+ if (i < 0) {
+ return 0;
+ }
+
+ return i;
+ }
+
+ var methods = {
+ init : function(options) {
+ var settings = {
+ x: 0,
+ y: 0,
+ x2: 0,
+ y2: 0,
+ minX: -1000,
+ minY: -1000,
+ maxX: 1000,
+ maxY: 1000,
+ zoomIndex: 24,
+ zoomPoints: [0.1, 0.14, 0.2, 0.4, 0.8, 1, 1.6, 2.4, 4, 8, 16],
+ zoomNumLevels: 48
+ };
+ if (options) {
+ $.extend(settings, options);
+ }
+ return this.each(function() {
+ var $this = $(this);
+ this.settings = settings;
+ this.marker = true;
+
+ if (window.addEventListener) this.addEventListener('DOMMouseScroll', mouseScrolled, false);
+ this.onmousewheel = mouseScrolled;
+ this.onmousedown = mouseDown;
+ this.onmouseup = mouseUp;
+ this.onmousemove = mouseMove;
+ this.onmouseout = mouseOut;
+
+ this.zoomLevels = new Array(settings.zoomNumLevels);
+ for (var i = 0; i < settings.zoomNumLevels; ++i) {
+ var scale = calculateZoomScale(i, settings.zoomNumLevels, settings.zoomPoints);
+ this.zoomLevels[i] = scale;
+ }
+ resetTransform(this);
+ });
+ },
+ transformToBox : function(arguments) {
+ var $this = $(this);
+ var target = ($this)[0];
+ var x = arguments.x;
+ var y = arguments.y;
+ var factor = 0.9;
+ var duration = arguments.duration;
+
+ var width = arguments.width ? arguments.width : 1;
+ var height = arguments.height ? arguments.height : 1;
+
+ var divHeight = target.parentNode.clientHeight;
+ var divWidth = target.parentNode.clientWidth;
+
+ var aspectRatioGraph = height/width;
+ var aspectRatioDiv = divHeight/divWidth;
+
+ var scale = aspectRatioGraph > aspectRatioDiv ? (divHeight/height)*factor : (divWidth/width)*factor;
+ //console.log("(" + x + "," + y + "," + width.toPrecision(4) + "," + height.toPrecision(4) + ")");
+ //console.log("(rg:" + aspectRatioGraph.toPrecision(3) + ",rd:" + aspectRatioDiv.toPrecision(3) + "," + scale.toPrecision(3) + ")");
+
+ if (arguments.maxScale) {
+ if (scale > arguments.maxScale) {
+ scale = arguments.maxScale;
+ }
+ }
+ if (arguments.minScale) {
+ if (scale < arguments.minScale) {
+ scale = arguments.minScale;
+ }
+ }
+
+ // Center
+ var scaledWidth = width*scale;
+ var scaledHeight = height*scale;
+
+ var sx = (divWidth - scaledWidth)/2 -scale*x;
+ var sy = (divHeight - scaledHeight)/2 -scale*y;
+ console.log("sx,sy:" + sx + "," + sy);
+
+ if (duration != 0 && !duration) {
+ duration = 500;
+ }
+
+ animateTransform(target, scale, sx, sy, duration);
+ },
+ attachNavigateModel : function(arguments) {
+ var $this = $(this);
+ var target = ($this)[0];
+ target.model = arguments;
+
+ if (target.model) {
+ var obj = {};
+ obj.scale = target.scale;
+ obj.height = target.parentNode.clientHeight;
+ obj.width = target.parentNode.clientWidth;
+
+ obj.x1 = target.translateX;
+ obj.y1 = target.translateY;
+ obj.x2 = obj.x1 + obj.height*obj.scale;
+ obj.y2 = obj.y1 + obj.width*obj.scale;
+
+ target.model.set({transform : obj});
+ }
+ }
+ };
+
+ // Main Constructor
+ $.fn.svgNavigate = function(method) {
+ if (methods[method]) {
+ return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
+ } else if (typeof method === 'object' || !method) {
+ return methods.init.apply(this, arguments);
+ } else {
+ $.error('Method ' + method + ' does not exist on svgNavigate');
+ }
+ };
+})(jQuery);