blob: be03f5d6922492ca21220b71eb45c693e61d6ab0 [file] [log] [blame]
(function () {
/**
* workaround to register events only once
* and get just the inner-event
*/
var eventMap = {};
window.addEventListener('message', function (event) {
var innerEvent = event.data;
var handler = eventMap[innerEvent.type];
if (handler instanceof Function) {
handler(innerEvent.data);
}
});
/**
* Use this function to register to post message events
*/
window.registerPostMessageEvent = function (type, handler) {
eventMap[type] = handler;
};
})();
function alertNoFlowTypeSelected() {
$('#selected-type-mt')
.addClass('pulse-info')
.on('change', function (event) {
$(event.target).removeClass('pulse-info');
})
alertError('Please select the flow type to continue');
}
var CompositionEditor = function () {
var componentId = document
.getElementById('iframe')
.getAttribute('componentid');
var userId = document
.getElementById('iframe')
.getAttribute('user_id');
var readOnlyComponent = document
.getElementById('iframe')
.getAttribute('readOnlyComponent');
var curcomp = {
cid: null,
//1806 US374595 save flow type in cdump
flowType: null,
version: 0,
nodes: [],
relations: [],
inputs: [],
outputs: []
};
window.d3Data = curcomp;
curcomp.cid = componentId;
this.curcomp = curcomp;
var flowTypes = window.flowTypes;
var typeSelect = document.getElementById("selected-type-mt");
if (flowTypes.length > 0) {
flowTypes
.forEach(function (flowType) {
var myOption = document.createElement("option");
myOption.text = flowType;
myOption.value = flowType;
typeSelect.add(myOption);
});
}
typeSelect
.addEventListener("change", function () {
curcomp.flowType = typeSelect.value;
});
document
.getElementById("savebtn")
.setAttribute("data-tests-id", "SaveButton");
document
.getElementById("savebtn")
.setAttribute("disabled", "true");
document
.getElementById("savebtn")
.setAttribute("style", "opacity:0.5");
if (readOnlyComponent == 'true') {
var componentUser = document
.getElementById('iframe')
.getAttribute('componentUser');
alertError("The resource is already checked out by user: " + componentUser);
} else {
document
.getElementById("savebtn")
.removeAttribute("disabled");
document
.getElementById("savebtn")
.setAttribute("style", "opacity:1");
}
document
.getElementById("savebtn")
.addEventListener("click", function () {
var mt = $('#selected-type-mt').val();
if (!mt) {
alertNoFlowTypeSelected();
return;
}
compController.saveComposition(curcomp);
});
var vfni = document
.getElementById('iframe')
.getAttribute('vnfiname');
// document.getElementById("submitbtn").setAttribute("data-tests-id",
// "SubmitButton"); if (!(vfni !== null && vfni !== "") || readOnlyComponent ==
// 'true') { //
// document.getElementById("submitbtn").setAttribute("disabled", "true"); //
// document.getElementById("submitbtn").setAttribute("style", "opacity:0.5"); }
// else { document.getElementById("submitbtn").addEventListener("click",
// function () { var mt = $('#selected-type-mt').val(); if (mt
// == null) { alertNoFlowTypeSelected(); return; }
// console.log('entered submitbtn eventlistener'); var component_Id =
// document.getElementById('iframe').getAttribute('componentid'); var
// serviceuuid = document.getElementById('iframe').getAttribute('serviceuuid');
// var vnfiname =
// document.getElementById('iframe').getAttribute('vnfiname');
// compController.createBlueprint(component_Id, serviceuuid, vnfiname, mt); });
// //console.log(x); }
function log(x, y) {
var args = ["composition:"].concat(Array.prototype.slice.call(arguments, 0));
console
.log
.apply(console, args);
}
function simulateclick(x, y, target) {
target = target || document.body;
var e = document.createEvent("MouseEvent");
e.initMouseEvent("click", true, true, window, 1, 0, 0, x, y, false, false, false, false, 0, null);
target.dispatchEvent(e);
}
function addcss(id, text) {
var head = document.getElementsByTagName("head")[0];
var style = document.createElement("style");
style.id = id;
style.type = "text/css";
style.innerHTML = text;
// if style with this id exists, remove it
try {
var styles = head.getElementsByTagName("style");
if (styles)
_.each(styles, function (x) {
if (x.id == id)
head.removeChild(x);
}
);
}
catch (e) {
log(e);
}
head.appendChild(style);
return style;
}
function parentmsg(s) {
return parent.postMessage(s, '*');
}
var msgid = 0;
function basemsg(action) {
return {
"action": action,
"channelID": "ice-to-cart",
"id": msgid++,
"timestamp": new Date()
};
}
function uuid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
}
function parsequery(s) {
s = s.substring(s.indexOf('?') + 1).split('&');
var params = {},
pair;
// march and parse
for (var i = s.length - 1; i >= 0; i--) {
pair = s[i].split('=');
params[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
}
return params;
}
var cid = "<span style='color:red'>NULL</span>";
function getCompositionError(error) {
console.error("unable to get composition: %o", error);
}
function init(cid) {
log("init");
cid = cid || uuid();
log("cid", cid);
curcomp.cid = cid;
console.log("get composition is called ");
$
.get(window.configOptions.backend_url + "getComposition/" + cid)
.then(function (resData) {
if (resData && resData.successResponse) {
try {
var composition = JSON.parse(resData.successResponse);
composition.cid = cid;
onCompositionEvent("initend");
restoregraph(composition);
} catch (error) {
getCompositionError(error);
}
} else {
document.dispatchEvent(new CustomEvent('noComposition'));
}
})
.fail(getCompositionError);
deletebutton();
}
function deletebutton() {
d3
.select("#compositiondiv")
.append("div")
.style("position", "absolute")
.style("bottom", "30px")
.style("right", "5px")
.style("background", "rgba(255,0,0,0.3)")
.html("toggle node deletion")
.on("click", function (d) {
if (d3.selectAll(".deletenode").style("visibility") == "hidden")
d3.selectAll(".deletenode").style("visibility", "visible");
else
d3
.selectAll(".deletenode")
.style("visibility", "hidden");
}
);
}
function getcdump(cid) {
var pe = svg.style("pointer-events");
log("suspend pointer-events");
svg.style("pointer-events", "none");
// fallback in case xhrget doesn't reset svg pointer events
setTimeout(function () {
if (svg.style("pointer-events") != pe) {
log("restore pointer-events fallback");
svg.style("pointer-events", pe);
}
}, 2000);
return new Promise(function (resolve, reject) {
xhrget("/cdump?cid=" + cid, function (resp) {
log("restore pointer-events");
if (svg.style("pointer-events") != "none") // assert?
log("wtf pointer-events");
svg.style("pointer-events", pe);
try {
resolve(JSON.parse(resp));
} catch (e) {
reject({"exception": e, "response": resp});
}
});
});
}
function compositionreadonly(val) {
if (val == undefined)
val = true;
if (val)
svg.style("pointer-events", "none");
else
svg.style("pointer-events", "all");
return svg.style("pointer-events");
}
function restoregraph(c) {
if (!c.nodes)
return;
curcomp.cid = c.cid;
rc = c;
c
.nodes
.forEach(function (n) {
addnode(n, 0, 0, n.ndata);
});
c
.relations
.forEach(function (r) {
var m = r.meta;
addlink(m.n1, m.n2, m.p1, m.p2, true);
});
sortinterfaces(); // HACK
//1806 US374595 flowType saved to cdump
if (c.flowType && _.contains(flowTypes, c.flowType)) {
typeSelect.value = c.flowType;
curcomp.flowType = typeSelect.value;
} else {
console.log(c.flowType + " not in flowTypes DDL")
}
}
function postcompimg() {
log("postcompimg");
/*xhrpostraw("/compimg?cid="+cid, serialsvg(),
function(resp) { log("compimg", resp); },
"image/svg");*/
}
function commitcomposition(callback) {
console.log("commitcomposition");
/*xhrpost("/composition.commit?cid="+cid, {},
function(resp) {
callback(resp);
});*/
}
function jsonparse(x) {
try {
return JSON.parse(x);
} catch (e) {
log("jsonparse", x, e);
throw e;
}
}
function template(id, fn) {
/*xhrget("/template?" + id, function(resp) {
var tp = JSON.parse(resp);
if (! tp.nodes) {
log("template:oops", tp);
fn(null);
}
var nn = tp.nodes.length;
if (nn == 0)
fn(tp);
else {
tp.nodes.map(function(n) {
if (n.id == 0) { // dummy node special case
if (--nn == 0)
fn(tp);
} else {
xhrget("/type?" + n.type.name, function(resp) {
var ti = JSON.parse(resp);
n.typeinfo = ti;
if (--nn == 0)
fn(tp);
});
}
});
}
});*/
}
function template0(id, fn) {
/*xhrget("/template?" + id, function(resp) {
var tp = JSON.parse(resp);
fn(tp);
});*/
}
function addtemplate(id) {
template(id, addcomp);
}
function addproduct(prod, x, y) {
log("addproduct", prod);
if (prod.offer) {
clearComposition();
}
catalogitem(prod.uuid, function (p) {
log("addproduct p", p);
// HACK -- this can't be right
log("HACK node.id", prod.uuid);
p
.model
.nodes
.forEach(function (n) {
n.id = prod.uuid;
});
if (p.models && p.models.length > 0) {
// addcomp
p
.models
.map(function (w) {
dropdata(w, x || bw / 2, y || bh / 2);
});
}
onCompositionEvent("added.product.to.composition", prod);
});
}
function setnodeproperties(nid, properties) {
/*xhrpost("/composition.setnodeproperties?cid="+cid, {"nid":nid, "properties":_.clone(properties)},
function(resp) {
});*/
}
function setnodepolicies(nid, policies) {
/*xhrpost("/composition.setnodepolicies?cid="+cid, {"nid":nid, "policies":_.clone(policies)},
function(resp) {
});*/
}
function deepclone(x) {
return JSON.parse(JSON.stringify(x));
}
var gensym = (function () {
var n = 0;
return function (x) {
var t = new Date().getTime();
return (x || "g") + "." + t + "." + n++;
};
})();
var xhrprefix = configOptions.backend_url;
function xhrgetBE(url, callback) {
var req = new XMLHttpRequest;
if (!(url.startsWith("https://") || url.startsWith("http://")))
url = xhrprefix + url;
req.open("GET", url, true); // asynchronous request
req.onreadystatechange = function (x) {
if (req.readyState === 4)
callback(req.responseText);
};
req.send(null);
}
function xhrget(url, callback) {
var req = new XMLHttpRequest;
if (!(url.startsWith("https://") || url.startsWith("http://")))
url = xhrprefix + url;
req.open("GET", url, true); // asynchronous request
req.onreadystatechange = function (x) {
if (req.readyState === 4)
callback(req.responseText);
};
req.send(null);
}
function xhrgetsync(url, callback) {
var req = new XMLHttpRequest;
if (!(url.startsWith("https://") || url.startsWith("http://")))
url = xhrprefix + url;
req.open("GET", url, false);
req.onreadystatechange = function (x) {
if (req.readyState === 4)
callback(req.responseText);
};
req.send(null);
}
callback = function (responseText) {
// write your own handler here.
console.log('result from http://localhost:8446/saveComposition/ac297d4d-0199-458f-99ff-2a6ff6' +
'ed849a \n' + responseText);
};
/**
* Callback function of AJAX request if the request fails.
*/
failCallback = function () {
// write your own failure handler here.
console.log('AJAXRequest failure!');
};
var apiService = new ApiService(xhrprefix, userId);
var compController = new CompController(apiService);
function xhrpost(url, obj, callback, type, async) {
var req = new XMLHttpRequest;
if (!(url.startsWith("https://") || url.startsWith("http://")))
url = xhrprefix + url;
req.open("POST", url, true); // asynchronous request
req.setRequestHeader("Content-Type", type || "application/json;charset=UTF-8");
req.setRequestHeader('USER_ID', userId);
req.onreadystatechange = function (x) {
if (req.readyState === 4)
callback(req.responseText);
};
try {
req.send(JSON.stringify(obj));
} catch (e) {
if (e.name == "TypeError")
req.send(JSON.stringify(obj));
else
throw(e);
}
}
function xhrpostraw(url, obj, callback, type) {
var req = new XMLHttpRequest;
if (!(url.startsWith("https://") || url.startsWith("http://")))
url = xhrprefix + url;
req.open("POST", url, true);
req.setRequestHeader("Content-Type", type || "text/plain");
req.setRequestHeader('USER_ID', userId);
req.onreadystatechange = function (x) {
if (req.readyState === 4)
callback(req.responseText);
};
req.send(obj);
}
var bw = document
.getElementById("compositioncontainer")
.clientWidth;
var bh = document
.getElementById("compositioncontainer")
.clientHeight;
// initially setting linkdistance to smaller value will help layout sort out
// edge crossings
var force = d3
.layout
.force()
.gravity(.9)
.charge(-3000)
.linkDistance(500)
.linkStrength(1)
.size([bw, bh]);
setTimeout(function () {
init(componentId);
});
function unfix() {
force
.nodes()
.forEach(function (n) {
n.fixed = false;
});
}
function fix() {
force
.nodes()
.forEach(function (n) {
n.fixed = true;
});
}
function resize() {
bw = document
.getElementById("compositioncontainer")
.clientWidth;
bh = document
.getElementById("compositioncontainer")
.clientHeight;
svg
.attr("width", bw)
.attr("height", bh);
force.size([
bw - 30,
bh - 30
]);
force.start();
}
var rev = 0;
var svg = d3
.select("#compositioncontainer")
.append("svg:svg")
.style("overflow", "visible")
.attr("width", bw)
.attr("height", bh);
var undergraph = svg.append("svg:g");
var graph = svg.append("svg:g");
var edgegroup = graph.append("svg:g");
function bbox(sel) { // arg is d3 svg selection
return sel[0][0].getBBox();
}
function clamp(d) {
var pad = 120; // HACK
var x1 = pad,
y1 = pad,
x2 = bw - pad,
y2 = bh - pad;
if (d.x < x1)
d.x = x1;
if (d.y < y1)
d.y = y1;
if (d.x > x2)
d.x = x2;
if (d.y > y2)
d.y = y2;
return d;
}
function circleclamp(d) {
var p2 = 2 * Math.PI;
if (d.a < 0)
d.a += p2;
if (d.a > p2)
d.a -= p2;
return d;
}
function tick() {
if (force.alpha() < .05) // chill
force.alpha(0);
graph
.selectAll(".node")
.attr("transform", function (d) {
clamp(d);
// (d.x > 350) { d.x = 350; } break;
// case 2: if (d.x < bw - 350) { d.x = bw - 350;
// } break; } } HACK for vLAN nodes in uCPE
var modelName = d
.model
.name
.toUpperCase()
.replace(/-/g, " ");
if (modelName.match(/\bVPN\sFACING\b/) || modelName.match(/\bINTERNET\sFACING\b/)) {
if (d.y > 130)
d.y = 130;
}
else if (modelName.match(/\bLAN\sFACING\b/)) {
if (d.y < bh - 130)
d.y = bh - 130;
}
else if (modelName.match(/\bNM\sVLAN\b/)) {
if (d.x < bw - 150)
d.x = bw - 150;
}
else if (modelName.match(/\bOAM\sVLAN\b/)) {
if (d.x > 150)
d.x = 150;
}
// END TODO
return "translate(" + d.x + "," + d.y + ")";
});
d3
.selectAll(".relation")
.attr("d", epath);
d3
.selectAll(".nodeport")
.attr("transform", function (d) {
circleclamp(d);
if (d.link) {
if (d.link.srcport == d)
n1 = d.link.source,
n2 = d.link.target;
else
n1 = d.link.target,
n2 = d.link.source;
var p1 = {
x: n1.x,
y: n1.y
},
p2 = {
x: n2.x,
y: n2.y
};
var dx = n2.x - n1.x,
dy = n2.y - n1.y;
d.a = Math.atan2(dx, dy);
circleclamp(d);
var p = nodeboundarypoint(p1, p2, d.parent.name);
d.x = p.x - d.parent.x;
d.y = p.y - d.parent.y;
} else {
if (d.parent.ports.length > 1) {
d
.parent
.ports
.forEach(function (x) {
if (d != x && Math.abs(x.a - d.a) < 0.8) {
d.a += x.a > d.a
? -.02
: .02;
}
});
}
var p1 = {
x: d.parent.x,
y: d.parent.y
},
p2 = {
x: d.parent.x + 100 * Math.sin(d.a),
y: d.parent.y + 100 * Math.cos(d.a)
};
var p = nodeboundarypoint(p1, p2, d.parent.name);
d.x = p.x - d.parent.x;
d.y = p.y - d.parent.y;
}
return "translate(" + d.x + "," + d.y + ")";
});
}
force.on("tick", tick);
// p1 inside, p2 outside
function nodeboundarypoint(p1, p2, nodename) {
if ((Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y)) < 0.2) {
return p1;
}
if (typeof document.elementsFromPoint === 'function') {
var mp = {
x: (p1.x + p2.x) / 2,
y: (p1.y + p2.y) / 2
};
var r = svg
.node()
.getBoundingClientRect();
var n = document
.elementsFromPoint(mp.x + r.left, mp.y + r.top)
.find(function (x) {
var d = d3
.select(x)
.datum();
return d3
.select(x)
.classed("nodebody") && d && d.name == nodename;
});
if (n)
p1 = mp;
else
p2 = mp;
return nodeboundarypoint(p1, p2, nodename);
}
// ... here when no function document.elementsFromPoint (FF <46) ... -- put on
// circle
var r = 30;
var dx = p2.x - p1.x,
dy = p2.y - p1.y;
if (!dx) {
if (r < Math.abs(dy)) {
p1.y += r * Math.sign(dy);
} else {
p1.y += dy;
}
return p1;
}
alpha = Math.atan2(dx, dy);
p1.x += r * Math.sin(alpha);
p1.y += r * Math.cos(alpha);
return p1;
}
var hitrect = svg
.node()
.createSVGRect();
hitrect.height = 1;
hitrect.width = 1;
// ffs, chrome's svg.getIntersectionList uses bbox for intersection
function nodeboundarypoint0(p1, p2, node) {
if ((Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y)) < 0.5)
return p1;
else {
var n,
mp = {
x: (p1.x + p2.x) / 2,
y: (p1.y + p2.y) / 2
};
hitrect.x = mp.x;
hitrect.y = mp.y;
var hits = svg
.node()
.getIntersectionList(hitrect, null);
if (hits) {
function itol(x) {
var v,
r = [],
i = 0;
while (v = x.item(i++))
r.push(v);
return r;
}
hits = itol(hits);
n = hits.find(function (x) {
var d = d3
.select(x)
.datum();
return d3
.select(x)
.classed("nodebody") && d && d.name == node;
});
}
if (n)
p1 = mp;
else
p2 = mp;
return nodeboundarypoint0(p1, p2, node);
}
}
function abs(n) {
return n < 0
? -n
: n;
}
function rand(n) {
n = n || 1.0;
return (Math.random() * n) - n / 2;
}
function elt(selection) {
return selection[0][0];
}
function nameof(x) {
return x
? x.name
: "...";
}
function dnode(name) {
return force
.nodes()
.find(function (n) {
return n.model.name === name;
});
}
function dlink(name) {
return force
.links()
.filter(function (l) {
return l.source.model.name === name || l.target.model.name === name;
});
}
function matchnodetype(n, tname) {
return ((n.type.name === tname) || (n.typeinfo && n.typeinfo.hierarchy && n.typeinfo.hierarchy.find(function (t) {
return t.name === tname;
})));
}
// "transactions" for composition operations
var logtxn = false;
var incompositiontxn = false;
function txn(name, fn) {
if (incompositiontxn) // already in transaction
return fn();
else {
try {
if (logtxn)
log("TXN.begin", name);
incompositiontxn = true;
return fn();
} finally {
incompositiontxn = false;
if (logtxn)
log("TXN.end", name);
onCompositionEvent("composition.done");
}
}
}
function addnode(model, x, y, ndata) {
return txn("addnode", function () {
return _addnode(model, x, y, ndata);
});
}
function _addnode(model, x, y, ndata) {
var hasdata = !!ndata;
curmodel = model;
if (!ndata) {
ndata = {
name: gensym("n"),
label: model.name,
x: x,
y: y,
px: x - 1,
py: y - 1,
ports: [],
radius: 30
};
if (matchnodetype(model, "tosca.nodes.network.Port"))
ndata.radius = 10;
if (model.name === "CPE")
ndata.radius = 80;
}
//log("node", model.name, ndata.x, ndata.y, ndata.fixed);
ndata.x = ndata.x || bw / 2 + rand(100);
ndata.y = ndata.y || bh / 2 + rand(100);
ndata.px = ndata.x - 1;
ndata.py = ndata.y - 1;
ndata.ports = ndata.ports || [];
ndata.label = model.name;
model = _.clone(model);
model.nid = ndata.name;
var node = _.extend({}, model, {ndata: ndata});
curcomp
.nodes
.push(deepclone(node));
if (!hasdata) {
/*xhrpost("/composition.addnode?cid="+cid, node,
function(resp) {
});
xhrpost("/composition.savecomp?cid="+cid, curcomp,
function(resp) {
});*/
onCompositionEvent("composition.add.node", model);
}
ndata.model = model;
force
.nodes()
.push(ndata);
var nodes = force.nodes();
var gnode = graph
.selectAll(".node")
.data(nodes, function (d) {
return d.name;
});
var n = gnode
.enter()
.append("g")
.attr("id", model.nid.replace(/\W/g, "_"))
// this must come first, because ?? .attr("class", "node draggable")
.classed("node", true)
.classed("draggable", true)
.attr("draggable", "true")
.classed(model.type.name.replace(/\W/g, "_"), true)
.attr("transform", function (d) {
if (!d.x || isNaN(d.x))
d.x = bw / 2 + rand(100);
if (!d.y || isNaN(d.y))
d.y = bh / 2 + rand(100);
return "translate(" + d.x + "," + d.y + ")";
})
.on("click", function (d) {
if (d3.event.metaKey || d3.event.ctrlKey)
removenode(d);
}
);
rendernode(n);
n.call(force.drag().on("dragstart", function (d) {
d3
.select(this)
.classed("fixed", d.fixed = true);
}));
function rti(n, name) {
return n
.typeinfo
.requirements
.find(function (tr) {
return tr.name === r.name;
})
}
if (!model.typeinfo)
model.typeinfo = {}; // dummy node special case
if (model.requirements) {
model.requirements = dedup(model.requirements);
model
.requirements
.forEach(function (x) {
if (x.name == "host") // CCD hack
return;
ndata
.ports
.push({
name: x.name,
parent: ndata,
ptype: "req",
id: uuid(),
portinfo: _.clone(x)
});
});
model
.requirements
.forEach(function (r) {
r.ti = model
.typeinfo
.requirements
.find(function (tr) {
return tr.name == r.name;
});
});
}
// this may become uneccessary(?) (serban: type info now merged with
// requirements/capabilities)
if (model.typeinfo.requirements) {
model.typeinfo.requirements = dedup(model.typeinfo.requirements);
model
.typeinfo
.requirements
.forEach(function (x) {
var port = ndata
.ports
.find(function (y) {
return y.name == x.name && y.ptype == "req";
});
if (port)
port.portinfo = _.extend(_.clone(x), port.portinfo); // this should never happen
else {
if (x.name == "host") // CCD hack
return;
log("UNEXPECTED TYPEINFO", x.name);
}
});
}
if (model.capabilities) {
model.capabilities = dedup(model.capabilities);
model
.capabilities
.forEach(function (x) {
ndata
.ports
.push({
name: x.name,
parent: ndata,
ptype: "cap",
id: uuid(),
portinfo: _.clone(x)
});
});
model
.capabilities
.forEach(function (c) {
c.ti = model
.typeinfo
.capabilities
.find(function (tc) {
return tc.name == c.name;
});
});
}
if (model.typeinfo.capabilities) {
model.typeinfo.capabilities = dedup(model.typeinfo.capabilities);
model
.typeinfo
.capabilities
.forEach(function (x) {
var port = ndata
.ports
.find(function (y) {
return y.name == x.name && y.ptype == "cap";
});
if (port)
port.portinfo = _.extend(_.clone(x), port.portinfo); // this should never happen
else {
log("UNEXPECTED TYPEINFO", x.name);
}
});
}
addports(n);
force.start();
return ndata;
}
// pin lans and wans in lexicographic order
function sortinterfaces() {
// var rx = [/^WAN/, /^LAN/]; for (var i in rx) { var x = 200;
// d3.selectAll(".node") .filter(function (d) { return
// d.label.match(rx[i]); }) .sort(function (a, b) { return
// a.label > b.label; }) .each(function (d) { d.x = d.px = x;
// x += 100; d.fixed = true; }); }
}
var _nd = [];
function updatenodes() {
var nd = force
.nodes()
.map(function (d) {
var f = {
nid: d.name,
x: d.x,
y: d.y,
px: d.px,
py: d.py,
radius: d.radius,
fixed: true
};
f.ndata = _.clone(f);
f.ndata.name = d.name;
return f;
});
// only consider x,y values in computing sameness
function same(a, b) {
return a.every(function (c) {
return b.some(function (d) {
return c.x == d.x && c.y == d.y;
});
});
}
if (!same(nd, _nd)) {
log("updatenodes...changed");
onCompositionEvent("after.loaded.composition");
} else {
log("updatenodes...stable");
}
_nd = nd;
}
force.on("end", updatenodes);
function pp(x) {
return JSON.stringify(x, null, 2);
}
function str(x) {
return JSON.stringify(x);
}
function dedup(a) {
var r = [];
a.forEach(function (x) {
if (!r.find(function (y) {
return x.name == y.name;
}))
r.push(x)
});
return r;
}
function addport(n, name, ptype, portinfo) {
n
.each(function (d) {
d
.ports
.push({
name: name || gensym(),
parent: d,
ptype: ptype,
portinfo: portinfo,
id: uuid()
});
});
addports(n);
}
var iconbase = "/images/ice/";
iconbase = "/img/";
var icons = {};
function ngon(node, n, offset) {
offset = offset || 0;
//var offset = n == 4 ? Math.PI/4 : 0;
var r = node
.datum()
.radius * (1 + 1 / n),
p = [];
for (var i = 0; i < n; i++) {
// offset is so squares are squared :/
var a = (i * Math.PI * 2) / n + offset;
p.push(r * Math.sin(a));
p.push(r * Math.cos(a));
}
return node
.append("svg:polygon")
.classed("nodebody", true)
.attr("points", p.join(","));
}
var compositionDraggingType;
function allowDropByType(event, type) {
if (compositionDraggingType === type) {
event.preventDefault();
event.dataTransfer.dropEffect = 'copy';
return true;
}
return false;
}
/* DEAD CODE?
// handle dropping location data on a node
function dropLocation(event,that) {
compositionDraggingType = null;
var locText = event.dataTransfer.getData('location');
if (locText === '') {return;}
if (event.stopPropagation) {event.stopPropagation();}
var nodeData = d3.select(that).datum();
log("dropLocation",locText,nodeData);
var loc = shoppingCart.locations.findByKey(Location.genKey(JSON.parse(locText)));
log("dropLocation loc",loc);
closeNodePropertiesEditor();
setLocationOnSite(nodeData.model,loc);
d3.select(that).select("#confignode").html(function(d) {return getShortPropertyValues(d.model);});
}
*/
// var getShortPropertyValues = getShortPropertyValues || function(x) { return
// "{property values}" };
var getShortPropertyValues = getShortPropertyValues || function (x) {
return ""
};
var cpe;
function renderucpe() {
cpe = undergraph
.append("svg:rect")
.classed("ucpecontainer", true)
.attr("width", bw - 200)
.attr("height", bh - 100)
.attr("x", 100)
.attr("y", 50)
.attr("rx", 20)
.attr("ry", 20)
.attr("stroke", "black")
.attr("stroke-width", 5)
.attr("fill", "rgba(0,0,0,0.1)")
.style("opacity", 0.2);
}
function rendernode(n) {
var type = n
.datum()
.model
.type
.name;
var model = n
.datum()
.model;
// HACK
if (matchnodetype(n.datum().model, "tosca.nodes.network.Port")) {
ngon(n, 4);
return;
}
n
.append("svg:rect")
.classed("nodebody", true)
/*
.attr("width", 70)
.attr("height", 44)
.attr("x", -35)
.attr("y", -22)
*/
.attr("width", 90)
.attr("height", 64)
.attr("x", -45)
.attr("y", -32)
.attr("rx", 5)
.attr("ry", 5);
n.classed(type.replace(/\W/g, "_"), true);
n.classed(n.datum().model.name.replace(/\W/g, "_"), true);
var w = 150,
h = 50; // node width, height
// n.append("g") .attr("transform", "translate(" + (-(w / 2 - 15)) + "," +
// (-(h / 2 - 5)) + ")") .append("foreignObject") .attr({ width:
// 100, height: 50, fill: '#7413E8', 'class':
// 'svg-tooltip' }) .append('xhtml:div') .append('div')
// .html(function (d) { return squishnodename(d.label) }) .append("div")
// .attr({ width: 50, height: 50, fill: 'red', color:
// 'green' }) .style("pointer-events", "all") .style("cursor",
// "pointer") .html("config") .on("click", function (d) { if
// (d3.event.metaKey || d3.event.ctrlKey) { return; } if
// (typeof editNodeProperties === 'function') { editNodeProperties(this); }
// else { configeditor(d3.select(this), d.model); } });
n
.append("g")
.attr("transform", "translate(" + (-(w / 2 - 15)) + "," + (-(h / 2 - 5)) + ")")
.append("foreignObject")
.attr("width", (w - 10) + "px")
.attr("height", (h + 60) + "px")
.append("xhtml:div")
.classed("compositionbody", true)
//.classed(type.replace(/\./g,"_"), true)
.classed("nodetext", true)
.style("width", (w - 30) + "px")
.style("height", (h - 20) + "px")
.style("pointer-events", function () {
return (type == "asc.nodes.Site"
? null
: "none");
})
.attr("ondragover", function () {
return (type == "asc.nodes.Site"
? "allowDropByType(event,'location');"
: null);
})
.attr("ondrop", function () {
return (type == "asc.nodes.Site"
? "dropLocation(event,this);"
: null);
})
.html(function (d) {
var icon = icons[d.model.type.name];
if (!icon) {
if (d.model.typeinfo && d.model.typeinfo.hierarchy) {
var h = d.model.typeinfo.hierarchy;
for (i in h) {
var type = h[i].name;
if (icon = icons[type])
break;
}
}
}
if (icon)
icon = iconbase + icon;
else
icon = window.location.origin + iconbase + "3net.png";
icon = "dcae/comp-fe/img/death.png";
if (propertynameval(d.model.properties, "designer_name")) {
// return "<img class=nodeicon width='40px' src=" + icon + "></img>" + "<br/>" +
// "<center>" + propertynameval(d.model.properties, "designer_name") +
// "</center>";
} else {
//return "<img width='30px' src=" + icon + "></img>" + "<br/>" +
return "<center>" +
// "<img class=nodeicon width='20px' src=" + icon + "></img>" + "<br/>" +
"<span class=nodenametext>" + squishnodename(d.label) + "</span><br/></center>";
}
})
// .append("span") .attr("id", "confignode") .attr("class", "confignode")
// .style("pointer-events", "all") .style("cursor", "pointer") .html(function(d)
// {return getShortPropertyValues(d.model);}) .html("config") .on("click",
// function (d) { if (d3.event.metaKey || d3.event.ctrlKey) { return; }
// if (typeof editNodeProperties === 'function') { editNodeProperties(this);
// } else { if ($('#selected-type-mt').val() == null) {
// alertNoFlowTypeSelected(); } else { var self = this;
// compController.saveComposition(curcomp) .then(function () {
// d.model.uniqeId = d.name; configeditor(d3.select(self),
// d.model); }); } } }); node delete button
var del = n
.append("g")
.classed("deletenode", true)
.attr("transform", "translate(0,-10)")
.style("visibility", "hidden");
del
.append("svg:ellipse")
.attr("rx", 10)
.attr("ry", 10)
.attr("fill", "red")
.attr("opacity", 0.7)
.on("click", function (d) {
if (d3.select(this).style("visibility") != "hidden")
removenode(d);
}
);
del
.append("svg:text")
.attr("dominant-baseline", "middle")
.attr("text-anchor", "middle")
.style("color", "black")
.style("pointer-events", "none")
.text(function (d) {
return "X";
})
}
var propertyeditor = propertyeditor || null;
function propertynameval(props, name) {
var p = props && props.find(function (x) {
return x.name == name;
});
return p && p.assignment
? p.assignment.value
: null;
}
function configeditor(selection, model) {
var x = selection[0][0]
var r = x.getBoundingClientRect();
var e = document.getElementById("configeditor");
var stuff = d3.select(e.children[0]);
var cfg = d3
.select("#configstuff")
.html("properties for <b>" + model.name + ":</b>");
var props = cfg
.append("form")
.attr("id", "cfgform")
.style("display", "table")
.style("overflow-y", "auto")
.style("overflow-x", "hidden")
.style("padding-left", "10px")
.style("width", "100%");
mm = model;
model
.properties
.forEach(function (p) {
var row = props
.append("div")
.style("line-height", "25px")
.style("height", "45px")
.style("display", "table-row");
row
.append("label")
.style("display", "table-cell")
.html(p.name);
row
.append("input")
.style("display", "table-cell")
.style("width", "90%")
.attr("type", "text")
.attr("name", p.name)
.attr("value", function () {
return p.value && !_.isObject(p.value)
? p.value
: null;
});
if (window.isRuleEditorActive) {
row
.append('span')
.attr({"class": "glyphicon glyphicon-cog rule-editor-btn"})
.on('click', function () {
var self = this;
var reModel = $('#rule-editor-modal');
openRuleEditor();
function openRuleEditor() {
var $propInput = $(self)
.parent()
.find('input');
var flowType = $('#selected-type-mt').val();
var data = {
vfcmtUuid: curcomp.cid,
nodeName: model.name,
nodeId: model.uniqeId,
fieldName: p.name,
userId: userId,
flowType: flowType
};
window.registerPostMessageEvent('disable-loader', function () {
$('#modal-loader').hide();
});
window.registerPostMessageEvent('exit', function (jsonResult) {
reModel.modal('hide');
if (jsonResult !== null) {
try {
JSON.parse(jsonResult); // verifing that jsonResult is a valid json
p.value = jsonResult; // updating model
$propInput.val(p.value); // updating view
} catch (err) {
alert('Internal Error: unable to parse rule-editor value');
}
}
});
reModel
.find('iframe')
.attr({
src: window.ruleEditorUrl + '?' + $.param(data),
style: 'display: none'
})
.load(function (event) {
$(event.target).attr({style: 'display: block'});
});
reModel.modal({backdrop: 'static', keyboard: false});
$('#modal-loader').show();
}
});
}
});
d3
.select("#configset")
.on("click", function () {
eatform(model);
});
props
.append("div")
.style("display", "table-column");
props
.append("div")
.style("display", "table-column")
.style("width", "70%");
e.style.left = r.left;
e.style.top = r.top;
e.style.visibility = "visible";
e.style.transform = "scale(1)";
}
function eatform(model) {
d3
.select("#cfgform")
.selectAll("input")
.each(function () {
var i = d3.select(this),
name = i.attr("name"),
val = i.property("value");
if (val) {
// debugger;
var p = model
.properties
.find(function (x) {
return x.name == name;
});
p.value = val;
log("forminput", name, val);
}
curcomp
.nodes
.forEach(function (node) {
if (node.nid == model.uniqeId) {
var copy = model
.properties
.map(function (a) {
return Object.assign({}, a);
});
node.properties = copy;
}
});
});
configclose();
}
function configclose(x) {
var e = document.getElementById("configeditor");
e.style.transform = "scale(.001)";
}
function selectucpe() {
return graph
.select(".node")
.filter(function (d) {
return d.label === "ucpe";
});
}
function removeucpe() {
selectucpe().each(removenode);
}
function removenode(d) {
return txn("removenode", function () {
return _removenode(d);
});
}
function _removenode(d) {
curcomp.nodes = curcomp
.nodes
.filter(function (n) {
return n.nid != d.name;
});
console.log("before: ", curcomp.nodes);
onCompositionEvent("composition.remove.node", {nid: d.name});
if (d.label === "ucpe") {
// cpe implementation is such a hack
cpe.remove();
return;
}
_
.values(d.ports)
.map(function (x) {
if (x.link)
removelink(x.link);
}
);
nodes = _.without(force.nodes(), d);
graph
.selectAll(".node")
.data(nodes, function (d) {
return d.name;
})
.exit()
.remove();
force
.nodes(nodes)
.start();
}
function addports(n) {
var port = n
.selectAll(".nodeport")
.data(function (d) {
return d.ports;
}, function (p) {
return p.id;
})
//.filter(function(d) { return d.name != "host"; })
.enter();
var pg = port
.append("g")
.attr("class", "nodeport")
.attr("name", function (d) {
return d.name;
})
.attr("transform", function (d) {
var pp = d.parent.ports;
d.a = rand() * Math.PI * 2;
d.x = d.parent.radius * Math.sin(d.a);
d.y = d.parent.radius * Math.cos(d.a);
return "translate(" + d.x + "," + d.y + ")";
});
pg
.append("svg:circle")
.classed("targetcircle", true)
.attr("r", 5);
var pc = pg
.append("svg:circle")
.attr("class", function (d) {
var rtype = "nuh";
if (d.ptype === "cap") {
if (d.portinfo) {
if (d.portinfo.type)
rtype = d.portinfo.type.name;
if (d.portinfo.target)
rtype = d.portinfo.target.name;
}
else {
rtype = "null";
}
} else {
try {
rtype = d.portinfo.capability.name;
} catch (e) {
log("oops", d, d.parent.model.name);
}
}
if (rtype.name) {
log("HACK rtype", rtype);
rtype = rtype.name;
}
if (rtype)
rtype = rtype.replace(/\W/g, "_");
else
log("NULL RTYPE", d);
return (d.ptype == "cap"
? "capabilityport"
: "requirementport") + " " + rtype;
})
.classed("srcport", true)
.attr("r", 5)
.attr("port", function (d) {
return d.name;
});
pg
.append("svg:text")
.attr("class", "nodeporttext")
.attr("transform", "scale(.75)")
.attr("dominant-baseline", "middle")
.style("pointer-events", "none")
.text(function (d) {
return squishportname(d.name);
});
pc.on("click", function (d) {
log("pc.onclick", d);
var port = d3.select(this);
if (d3.selectAll(".tmprelation").empty())
startedge(port, d);
else
targetclick(port, d);
}
);
pc.on("mouseenter", function (d) {
var self = d3.select(this);
var t = d3
.select(self.node().parentNode)
.select("text");
t.style("font-size", "15px");
//t.style("stroke", "black");
});
pc.on("mouseleave", function (d) {
var self = d3.select(this);
var t = d3
.select(self.node().parentNode)
.select("text");
t.style("font-size", "12px");
//t.style("font-size", "inherit"); t.style("stroke", "rgba(0,0,0,0.3)");
});
pc.on("touchstart", function (d) {
d3
.event
.preventDefault();
var t = d3.event.touches[0],
x = t.clientX,
y = t.clientY;
var port = d3.select(this);
if (d3.selectAll(".tmprelation").empty()) {
force.stop();
startedge(port, d);
} else {
force.resume();
targetclick(port, d);
}
});
pg.classed("targetport", true);
return pg;
}
//hacks
function squishportname(s) {
return s
.replace(/_connection$/, "")
.replace(/_input$/, "")
.replace(/_output$/, "")
.replace(/network/, "net");
}
function squishnodename(s) {
return s
.replace(/network/, "net")
.replace(/_analytics$/, "")
.replace(/customer/, "cust");
}
function portclick(d) {
var port = d3.select(this);
if (d3.selectAll(".tmprelation").empty())
startedge(port, d);
else
targetclick(port, d);
}
function addlink(n1, n2, p1, p2, restoring) {
return txn("addlink", function () {
return _addlink(n1, n2, p1, p2, restoring);
});
}
function _addlink(n1, n2, p1, p2, restoring) {
var nodes = force.nodes();
var links = force.links();
//p1 = p1 || gensym(); p2 = p2 || gensym();
n1 = typeof n1 === "object"
? n1
: _.findWhere(nodes, {name: n1});
n2 = typeof n2 === "object"
? n2
: _.findWhere(nodes, {name: n2});
if (!n1 || !n2) {
// hostedOn exceptional case log("addlink?", n1, n2, p1, p2);
return [n1, n2];
}
var sp,
tp;
try {
sp = n1
.ports
.find(function (p) {
return p.name == p1 && (!p.link || !p.link.name);
});
tp = n2
.ports
.find(function (p) {
return p.name == p2 && (!p.link || !p.link.name);
});
} catch (e) {
log(e, n1, n2);
}
if (sp == null) {
// port is already linked, so we'll duplicate it (unless occurrences exceeded)
var xp = n1
.ports
.find(function (p) {
return p.name == p1
});
sp = _.clone(xp);
sp.link = null;
sp.id = uuid();
n1
.ports
.push(sp);
addports(d3.selectAll(".node").filter(function (n) {
return n1.name == n.name;
}));
}
if (tp == null) {
// port is already linked, so we'll duplicate it (unless occurrences exceeded)
var xp = n2
.ports
.find(function (p) {
return p.name == p2
});
tp = _.clone(xp);
tp.link = null;
tp.id = uuid();
n2
.ports
.push(tp);
addports(d3.selectAll(".node").filter(function (n) {
return n2.name == n.name;
}));
}
// if port allows multiple occurrences, duplicate it (weak test on occurrences
// is a hack)
if (sp.portinfo.occurrences == null || sp.portinfo.occurrences[1] > 1) {
var nx = d3
.selectAll(".node")
.filter(function (n) {
return n1.name == n.name;
});
addport(nx, sp.name, sp.ptype, sp.portinfo);
}
if (tp.portinfo.occurrences == null || tp.portinfo.occurrences[1] > 1) {
var nx = d3
.selectAll(".node")
.filter(function (n) {
return n2.name == n.name;
});
addport(nx, tp.name, tp.ptype, tp.portinfo);
}
/*
// hack -- add port for relations with multiple occurrences
//log("addlink", sp.type, sp.name, tp.type, tp.name);
if (sp.ptype == "cap" && sp.name == "access_conn") {
var nx = d3.selectAll(".node").filter(function(n) { return n1.name == n.name; });
addport (nx, "access_conn", "cap", sp.portinfo);
}
if (tp.ptype == "cap" && tp.name == "access_conn") {
var nx = d3.selectAll(".node").filter(function(n) { return n2.name == n.name; });
addport (nx, "access_conn", "cap", tp.portinfo);
}
*/
var link = {
name: gensym("lnk"),
source: n1,
target: n2,
srcport: sp,
targetport: tp,
pending: true
};
sp.link = link;
tp.link = link;
links.push(link);
var ln = edgegroup
.selectAll(".relation")
.data(links, function (d) {
return d.name;
});
ln
.enter()
.insert("svg:path")
.classed("relation", true)
.classed("pending", true)
.attr("d", epath)
.on("click", function (d) {
if (d3.event.metaKey || d3.event.ctrlKey)
removelink(d);
}
);
ln
.exit()
.remove();
setTimeout(function () {
edgegroup
.selectAll(".pending")
.classed("pending", false)
.each(function (d) {
d.pending = false;
});
}, 1000);
var meta = {
n1: n1.name,
n2: n2.name,
p1: p1,
p2: p2
};
function findrelationship(n, p) {
var r = n.typeinfo.requirements && n
.typeinfo
.requirements
.find(function (x) {
return x.name == p;
});
return r && r.relationship && [n.name, r.relationship.name, r.name];
}
meta.relationship = findrelationship(n1.model, p1) || findrelationship(n2.model, p2);
var relation = {
rid: link.name,
n1: n1.name,
name1: n1.model.name,
n2: n2.name,
name2: n2.model.name,
meta: meta
};
curcomp
.relations
.push(deepclone(relation));
if (!restoring) {
/*xhrpost("/composition.addrelation?cid="+cid, relation,
function(resp) {
});
xhrpost("/composition.savecomp?cid="+cid, curcomp,
function(resp) {
});*/
}
force.start();
return link;
}
function removelink(d) {
return txn("removelink", function () {
return _removelink(d);
});
}
function removelink(d) {
/*xhrpost("/composition.deleterelation?cid="+cid, {rid: d.name},
function(resp) {
});*/
curcomp.relations = curcomp
.relations
.filter(function (r) {
r.rid != d.name
});
/*xhrpost("/composition.savecomp?cid="+cid, curcomp,
function(resp) {
});*/
var links = _.without(force.links(), d);
edgegroup
.selectAll(".relation")
.data(links, function (d) {
return d.name;
})
.exit()
.remove();
force
.links(links)
.start();
d.srcport.link = null;
d.targetport.link = null;
}
var srcport = false;
function startedge(port, d) {
log("startedge", d);
d = port.datum(); // ??
var type,
rtype;
try {
if (d.ptype == "cap") {
rtype = d.portinfo.type.name;
type = "requirementport";
} else {
rtype = d.portinfo.capability.name;
//rtype = d.portinfo.target.name; // MAYBE??
type = "capabilityport";
}
} catch (e) {
log("startedge-error", e, d);
}
if (rtype.name) {
log("HACK rtype", rtype);
rtype = rtype.name;
}
if (rtype)
rtype = rtype.replace(/\./g, "_");
else
log("NULL RTYPE");
d3
.selectAll("." + rtype + "." + type)
.filter(function (c) {
return c.parent != d.parent;
}) // same node
.filter(function (c) {
return !c.link;
}) // already linked
.classed("fabulous", true);
var mx = port[0][0].getScreenCTM();
x = mx.e;
y = mx.f;
srcport = port; // global
d3
.selectAll(".targetport")
.classed("targeting", true);
var link = {
source: d.parent,
target: {
x: x,
y: y
},
srcport: d,
targetport: {
x: 5,
y: 5
}
};
srcport.each(function (d) {
d.link = link;
});
var tmp = graph
.selectAll(".tmprelation")
.data([link])
.enter()
.insert("svg:path")
.attr("class", "tmprelation")
.attr("d", tpath);
d3
.select("#compositioncontainer")
.on("mousemove", function () {
var m = d3.mouse(this);
link.target.x = m[0];
link.target.y = m[1];
tick();
tmp.attr("d", tpath);
})
.on("touchmove", function () {
d3
.event
.preventDefault();
var m = d3.mouse(this);
link.target.x = m[0];
link.target.y = m[1];
tick();
tmp.attr("d", tpath);
})
.on("mouseup", function () {
// will miss target click event if this is immediate
setTimeout(function () {
if (srcport)
srcport.each(function (d) {
d.link = null;
});
srcport = false; // HACK
d3
.selectAll(".targeting")
.classed("targeting", false);
d3
.select("#compositioncontainer")
.on("mousemove", null)
.on("mouseup", null);
tmp.remove();
d3
.selectAll(".fabulous")
.classed("fabulous", false);
}, 1);
});
}
function targetclick(tport, d) {
d3
.selectAll(".tmprelation")
.remove();
d3
.selectAll(".fabulous")
.classed("fabulous", false); // DRY
if (srcport) {
var tp = tport.attr("port");
var sp = srcport.attr("port");
var spd = srcport.datum(),
tpd = tport.datum();
//log("targetclick", spd, tpd);
var stype = spd.ptype == "cap"
? spd.portinfo.type.name
: spd.portinfo.capability.name;
var ttype = tpd.ptype == "cap"
? tpd.portinfo.type.name
: tpd.portinfo.capability.name;
// var stype = spd.type == "cap" ? spd.portinfo.type.name :
// spd.portinfo.target.name; var ttype = tpd.type == "cap" ?
// tpd.portinfo.type.name : tpd.portinfo.target.name; log("target",
// "stype="+stype, "ttype="+str(ttype));
var name = d.label;
if (spd.ptype != tpd.ptype && stype == ttype)
log("MATCH ", d);
else {
log("NOMATCH", d);
var mx = tport[0][0].getScreenCTM(); // fun!
var x = mx.e,
y = mx.f;
d3
.select("#problem")
.style("left", x)
.style("top", y)
.style("visibility", "visible")
.html("node/type mismatch");
setTimeout(function () {
d3
.select("#problem")
.style("visibility", "hidden");
}, 3000);
return;
}
// match success...
var port = d3.select(tport[0][0].parentNode);
port.classed("boundport", true);
port.classed("targetport", false);
var sp = srcport.datum(),
tp = tport.datum();
addlink(sp.parent, tp.parent, sp.name, tp.name);
srcport = false;
}
}
var edgelink = function () {
var curvature = .5;
function link(d) {
var spx = d.srcport.x,
spy = d.srcport.y,
tpx = d.targetport.x,
tpy = d.targetport.y;
var x0 = d.source.x + spx,
x1 = d.target.x + tpx,
y0 = d.source.y + spy,
y1 = d.target.y + tpy,
xi = d3.interpolateNumber(x0, x1),
yi = d3.interpolateNumber(y0, y1),
x2 = xi(curvature),
y2 = yi(curvature),
x3 = xi(1 - curvature),
y3 = yi(1 - curvature);
// if (isNaN(spx)) log("edgelink", d); fix curvature depending on whether port
// is on side, top, or bottom log(":: " + tpy + ", " + spy);
/*
if (tpy == 0) y3 = y1;
if (spy == 0) y2 = y0;
if (tpy != 0) x3 = x1;
if (spy != 0) x2 = x0;
*/
y3 = y1;
y2 = y0;
return "M" + x0 + "," + y0 + "C" + x2 + "," + y2 + " " + x3 + "," + y3 + " " + x1 + "," + y1;
}
link.curvature = function (_) {
if (!arguments.length)
return curvature;
curvature = +_;
return link;
};
return link;
};
var epath = edgelink();
var tpath = edgelink();
epath.curvature(0.3);
function node(name) {
return cc
.nodes
.find(function (n) {
return n.name == name;
});
}
function hascapability(n, cap) {
return n.capabilities && n
.capabilities
.find(function (c) {
return c.name == cap;
});
}
function addcomp(c, x, y) {
return txn("addcomp", function () {
return _addcomp(c, x, y);
});
}
function _addcomp(c, x, y) {
x = x || 0;
y = y || 0;
log("addcomp", c);
cc = c;
var nodes = {};
var links = [];
var newnodes = [];
if (!c.outputs)
c.outputs = []; // dummy node special case
if (!c.inputs)
c.inputs = [];
var outputs = c.outputs;
outputs.forEach(function (o) {
o.value = jsonparse(o.value);
});
var inputs = c.inputs;
c
.nodes
.forEach(function (n) {
n = _.clone(n);
outputs.forEach(function (o) {
_
.values(o)
.forEach(function (a) {
_
.values(a)
.forEach(function (m) {
if (m.forEach) {
m
.forEach(function (y) {
if (n.name == y) {
n.output = o;
}
});
}
});
});
});
if (c.policies) {
c
.policies
.forEach(function (po) {
if (!po.targets) {
return;
}
po
.targets
.some(function (potarget) {
if (n.name != potarget) {
return false;
}
log("ASSIGN POLICY", potarget, po);
if (!n.policies) {
n.policies = [];
}
var policy = JSON.parse(JSON.stringify(po));
n
.policies
.push(policy);
return true;
});
});
}
onCompositionEvent("init.properties.on.node", n);
// debugger;
if (n.properties) {
n
.properties
.forEach(function (p) {
if (!p.value && p.assignment) {
p.value = p.assignment.value || p["default"];
if (!p.value && p.assignment.input) {
p.value = p.assignment.input["default"];
}
//log("assignment value", p.name, p.value);
}
});
}
inputs
.forEach(function (i) {
// debugger;
if (n.properties)
n.properties.forEach(function (p) {
if (p.name == i.name && !p.value) {
p.value = i['default'];
log("INPUT", p.name, p.value);
}
});
}
);
var d = addnode(n, x, y);
newnodes.push(d);
nodes[n.name] = d;
d.model = n;
x += 50;
y += rand(50);
});
log(c.name);
log("inputs", inputs);
log("outputs", outputs);
//if (inputs && inputs.length > 0)
/*xhrpost("/composition.addinputs?cid="+cid, inputs);*/
//if (outputs && outputs.length > 0)
/*xhrpost("/composition.addoutputs?cid="+cid, outputs);*/
c
.nodes
.forEach(function (n) {
if (n.requirements) {
n
.requirements
.forEach(function (req) {
var tn = c
.nodes
.find(function (x) { // target node
return req.node == x.name;
});
if (tn) {
// find target capability from requirement
var tc = tn
.typeinfo
.capabilities
.find(function (cap) {
return req.capability.name === cap.type.name;
});
if (tc)
links.push({
n1: nodes[n.name],
sp: req.name,
n2: nodes[tn.name],
tp: tc.name
});
}
});
}
});
links.forEach(function (x) {
addlink(x.n1, x.n2, x.sp, x.tp);
});
sortinterfaces(); // HACK
return newnodes;
}
function unfade() {
d3
.selectAll(".node")
.classed("faded", false);
}
var dropzone1 = d3
.select("#compositioncontainer")
.node();
dropzone1.addEventListener('dragover', function (e) {
if (e.preventDefault)
e.preventDefault();
//e.dataTransfer.dropEffect = 'move'; return allowDropByType(e,'product');
return true;
});
dropzone1.addEventListener('dragenter', function (e) {
e.preventDefault();
this.className = "over"; // assumes react
});
dropzone1.addEventListener('dragleave', function (e) {
this.className = "";
});
function droplistener(e) {
compositionDraggingType = null;
e.preventDefault();
e.stopPropagation();
var data = JSON.parse(e.dataTransfer.getData('product'));
dropdata(data, e.clientX, e.clientY);
}
dropzone1.addEventListener('drop', droplistener);
var catalogprefix = catalogprefix || "";
function dropdata(data, x, y) {
//log("DROPDATA",data);
if (data.nodes) {
log("dropdata", 1);
// assuming incoming data blob is a composition template
fixcomp(data, function (c) {
var nodes = addcomp(c, x, y);
});
} else if (data.product) { // DEAD?
log("dropdata", 2);
addproduct(data.product, x, y);
} else if (data.uuid) { // self-contained catalog interface
log("dropdata", 3);
addproduct(data, x, y);
} else {
log("dropdata", 4);
// it's a single node
var n = data;
if (n.id == 0) { // dummy node special case
n.type = {
name: "NOTYPE"
};
addnode(n);
} else {
// replace with catalogtype TODO
if (x.type && x.type.name) {
xhrget(catalogprefix + "/type?" + n.type.name, function (resp) {
var ti = JSON.parse(resp);
n.typeinfo = ti;
addnode(n);
});
} else {
addnode(n);
}
}
}
return false;
}
// query type info for each node
function fixcomp(c, fn) {
var nn = c.nodes.length;
c
.nodes
.map(function (n) {
if (n.id === 0) { // dummy node special case
n.type = {
name: "NOTYPE"
};
if (--nn == 0)
fn(c);
}
else {
// replace with catalogtype TODO HACK n.type into n.type.name
if (typeof n.type == "string")
n.type = {
name: n.type
};
if (!n.name)
n.name = "foo";
catalogtype(n.id, n.type.name, function (resp) {
var ti = JSON.parse(resp);
if (n.typeinfo)
log("fixcomp - already have typeinfo");
else
n.typeinfo = ti;
if (--nn == 0)
fn(c);
}
);
}
});
}
function fixcomp0(c, fn) {
var nn = c.nodes.length;
c
.nodes
.map(function (n) {
if (n.id == 0) { // dummy node special case
n.type = {
name: "NOTYPE"
};
if (--nn == 0)
fn(c);
}
else {
// replace with catalogtype TODO
xhrget(catalogprefix + "/type?" + n.type.name, function (resp) {
var ti = JSON.parse(resp);
n.typeinfo = ti;
if (--nn == 0)
fn(c);
}
);
}
});
}
function setcomposition(c) {
c = JSON.parse(c);
d3
.select("#composition")
.html("\"" + c.name + "\"<br><span style='font-size: 60%'>(" + c.description + ")<br>" + c.composition + "</span>");
}
// stolen from bostock
function wrap(text, width) {
text
.each(function () {
var text = d3.select(this),
words = text
.text()
.split(/\s+/)
.reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.1, // ems
y = text.attr("y"),
dy = parseFloat(text.attr("dy")),
tspan = text
.text(null)
.append("tspan")
.attr("x", 0)
.attr("y", y)
.attr("dy", dy + "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text
.append("tspan")
.attr("x", 0)
.attr("y", y)
.attr("dy", ++lineNumber * lineHeight + dy + "em")
.text(word);
}
}
});
}
// filters go in defs element
var defs = svg.append("defs");
var filter = defs
.append("filter")
.attr("id", "drop-shadow")
.attr("x", "-50%")
.attr("y", "-50%")
.attr("width", "200%")
.attr("height", "200%");
// SourceAlpha refers to opacity of graphic that this filter will be applied to
// convolve that with a Gaussian with standard deviation 3 and store result in
// blur
filter
.append("feGaussianBlur")
.attr("in", "SourceAlpha")
.attr("stdDeviation", 7)
.attr("result", "blur");
// translate output of Gaussian blur to the right and downwards with 2px store
// result in offsetBlur
filter
.append("feOffset")
.attr("in", "blur")
.attr("dx", 2)
.attr("dy", 2)
.attr("result", "offsetBlur");
// overlay original SourceGraphic over translated blurred opacity by using
// feMerge filter. Order of specifying inputs is important!
var feMerge = filter.append("feMerge");
feMerge
.append("feMergeNode")
.attr("in", "offsetBlur")
feMerge
.append("feMergeNode")
.attr("in", "SourceGraphic");
function _serialsvg() {
svg
.append("style")
.html(css); // stick the stylesheet in
return new XMLSerializer().serializeToString(svg[0][0]);
// <object type="image/svg+xml" data="foo4.svg"> </object>
}
function serialsvg() {
// no, clone is poison var s = clone(svg);
s = svg;
// s.append("style").html(css); // stick the stylesheet in
return new XMLSerializer().serializeToString(s.node());
// <object type="image/svg+xml" data="foo4.svg"> </object>
}
function clone(s) {
var n = s.node();
return d3.select(n.parentNode.insertBefore(n.cloneNode(true), n.nextSibling));
}
// EXTERNAL / OUTBOUND APIs window.addEventListener('load', function(){
// },false);
if (typeof ascIceServerGet == 'function') {
xhrget = function (url, callback) {
console.log("ascIceServerGet", url);
ascIceServerGet(url, function (responseText) {
if (callback)
callback(responseText);
}
);
}
xhrpost = function (url, obj, callback, type) {
console.log("ascIceServerPost", url, obj, type);
ascIceServerPost(url, JSON.stringify(obj), function (responseText) {
if (callback)
callback(responseText);
}
, type);
}
xhrpostraw = function (url, obj, callback, type) {
// console.log("ascIceServerPost raw", url, obj, type);
ascIceServerPost(url, obj, function (responseText) {
if (callback)
callback(responseText);
}
, type || "text/plain");
}
}
function onCompositionEvent(compositionEvent, compositionElement) {
if (typeof onEventFromComposition === 'function') {
onEventFromComposition(compositionEvent, compositionElement);
}
}
function setCompositionDropZone(type, yesno) {
compositionDraggingType = (yesno === true
? type
: null);
switch (type) {
case 'product':
d3
.select("#compositiondiv")
.selectAll(".compositioncontainer")
.classed("highlight", !!yesno);
break;
case 'location':
d3
.select("#compositiondiv")
.selectAll(".asc_nodes_Site")
.classed("highlight", !!yesno);
break;
}
}
function clearComposition() {
graph
.selectAll(".node")
.each(function (d) {
removenode(d);
});
}
var composition_version = {
revision: "revision: 2568",
lastmod: "last modified: Thu Sep 21 13:22:37 2017"
};
// log(composition_version); extern
window.xhrget = xhrget;
window.configclose = configclose;
window.dropdata = dropdata;
// log("starting composition inside " + window.location.host + " " +
// JSON.stringify(composition_version));
};
setTimeout(function () {
window.comp = new CompositionEditor();
$('#composition-loader').hide();
}, 2000);
//
// ─── CONTROLLERS
// ────────────────────────────────────────────────────────────────
//
/**
* Represents a controller that connects DOM operations with services. Comp(ostion)Controller
* @param {ApiService} apiService - service that handles api calls
* @requires jquery
* @requires bootstrap-modal
*/
function CompController(apiService) {
/* Private members */
var self = this,
loaderElement = $('#composition-loader');
/* Public members */
/**
* Saves a given composition, up to 3 attempts are made in case of failure
* UI interactions:
* - loader showing until request returns
* - notification on success
* - modal on failure
* @param {Object} composition
*/
self.saveComposition = function (composition) {
loaderElement.show();
return attempt(3, function () {
return apiService.saveComposition(composition.cid, composition);
})
.then(function (response) {
console.log(response);
composition.cid = response.uuid;
notifySuccess('Composition saved', 'saveMsg');
})
.fail(function (jqXHR) {
console.error('SaveComposition failed %o', jqXHR.responseJSON.notes);
var tempError = Object
.keys(jqXHR.responseJSON.requestError)
.map(function (key) {
return jqXHR.responseJSON.requestError[key];
});
var message = (jqXHR.responseJSON !== undefined)
? tempError[0].formattedErrorMessage // use response when it's not in JSON format
: 'Internal server error - unable to save composition.';
alertError(message);
})
.always(function () {
loaderElement.hide();
window
.sdc
.notify('ACTION_COMPLETED');
});
};
/**
* Creates a blueprint
* UI interactions:
* - loader showing until request returns
* - notification on success
* - modal on failure
* @param {String} component_Id
* @param {String} serviceuuid
* @param {String} vnfiname
* @param {String} mt - flow-type
*/
self.createBlueprint = function (component_Id, serviceuuid, vnfiname, mt) {
loaderElement.show();
apiService
.createBlueprint(component_Id, serviceuuid, vnfiname, mt)
.then(function (response) {
console.log('create blueprint response body: %o', response);
notifySuccess('Blueprint Created', 'submitMsg');
})
.fail(function (jqXHR) {
console.error('Create blueprint failed %o', jqXHR.responseJSON.notes);
var tempError = Object
.keys(jqXHR.responseJSON.requestError)
.map(function (key) {
return jqXHR.responseJSON.requestError[key];
});
var message = (jqXHR.responseJSON !== undefined)
? tempError[0].formattedErrorMessage
// use response when it's not in JSON format
: 'Internal server error: unable to create blueprint';
alertError(message);
})
.always(function () {
loaderElement.hide();
});
};
}
//
// ─── SERVICES
// ───────────────────────────────────────────────────────────────────
//
/**
* Represents a service that handles api calls
* (!) Do not make any UI interactions or DOM changes in this context - use the controller for that
* @constructor
* @param {String} baseUrl
* @param {String} userId
*/
function ApiService(baseUrl, userId) {
/* Private members */
var self = this,
headers = {
'Content-Type': 'text/plain;charset=UTF-8',
'Access-Control-Allow-Origin': '*',
'USER_ID': userId
};
function post(path, data) {
var deferred = $.Deferred();
$.ajax({
type: 'POST',
url: baseUrl + path,
data: JSON.stringify(data),
headers: headers
})
.then(function () {
// connect deferred with ajax on success
return deferred
.resolve
.apply(null, arguments);
})
.fail(function (jqXHR) {
// when no response show server-unavilable
jqXHR.responseText = jqXHR.responseText || 'Server Unavailable';
// connect deferred with ajax on failure
return deferred.reject(jqXHR);
});
return deferred;
}
/* Public members */
self.saveComposition = function (cid, data) {
return post('/saveComposition/' + cid, data);
};
self.createBlueprint = function (componentId, serviceUuid, vfniName, mt) {
var path = ['/createBluePrint', componentId, serviceUuid, vfniName, mt].join('/');
return post(path, null);
};
}
//
// ─── UTILS
// ──────────────────────────────────────────────────────────────────────
//
/**
* Attempt to perform an given action (deferredFunc)
* @param {Integer} maxAttempts - maximum number of retries
* @param {Function} deferredFunc - function that returns deferred object (jquery promise object)
* @returns {jquery Deferred object} - see api at https://api.jquery.com/category/deferred-object/
* @requires jquery
* @example
* // prints 'error' if GET request to 'http://some-url' failed 3 times
* // prints 'success' if one of the attempts was successful
* attempt(3, () => $.get('http://some-url'))
* .then(() => console.log('success'))
* .fail(() => console.log('error'))
*/
function attempt(maxAttempts, deferredFunc) {
var promise = $.Deferred(),
errorArgs = null;
function recurse(attemptsLeft) {
if (attemptsLeft < 1) {
// fail when no more attempts left
promise
.reject
.apply(null, errorArgs);
} else {
deferredFunc()
.then(function () {
return promise
.resolve
.apply(null, arguments);
})
.fail(function () {
errorArgs = arguments;
recurse(attemptsLeft - 1); // retry on fail
});
}
}
recurse(maxAttempts);
return promise;
}
/**
* Displays success notification on the bottom of the screen
* Will auto-close after 5 secs
* @param {String} message
* @param {String} testId - id for selenium tests
* @requires jquery
* @requires remarkable-bootstrap-notify
*/
function notifySuccess(message, testId) {
var template = $('<span>')
.attr('data-tests-id', testId)
.text(message)
.prop('outerHTML') // stringify the element
$.notify({
// options
message: template,
icon: 'glyphicon glyphicon-ok' // v icon
}, {
// settings
type: 'success',
delay: 5000, // auto-close after 5sec
placement: {
from: 'bottom'
}
});
}
/**
* Displays alert modal
* @param {String} message
* @requires bootstrap
*/
function alertError(message) {
$('#alert-modal')
.modal('show')
.find('.modal-body')
.text(message);
}