diff options
author | huangjian <huang.jian12@zte.com.cn> | 2016-08-31 16:47:33 +0800 |
---|---|---|
committer | huangjian <huang.jian12@zte.com.cn> | 2016-08-31 16:47:33 +0800 |
commit | fa49e78cc199526a9e33b59c5194f8e3bf0f0952 (patch) | |
tree | 3478e867a8f304266dbceca6e992cceca410ede4 /winery/org.eclipse.winery.topologymodeler/src/main/webapp/js | |
parent | 159d40f0011559c8f82338b29dca1bffd700f2c8 (diff) |
Add winery source code
Change-Id: I1c5088121d79b71098c3cba1996c6f784737532e
Issue-id: TOSCA-49
Signed-off-by: huangjian <huang.jian12@zte.com.cn>
Diffstat (limited to 'winery/org.eclipse.winery.topologymodeler/src/main/webapp/js')
12 files changed, 2226 insertions, 0 deletions
diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/artifacttemplateselection.js b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/artifacttemplateselection.js new file mode 100644 index 0000000..7b7f20b --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/artifacttemplateselection.js @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2012-2013 University of Stuttgart. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Oliver Kopp - initial API and implementation and/or initial documentation + *******************************************************************************/ + + // also loaded from the repository + +// TODO: winery-common should be required -> encodeID; typing could be required (but that is no AMD module) +define([], function() { + $("#artifactTemplateName").typing({ + start: function(event, $elem) { + flagArtifactTemplateNameAsUpdating(); + }, + stop: function(event, $elem) { + checkArtifactTemplateName(); + } + }); + + $("#artifactTemplateNS").on("blur", checkArtifactTemplateName).on("change", checkArtifactTemplateName).on("focus", flagArtifactTemplateNameAsUpdating); + + var repositoryURL; + + return { + setRepositoryURL: function(url) { + repositoryURL = url; + }, + checkArtifactTemplateName: checkArtifactTemplateName, + flagArtifactTemplateNameAsUpdating: flagArtifactTemplateNameAsUpdating + }; + + function checkArtifactTemplateName() { + var ns = $("#artifactTemplateNS").val(); + var name = $("#artifactTemplateName").val(); + var url = repositoryURL + "/artifacttemplates/" + encodeID(ns) + "/" + encodeID(name) + "/"; + if (name == "") { + var valid = false; + var invalidReason = "No name provided"; + setValidityStatus(valid, invalidReason); + } else { + $.ajax(url, { + type: 'HEAD', + dataType: 'html', + error: function(jqXHR, textStatus, errorThrown) { + if (jqXHR.status == 404) { + // artifact template does not exist: everything is allright + setValidityStatus(true, null); + } else { + setValidityStatus(false, textStatus); + } + }, + success: function(data, textStatus, jqXHR) { + setValidityStatus(false, "artifact template already exists"); + } + }); + } + } + + function flagArtifactTemplateNameAsUpdating() { + $("#artifactTemplateNameIsValid").removeClass("invalid").removeClass("valid").addClass("unknown"); + $("#artifactTemplateNameIsInvalidReason").text(""); + } + + function setValidityStatus(valid, invalidReason) { + $("#artifactTemplateNameIsValid").removeClass("unknown"); + if (valid) { + $("#artifactTemplateNameIsValid").addClass("valid"); + $("#artifactTemplateNameIsInvalidReason").text("Ok"); + } else { + $("#artifactTemplateNameIsValid").addClass("invalid"); + $("#artifactTemplateNameIsInvalidReason").text(invalidReason); + } + } + + +});
\ No newline at end of file diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/jquery.fileupload-audio.js b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/jquery.fileupload-audio.js new file mode 100644 index 0000000..20e2cde --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/jquery.fileupload-audio.js @@ -0,0 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2012-2013 University of Stuttgart. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Oliver Kopp - initial API and implementation and/or initial documentation + *******************************************************************************/ + +// dummy as we don't need the functionality, but jquery.fileupload-ui.js requires it
\ No newline at end of file diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/jquery.fileupload-image.js b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/jquery.fileupload-image.js new file mode 100644 index 0000000..20e2cde --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/jquery.fileupload-image.js @@ -0,0 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2012-2013 University of Stuttgart. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Oliver Kopp - initial API and implementation and/or initial documentation + *******************************************************************************/ + +// dummy as we don't need the functionality, but jquery.fileupload-ui.js requires it
\ No newline at end of file diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/jquery.fileupload-validate.js b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/jquery.fileupload-validate.js new file mode 100644 index 0000000..20e2cde --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/jquery.fileupload-validate.js @@ -0,0 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2012-2013 University of Stuttgart. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Oliver Kopp - initial API and implementation and/or initial documentation + *******************************************************************************/ + +// dummy as we don't need the functionality, but jquery.fileupload-ui.js requires it
\ No newline at end of file diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/jquery.fileupload-video.js b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/jquery.fileupload-video.js new file mode 100644 index 0000000..20e2cde --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/jquery.fileupload-video.js @@ -0,0 +1,13 @@ +/******************************************************************************* + * Copyright (c) 2012-2013 University of Stuttgart. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Oliver Kopp - initial API and implementation and/or initial documentation + *******************************************************************************/ + +// dummy as we don't need the functionality, but jquery.fileupload-ui.js requires it
\ No newline at end of file diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/winery-common-topologyrendering.js b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/winery-common-topologyrendering.js new file mode 100644 index 0000000..fa37897 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/winery-common-topologyrendering.js @@ -0,0 +1,143 @@ +/******************************************************************************* + * Copyright (c) 2012-2013 University of Stuttgart. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Oliver Kopp - initial API and implementation and/or initial documentation + *******************************************************************************/ +/** + * This file contains supporting functions for the rendering a topology template + */ +define( + ["jsplumb", "winery-support-common"], + function (globdefa, wsc) { + var readOnly = false; + + var module = { + initNodeTemplate: initNodeTemplate, + handleConnectionCreated: handleConnectionCreated, + imageError: imageError, + setReadOnly: setReadOnly + }; + + return module; + + /** + * @param nodeTemplateShape the set of node template shapes to initialize + * @param makeDraggable true if the nodeTemplates should be made draggable + */ + function initNodeTemplate(nodeTemplateShapeSet, makeDraggable) { + if (makeDraggable) { + jsPlumb.draggable(nodeTemplateShapeSet); + } + jsPlumb.makeTarget(nodeTemplateShapeSet, { + anchor:"Continuous", + endpoint:"Blank" + }); + + // this function is defined in index.jsp via jsp functions + // as it depends on the available relationship types + createConnectorEndpoints(nodeTemplateShapeSet); + + nodeTemplateShapeSet.addClass("layoutableComponent"); + + nodeTemplateShapeSet.each(function(idx, s) { + var shape = $(s); + + var id = shape.attr("id"); + + // KV Properties + var props = shape.children(".propertiesContainer") + .children(".content") + .children("table") + .children("tbody"); + if (!readOnly) { + props.find(".KVPropertyValue").editable(); + } + + // Deployment Artifacts + var fu = shape.children(".deploymentArtifactsContainer") + .children(".content") + .children(".addnewartifacttemplate") + .children(".fileupload"); + fu.attr("data-url", "nodetemplates/" + wsc.encodeId(id) + "/deploymentartifacts/"); + }); + + // nodeTemplateShapeSet.children(".deploymentArtifactsContainer").children(".content").children(".deploymentArtifact").each(function(index, e) { + // addnewfileoverlay could be added here + // $(this). + // }); + } + + /** + * Handles the creation of connections by jsPlumb + * + * Also called if connection is created during loading + */ + function handleConnectionCreated(data) { + // might be called directly from here or by the event + // if called by jsPlumb infrastructure, we have to get rid of the surrounding element + var conn; + if (data.connection) { + conn = data.connection; + } else { + conn = data; + } + winery.debugConnData = conn; + + var id = conn.id; + winery.connections[id] = { + // we store the id to have a default for the id + id: id, + // and use it also as starting point of a name + name: id, + // we do NOT copy the plain type here + // type is stored in the connection + // type: .getType()[0] + // BUT: we copy the detailed ns and id + nsAndLocalName: wsc.getNamespaceAndLocalNameFromQName(conn.getType()[0]) + }; + putToolTipInfo(conn); + + // we have to manually show and hide the tooltips as Bootstrap's tooltip plugin does not work after a connection was highlighted. + conn.bind("mouseenter", function(conn,e) { + putToolTipInfo(conn); + }); + conn.bind("mouseexit", function(conn,e) { + $("div.tooltip").remove(); + // we have to replace the tooltip as + putToolTipInfo(conn); + }); + } + + function putToolTipInfo(conn) { + // add tooltip showing the relationship type + var svgElement = $(conn.canvas); + // the title attribute is shown in the tooltip + // set the relationship type as tooltip + // we show the localname only + var nsAndLocalName = winery.connections[conn.id].nsAndLocalName; + // Vino4TOSCA: type in brackets + var title = "(" + nsAndLocalName.localname + ")"; + svgElement.tooltip({title: title}); + } + + /** + * Removes the image from the display. Used at images which could not be loaded + * + * Used via {@code <img onerror="imageError(this);" ... />} + */ + function imageError(image) { + image.onError=""; + image.style.visibility = "hidden"; + } + + function setReadOnly() { + readOnly = true; + } + } +);
\ No newline at end of file diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/winery-common.js b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/winery-common.js new file mode 100644 index 0000000..434bec3 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/winery-common.js @@ -0,0 +1,265 @@ +/******************************************************************************* + * Copyright (c) 2012-2013 University of Stuttgart. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Oliver Kopp - initial API and implementation and/or initial documentation + *******************************************************************************/ + +/** +This .js file is shared between the Winery Repository and the Winery Topology Modeler + +This should be rewritten as AMD module. A start is made in winery-support-common.js. + +vConfirmYesNo is defined in jsp/shared/dialogs.jsp +*/ + +/** + * Highlights fields, which are required but not filled out by the user. + * The check for required fields is done via the CSS class "required" + * + * @return true if a required field is not filled with a value + */ +function highlightRequiredFields() { + var requiredFieldMissing = false; + // includes check input field attribute "required" of HTML5: http://www.w3.org/TR/html-markup/input.radio.html#input.radio.attrs.required + $("input.required:visible:enabled, input[required]:visible").each(function(){ + if ($(this).val() == '') { + $(this).parent().addClass('has-warning'); + requiredFieldMissing = true; + } else { + $(this).parent().removeClass('has-warning'); + } + }); + return requiredFieldMissing; +} + +function vPnotify(text, title, type) { + require(["pnotify"], function() { + var notice = $.pnotify({ + title: title, + text: text, + type: type + }); + notice.on("click", function(e) { + var target = $(e.target); + if (target.is(".ui-pnotify-closer *, .ui-pnotify-sticker *, a")) { + // buttons clicked - call their functionality + return true; + } else { + // click on text leads to display of a dialog showing the complete content + + var textDiv; + if (target.is("div.ui-pnotify-text")) { + textDiv = target; + } else { + textDiv = target.closest("div.ui-pnotify-container").find("div.ui-pnotify-text"); + } + + // put text into dialog and show it + $("#diagmessagetitle").text("Full notification"); + $("#diagmessagemsg").html(textDiv.html()); + $("#diagmessage").modal("show"); + + return false; + } + }); + }); +} + +/** + * @param title optional title + */ +function vShowError(text, title) { + vPnotify(text, title, "error"); +} + +function vShowAJAXError(msg, jqXHR, errorThrown) { + vShowError(msg + "<br />" + errorThrown + "<br/>" + jqXHR.responseText); +} + +/** + * @param title optional title + */ +function vShowNotification(text, title) { + vPnotify(text, title, "notification"); +} + + +/** + * @param title optional title + */ +function vShowSuccess(text, title) { + vPnotify(text, title, "success"); +} + +/** + * Deletes the given resource with confirmation. + * if deletion fails, an error message is shown + * + * @param onSuccess: function(data, textStatus, jqXHR) to call if deletion has been successful + * @param onError: function(jqXHR, textStatus, errorThrown) called if deletion lead to an error (optional) + * @param onStart: function() called if user has agreed to delete resource (optional) + * @param withoutConfirmation if given, the resource is deleted without any confirmation + */ +function deleteResource(nameOfResource, url, onSuccess, onError, onStart, withoutConfirmation) { + var f = function() { + $.ajax({ + url: url, + type: 'DELETE', + async: true, + error: function(jqXHR, textStatus, errorThrown) { + vShowAJAXError("Could not delete " + nameOfResource, jqXHR, errorThrown); + if (onError) onError(); + }, + success: function(data, textStatus, jqXHR) { + vShowSuccess("Successfully deleted " + nameOfResource); + onSuccess(data, textStatus, jqXHR); + } + }); + }; + if (withoutConfirmation) { + f(); + } else { + vConfirmYesNo("Do you really want to delete " + nameOfResource + "?", f); + } +} + +/** + * Function to create a td with two columns: one for a key and one for a value + * + * Has to be called from a td element: This function uses $(this) to determine the td + * It changes the content of the td to an input field. In case the input was updated, it is POSTed to the given URL + * + * TODO: The editing mode should use x-editable instead of a self-written input handling + * + * @param url the URL to post the key/value pair to + * @param keyName the keyname of the key - used at POST with <keyName>=<newKey> + * @param valueName the keyname of the value - used at POST with <valueName>=<newValue> + * + */ +function vCreateTdClickFunction(url, keyName, valueName) { + var inputId = "thingedit" + Math.floor((Math.random()*100)+1); ; + keyName = keyName || "key"; + valueName = valueName || "value"; + + var f = function(e) { + var input = $("#" + inputId); + if (input.length != 0) { + // input field already there + return; + } + + var td = $(this); + var oldPrefix = td.text(); + var html = "<input id='" + inputId + "' value='" + oldPrefix + "'></input>"; + td.html(html); + + // new field generated, has to be looked up + input = $("#" + inputId); + + input.keydown(function(e) { + if (e.keyCode == 27) { + // ESC key pressed + input.off("blur"); + td.html(oldPrefix); + } else if (e.keyCode == 13) { + // enter key pressed + input.trigger("blur"); + } + }); + + input.focus(); + + input.on("blur", function() { + var newPrefix = input.val(); + if (newPrefix == oldPrefix) { + td.html(newPrefix); + } else { + var namespace = td.next().text(); + newPrefixEscaped = escape(newPrefix); + namespaceEscaped = escape(namespace); + var data = keyName + "=" + newPrefixEscaped + "&" + valueName + "=" + namespaceEscaped; + $.ajax({ + url: url, + type: "POST", + async: false, + data: data, + error: function(jqXHR, textStatus, errorThrown) { + vShowAJAXError("Could not update data", jqXHR, errorThrown); + input.focus(); + }, + success: function(data, textSTatus, jqXHR) { + vShowSuccess("Successfully updated data"); + td.html(newPrefix); + } + }); + } + }); + }; + + return f; +} + +/** + * This function is also availble at winery-support-common.js + * This function here is due to legacy reasons and all callers should move to winery-support-common.js + * + * @param qname a QName in the form {namespace}localname + * @return { namespace: namespace, localname: localname } + */ +function getNamespaceAndLocalNameFromQName(qname) { + var i = qname.indexOf("}"); + var res = { + namespace : qname.substr(1,i-1), + localname : qname.substr(i+1) + }; + return res; +} + +/** + * Converts a QName of the form {namespace}id to the form prefix:id + * with a lookup of the prefix in the element + * + * returns wrong data if prefix wsa not found + */ +function getQNameOutOfFullQName(fullQname, element) { + var nsAndId = getNamespaceAndLocalNameFromQName(fullQname); + var prefix = element.lookupPrefix(nsAndId.namespace); + var qname = prefix + ":" + nsAndId.localname; + return qname; +} + +/** + * Converts a QName of the form prefix:id to the form {namespace}id + * with a lookup of the prefix in the element + * + * Currently not used anywhere + */ +function getFullQNameOutOfQName(qname, element) { + var i = qname.indexOf(":"); + var prefix = qname.substr(0,i-1); + var localname = qname.substr(i+1); + var namespace = element.lookupPrefix(prefix); + return "{" + namespace + "}" + localname; +} + +function encodeID(id) { + // the URL sent to the server should be the encoded id + id = encodeURIComponent(id); + // therefore, we have to encode it twice + id = encodeURIComponent(id); + return id; +} + +// URLs from QName are provided by winery-support-common.js +function makeArtifactTemplateURL(repoURL, namespace, id) { + return repoURL + "/artifacttemplates/" + encodeID(namespace) + "/" + encodeID(id) + "/"; +} + +if (!window.winery) window.winery = {}; +window.winery.BOOTSTRAP_ANIMATION_DURATION = 400; diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/winery-sugiyamaLayouter.js b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/winery-sugiyamaLayouter.js new file mode 100644 index 0000000..fa63f56 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/winery-sugiyamaLayouter.js @@ -0,0 +1,634 @@ +/******************************************************************************* + * Copyright (c) 2012-2013 University of Stuttgart. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Kálmán Képes - initial API and implementation and/or initial documentation + *******************************************************************************/ + +/** + * Implements abstractLayouter#layout and uses the Sugiyama method to apply a + * layered layout for TOSCA Topology Template graphs. The Sugiyama method + * proposes four steps to apply the layout:<br> + * First: Remove the cycles temporarly of the given graph by finding edges that + * are members of a cycle and reverse them<br> + * Second: Layer the nodes by using DFS/BFS and reduce the "height"/"width" of + * the graph<br> + * Third: Reduce the crossings of edges, by applying a suitable heuristic <br> + * Fourth: Assign the coordinates for the nodes/edges + * + * @param {Array} + * nodeTemplates, an array with div-elements of class + * ".NodeTemplateShape" + */ +define(function() { + + var module = { + layout: layout + }; + return module; + + function layout(nodeTemplates) { + + + // Step 1: "Remove" Cycles + var hasCycle = _hasCycle(nodeTemplates); + var complementEdgesOfSubgraph = null; + if (hasCycle){ + var edgesOfAcyclicSubgraph = _computeAcyclicSubgraph(nodeTemplates); + complementEdgesOfSubgraph = _computeEdgeComplement(nodeTemplates, + edgesOfAcyclicSubgraph); + _reverseEdges(complementEdgesOfSubgraph); + } + + // Step 2: Assign Layers + _assignLayers(nodeTemplates); + + // Step 3: reduce crossing of edges + _reduceCrossing(nodeTemplates); + + // Step 4: assign coordinates + _assignCoordinates(nodeTemplates); + + if(hasCycle){ + _reverseEdges(complementEdgesOfSubgraph); + } + + jsPlumb.repaintEverything(); + // if used twice, edges are drawn straight and not random + jsPlumb.repaintEverything(); + + // cleanup -> remote internal data + $(nodeTemplates).removeAttr("layer"); + $(nodeTemplates).removeAttr("layerPos"); + + } + + + function callsOnLayout(nodeTemplates) { + // TODO start sugiyama layout method with special handling of calls-on + // relations + } + + /** + * Returns true if the given graph contains a cycle + * @param nodeTemplates the vertices of the graph, where cycle detection should be performed. The vertices should be an array of NodeTemplates + * @returns {Boolean} true iff a cycle was detected in the graph, else false + */ + function _hasCycle(nodeTemplates) { + for(var i = 0; i < nodeTemplates.length;i++){ + nodeTemplates[i].cycleMark = "notStarted"; + } + + var hasCycle = false; + var sources = _returnSources(nodeTemplates); + + for(var i = 0; i < sources.length; i++){ + _cycleSearch(sources[i],hasCycle); + if(hasCycle){ + // remove marks + $(nodeTemplates).removeAttr("cycleMark"); + return hasCycle; + } + } + + return hasCycle; + } + + var tarjanCounter = 1; + + /** + * Detects cycles which are reachable by the given NodeTemplate of a graph. Algorithm is based on DFS + * @param nodeTemplate the starting NodeTemplate for the DFS + * @param hasCycle boolean variable to propagate if a cycle was found, init value should be 'false' + */ + function _cycleSearch(nodeTemplate,hasCycle){ + if(nodeTemplate.cycleMark = "inWork"){ + hasCycle =true; + } else if(nodeTemplate.cycleMark ="notStarted") { + nodeTemplate.cycleMark ="inWork"; + var successors = _returnSuccessors(nodeTemplate); + for(var i = 0 ; i < successors.length; i++){ + _cycleSearch(successors[i]); + } + nodeTemplate.cycleMark ="finished"; + } + + } + + /** + * Calculates strongly connected components according to Tarjans algorithm + * @param nodeTemplate a NodeTemplate that is used to begin the algorithm on + * @param foundCycle boolean variable to propagate if a cycle was found, init value should be 'false' + */ + function _findComponent(nodeTemplate, foundCycle){ + if(nodeTemplate.cycleMark == "inWork"){ + foundCycle = true; + } else if(nodeTemplate.cycleMark == "notStarted"){ + nodeTemplate.cycleMark = "inWork"; + nodeTemplate.dfsNum = tarjanCounter; + nodeTemplate.compNum = tarjanCounter; + tarjanCounter++; + var successors = _returnSuccessors(nodeTemplate); + for(var i = 0; i < successors.length;i++){ + var successor = successors[i]; + if(successor.cycleMark != "finished"){ + _findComponent(successor,foundCycle); + if(successor.compNum < nodeTemplate.compNum){ + nodeTemplate.compNum = successor.compNum; + } + } + } + nodeTemplate.cycleMark = "finished"; + } + } + + /** + * Assigns x/y-Coordinates to the given NodeTemplates. It is assumed that the + * NodeTemplates are already layered, e.g. nodeTemplate.layer, + * nodeTemplate.layerPos is set. + * + * @param nodeTemplates + * the nodetemplate to set it's x/y coordinates + */ + function _assignCoordinates(nodeTemplates) { + // get the layers out of the graph and the number of layers + var highestLayerNumber = _returnHighestLayerNumber(nodeTemplates); + var layerMap = {}; + for ( var i = 1; i <= highestLayerNumber; i++) { + layerMap[i.toString()] = _returnLayer(nodeTemplates, i); + } + + // determine the layer with the greatest width, width = {max(|U|) : U is + // layer of V} + var widestLayerWidth = -1; + for (layer in layerMap) { + if (layer.length > widestLayerWidth) { + widestLayerWidth = layer.length; + } + } + + // get maximum node width + var maxNodeWidth = _getMaxWidthOfNodeArray(nodeTemplates); + + // get maximum node height + var maxNodeHeight = _getMaxHeightOfNodeArray(nodeTemplates); + + var editorArea = $("#editorArea")[0]; + var height = editorArea.clientHeight; + var width = editorArea.clientWidth; + + // TODO fine tuning or change the algorithm to handle callsOn Relations + // independently, e.g. draw it in an own tree/list/graph + // distance in height between nodes + var prettyHeight = 75; + for ( var i = highestLayerNumber; i > 0; i--) { + var layer = _returnLayer(nodeTemplates, i); + // distance in width between nodes + var prettyWidth = 75; + for ( var nodeIndex = 0; nodeIndex < layer.length; nodeIndex++) { + var layerNumber = i; + var layerPos = layer[nodeIndex].layerPos; + layer[nodeIndex].style.left = ((layerPos) * maxNodeWidth + prettyWidth) + + "px"; + layer[nodeIndex].style.top = ((highestLayerNumber - layerNumber) + * maxNodeHeight + prettyHeight) + + "px" + prettyWidth += 50; + } + prettyHeight += 100; + } + } + + /** + * Returns the height of the nodeTemplate with the highest height + * @param nodeTemplates an array of NodeTemplates + * @returns {Number} the maximum height of the given NodeTemplates + */ + function _getMaxHeightOfNodeArray(nodeTemplates) { + var maxHeight = -1; + for ( var i = 0; i < nodeTemplates.length; i++) { + // TODO clientHeight responds to the complete page, not only the + // nodetemplate + if (maxHeight < nodeTemplates[i].clientHeight) { + maxHeight = nodeTemplates[i].clientHeight; + } + } + return maxHeight; + } + + /** + * Returns the width of the nodeTemplate with the highest width + * @param nodeTemplates an array of NodeTemplates + * @returns {Number} the maximum width of the given NodeTemplates + */ + function _getMaxWidthOfNodeArray(nodeTemplates) { + var maxWidth = -1; + for ( var i = 0; i < nodeTemplates.length; i++) { + // TODO clientWidth responds to the complete page, not only the + // nodetemplate + if (maxWidth < nodeTemplates[i].clientWidth) { + maxWidth = nodeTemplates[i].clientWidth; + } + } + return maxWidth; + } + + /** + * Reduces crossing between the already layered Graph, given as a Array of NodeTemplates + * @param nodeTemplates the vertices of the layered graph, to reduce crossing between the layers + */ + function _reduceCrossing(nodeTemplates) { + // initialize arbitrary orderings inside layers + _initLayerOrder(nodeTemplates); + var maxlayerlevel = _returnHighestLayerNumber(nodeTemplates); + // variable to reduce crossings of layer-pairs one by one + var layerCount = 1; + // variable to check if some change was made + var changed = true; + while (layerCount < maxlayerlevel | changed) { + // the algorithm should run until no position changes are made + if (layerCount == maxlayerlevel) { + layerCount = 1; + } + changed = false; + + var layer = _returnLayer(nodeTemplates, layerCount + 1); + + // INFO: This piece of code reduces the corssing according to the + // barycenter heuristic + for ( var i = 0; i < layer.length; i++) { + // deg(u), where u = layer[i] + var deg_u = _returnIncomingEdges(layer[i]).length + + _returnOutgoingEdges(layer[i]).length; + + // sum of layerpos(v), for ever v where (u,v) exists in the graph + var outgoingEdges = _returnOutgoingEdges(layer[i]); + var sum = 0.0; + for ( var j = 0; j < outgoingEdges.length; j++) { + var node_v = outgoingEdges[j].target; + sum = sum + node_v.layerPos; + } + + // barycenter heuristic + var bary_u = 1 / deg_u * sum; + layer[i].barycenter = bary_u; + } + + // sorting the nodetemplates in the layer accorindg to the barycenter + layer.sort(function(a, b) { + return a.barycenter - b.barycenter + }); + + // rearrange positions in layer + for ( var i = 0; i < layer.length; i++) { + if (layer[i].layerPos != i + 1) { + // checks whether some change of the position is made + changed = true; + } + layer[i].layerPos = i + 1; + } + + // get ready for the next layer + layerCount++; + } + } + + /** + * Returns an array of NodeTemplates which are in the same Layer (nodeTemplate.layer) + */ + function _returnLayer(nodeTemplates, layerNumber) { + var layer = new Array(); + for ( var i = 0; i < nodeTemplates.length; i++) { + if ("layer" in nodeTemplates[i] + && nodeTemplates[i].layer == layerNumber) { + layer.push(nodeTemplates[i]); + } + } + layer.sort(function(a, b) { + return a.layerPos - b.layerPos; + }); + return layer; + } + + /** + * Returns the number of the highest layer of the given layered graph + * @param nodeTemplates the vertices of the layered graph as an array of NodeTemplates + * @returns {Number} the highest layer number + */ + function _returnHighestLayerNumber(nodeTemplates) { + var layerNumber = -1; + for ( var i = 0; i < nodeTemplates.length; i++) { + if (!"layer" in nodeTemplates[i]) { + // some node has no "layer" property defined <=> graph isn't layered + return -1; + } else { + if (nodeTemplates[i].layer > layerNumber) { + layerNumber = nodeTemplates[i].layer; + } + } + } + return layerNumber; + } + + /** + * Initializes a layering order in each layer of the given graph + * @param nodeTemplates the vertices of the graph to layer, as an array of NodeTemplates + */ + function _initLayerOrder(nodeTemplates) { + // "map" := layerNumber : ordernumber + var orderingCount = {}; + // init with 1 : 1 + orderingCount["1"] = 1; + for ( var i = 0; i < nodeTemplates.length; i++) { + // get layer number of the nodetemplate + var layerNumber = nodeTemplates[i].layer; + // is the layer already in the map ? + if (layerNumber.toString() in orderingCount) { + // if yes, set the pos inside layer and increment for next node + nodeTemplates[i].layerPos = orderingCount[layerNumber.toString()]; + orderingCount[layerNumber.toString()] = orderingCount[layerNumber + .toString()] + 1; + } else { + // if not, set the pos to 1 and add layer to map with count 2 + nodeTemplates[i].layerPos = 1; + // init pos for next node + orderingCount[layerNumber.toString()] = 2; + } + } + } + + /** + * Assigns layer numbers to the given graph + * @param nodeTemplates the vertices of the graph to assign layers to, as an array of NodeTemplates + */ + function _assignLayers(nodeTemplates) { + var sinks = {}; + for ( var i = 0; i < nodeTemplates.length; i++) { + if (_returnOutgoingEdges(nodeTemplates[i]).length == 0) { + sinks[i] = nodeTemplates[i]; + } + } + + // array of DIV delements + var nodesToCompute = new Array(); + + for ( var sink in sinks) { + // set layer level for the sinks and add the nodes which are reachable + // from there + sinks[sink].layer = 1; + var inEdges = _returnIncomingEdges(sinks[sink]); + for ( var i = 0; i < inEdges.length; i++) { + nodesToCompute.push(inEdges[i].source); + } + } + + // set other layer levels + while (nodesToCompute.length != 0) { + var node = nodesToCompute.shift(); + var layerNumber = _returnLayerNumber(node); + if (layerNumber == -1) { + nodesToCompute.push(node); + } else { + node.layer = layerNumber; + // add ingoing nodes to compute + var edgesToNodes = _returnIncomingEdges(node); + for ( var i = 0; i < edgesToNodes.length; i++) { + nodesToCompute.push(edgesToNodes[i].source); + } + } + } + } + + /** + * Returns the number of the layer a NodeTemplate belongs to + * @param nodeTemplate a NodeTemplate with already set layer position + * @returns {Number} the number of the layer the NodeTemplate belongs to + */ + function _returnLayerNumber(nodeTemplate) { + var layerNumber = -1; + var outgoingEdges = _returnOutgoingEdges(nodeTemplate); + for ( var i = 0; i < outgoingEdges.length; i++) { + // we will see if it works here + var successorNode = outgoingEdges[i].target; + if ("layer" in successorNode) { + if (successorNode.layer + 1 >= layerNumber) { + layerNumber = successorNode.layer + 1; + } + } else { + layerNumber = -1; + break; + } + } + return layerNumber; + } + + /** + * Returns all jsPlumb#Connection edges of the graph represented by the + * nodeTemplates array of NodeTemplateShape which aren't referenced in the + * subgraphEdges array of type jsPlumb#Connection. + * + * @param {Object} + * nodeTemplates, array of NodeTemplateShape + * @param {Object} + * subgraphEdges, array of jsPlumb#Connection + * @return {Object} array of jsPlumb#Connection which contains edges that aren't + * in the subgraph + */ + function _computeEdgeComplement(nodeTemplates, subgraphEdges) { + var edgeSet = {}; + for ( var i = 0; i < nodeTemplates.length; i++) { + var incomingEdges = _returnIncomingEdges(nodeTemplates[i]); + var outgoingEdges = _returnOutgoingEdges(nodeTemplates[i]); + for ( var incomingEdgeIndex = 0; incomingEdgeIndex < incomingEdges.length; incomingEdgeIndex++) { + var inSubgraph = false; + for ( var subgraphEdgeIndex = 0; subgraphEdgeIndex < subgraphEdges.length; subgraphEdgeIndex++) { + if (incomingEdges[incomingEdgeIndex] === subgraphEdges[subgraphEdgeIndex]) { + inSubgraph = true; + break; + } + } + if (!inSubgraph) { + edgeSet[incomingEdges[incomingEdgeIndex]] = incomingEdges[incomingEdgeIndex]; + } + } + for ( var outgoingEdgeIndex = 0; outgoingEdgeIndex < outgoingEdges.length; outgoingEdgeIndex++) { + var inSubgraph = false; + for ( var subgraphEdgeIndex = 0; subgraphEdgeIndex < subgraphEdges.length; subgraphEdgeIndex++) { + if (outgoingEdges[outgoingEdgeIndex] === subgraphEdges[subgraphEdgeIndex]) { + inSubgraph = true; + break; + } + } + if (!inSubgraph) { + edgeSet[outgoingEdges[outgoingEdgeIndex]] = outgoingEdges[outgoingEdgeIndex]; + } + } + } + // transform edge-set to array + var edgeArray = new Array(); + for (edge in edgeSet) { + edgeArray.push(edgeSet[edge]); + } + return edgeArray; + } + + /** + * Returns a list of edges which represent an acyclic subgraph of the given + * nodes. The graph is at least of size 1/2*|E|, which can be troublesome in + * some situations. This means the graph could be layouted only on the "half" of + * it. + * + * Info: There are more sophisticated methods that guarantee to produce bigger + * subgraphs, see "Drawing Graphs: Methods and Models". But in this state it is + * a huge undertake, cause Oryx doesn't seem to have any basic graph algorithm + * shipped, like DFS etc.(correct me if i'm wrong, please). + * + * @param {Object} + * nodes of a graph, which are NodeTemplatesShapes + * @return [Object] edges of the contained acyclic subgraph, which are + * jsPlumb#Connection Objects + * @see "Drawing Graphs: Methods and Models", p. 91 + */ + function _computeAcyclicSubgraph(nodeTemplates) { + var subgraphEdges = []; + for ( var i = 0; i < nodeTemplates.length; i++) { + if (_returnOutgoingEdges(nodeTemplates[i]).length >= _returnIncomingEdges(nodeTemplates[i]).length) { + for ( var j = 0; j < _returnOutgoingEdges(nodeTemplates[i]).length; j++) { + subgraphEdges.push(_returnOutgoingEdges(nodeTemplates[i])[j]); + } + } else { + for ( var j = 0; j < _returnIncomingEdges(nodeTemplates[i]).length; j++) { + subgraphEdges.push(_returnIncomingEdges(nodeTemplates[i])[j]); + } + } + } + var filteredArray = subgraphEdges.filter(function(elem, pos) { + return subgraphEdges.indexOf(elem) == pos; + }); + return filteredArray; + } + + /** + * Returns relations having the given NodeTemplate as target + * + * @param nodeTemplate + * a nodeTemplateShape + * @returns Returns an array containing jsPlumb#Connection objects representing + * relations + */ + function _returnIncomingEdges(nodeTemplate) { + var edgeArray = new Array(); + jsPlumb.select().each( + function(connection) { + if (connection.targetId === nodeTemplate.id) { + /*if (connection.getType().length != 0) { + for ( var i = 0; i < connection.getType().length; i++) { + if (connection.getType()[i].toLowerCase().indexOf( + "hostedon") !== -1) { + edgeArray.push(connection); + } + } + } else { + edgeArray.push(connection); + }*/ + edgeArray.push(connection); + + } + }); + return edgeArray; + } + + /** + * Returns relations having the given NodeTemplate as source + * + * @param nodeTemplate + * a nodeTemplateShape + * @returns Returns an array containing jsPlumb#Connections objects representing + * relations + */ + function _returnOutgoingEdges(nodeTemplate) { + var edgeArray = new Array(); + jsPlumb.select().each( + function(connection) { + if (connection.sourceId === nodeTemplate.id) { + /*if (connection.getType().length != 0) { + for ( var i = 0; i < connection.getType().length; i++) { + if (connection.getType()[i].toLowerCase().indexOf( + "hostedon") !== -1) { + edgeArray.push(connection); + } + } + } else { + edgeArray.push(connection); + }*/ + edgeArray.push(connection); + } + }); + return edgeArray; + } + + /** + * Returns the NodeTemplates which can be reached by a outgoing edges of the given NodeTemplate + * @param nodeTemplate the NodeTemplate whose Succesors should be calculated + * @returns {Array} an array of NodeTemplates which are successors of the given NodeTemplate, may be empty + */ + function _returnSuccessors(nodeTemplate){ + var nodeArray = new Array(); + var outgoingEdges = _returnOutgoingEdges(nodeTemplate); + for(var i =0; i < outgoingEdges.length; i++){ + nodeArray.push(outgoingEdges[i].target); + } + return nodeArray; + } + + /** + * Returns all sources of the given graph + * @param nodeTemplates vertices of the graph, as an array of NodeTemplates + * @returns {Array} an Array of NodeTemplates + */ + function _returnSources(nodeTemplates){ + var sourceArray = new Array(); + for(var i = 0; i < nodeTemplates.length; i++){ + if(_returnIncomingEdges(nodeTemplates[i]) == 0){ + sourceArray.push(nodeTemplates[i]); + } + } + return sourceArray; + } + + /** + * Reverses a relationshipTemplate + * + * @param relationshipTemplate + * as we don't have relationshiptemplates modelled here we expect + * jsPlumb#Connection objects + */ + function _reverseEdge(relationshipTemplate) { + // this seems to work + var source = relationshipTemplate["source"]; + var sourceId = relationshipTemplate["sourceId"]; + var target = relationshipTemplate["target"]; + var targetId = relationshipTemplate["targetId"]; + + relationshipTemplate["source"] = target; + relationshipTemplate["sourceId"] = targetId; + relationshipTemplate["target"] = source; + relationshipTemplate["targetId"] = sourceId; + } + + /** + * Reverses relationshipTemplates + * + * @param relationshipTemplates + */ + function _reverseEdges(relationshipTemplates) { + for ( var i = 0; i < relationshipTemplates.length; i++) { + _reverseEdge(relationshipTemplates[i]); + } + } +});
\ No newline at end of file diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/winery-support-common.js b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/winery-support-common.js new file mode 100644 index 0000000..4e87f22 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/winery-support-common.js @@ -0,0 +1,330 @@ +/******************************************************************************* + * Copyright (c) 2012-2014 University of Stuttgart. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Oliver Kopp - initial API and implementation and/or initial documentation + *******************************************************************************/ + +/** + * Functions copied from winery-common.js to replace it in the long term + * + * Shared between topology modeler and repository + */ +define([], function() { + var xmlParser = new DOMParser(); + var VALUE_OF_NONE_CHOOSEN = "(none)"; // constant to indicate that nothing is chosen in a select2 + + return { + encodeId: encodeId, + getNamespaceAndLocalNameFromQName: getNamespaceAndLocalNameFromQName, + replaceDialogShownHookForOrionUpdate: replaceDialogShownHookForOrionUpdate, + writeCollectionDefinedByATextArea: writeCollectionDefinedByATextArea, + getDocument: getDocument, + + getURLFragmentOutOfFullQName: getURLFragmentOutOfFullQName, + makeArtifactTypeURLFromQName: makeArtifactTypeURLFromQName, + makeNodeTypeURLFromQName: makeNodeTypeURLFromQName, + makeRelationshipTypeURLFromQName: makeRelationshipTypeURLFromQName, + makeRelationshipTypeURLFromNSAndLocalName: makeRelationshipTypeURLFromNSAndLocalName, + + qname2href: qname2href, + + fetchSelect2DataAndInitSelect2: fetchSelect2DataAndInitSelect2, + removeItemFromSelect2Field: removeItemFromSelect2Field, + + checkXMLValidityAndShowErrorIfInvalid: checkXMLValidityAndShowErrorIfInvalid, + synchronizeNameAndType: synchronizeNameAndType, + + VALUE_OF_NONE_CHOOSEN: VALUE_OF_NONE_CHOOSEN + }; + + /** + * OriginalName: encodeID + */ + function encodeId(id) { + // the URL sent to the server should be the encoded id + id = encodeURIComponent(id); + // therefore, we have to encode it twice + id = encodeURIComponent(id); + return id; + } + + /** + * @param qname a QName in the form {namespace}localname + * @return { namespace: namespace, localname: localname } + */ + function getNamespaceAndLocalNameFromQName(qname) { + var i = qname.indexOf("}"); + var res = { + namespace : qname.substr(1,i-1), + localname : qname.substr(i+1) + }; + return res; + } + + /** + * Orion does not update content if field not fully shown + * therefore, we hook in into the "shown" event + */ + function replaceDialogShownHookForOrionUpdate(diag, orionAreaId, content) { + diag.off("shown.bs.modal"); + diag.on("shown.bs.modal", function() { + var area = window.winery.orionareas[orionAreaId]; + area.editor.setText(content); + area.fixEditorHeight(); + }); + } + + function getURLFragmentOutOfNSAndLocalName(nsAndLocalName) { + var res; + res = encodeID(nsAndLocalName.namespace); + res = res + "/"; + res = res + encodeID(nsAndLocalName.localname); + return res; + } + + /** + * Extracts an URL fragment of the form <encoded namespace>/<encoded id> out of a full QName + * + * @param qname a QName in the form {namespace}localname + */ + function getURLFragmentOutOfFullQName(qname) { + var d = getNamespaceAndLocalNameFromQName(qname); + return getURLFragmentOutOfNSAndLocalName(d); + } + + /** + * @param w the XMLwriter + * @param elementSet the set of HTML elements to write + * @param elementName the name of the wrapper element (e.g., "Requirements", "Policies") + */ + function writeCollectionDefinedByATextArea(w, elementSet, elementName) { + if (elementSet.length !== 0) { + w.writeStartElement(elementName); + elementSet.each(function(i, element) { + // XML contains element completely + // we do not have to parse reqorcap.children("div.id").children("span.id").text() or the span.name + var text = $(element).children("textarea").val(); + w.writeXML(text); + }); + w.writeEndElement(); + } + } + + function makeArtifactTypeURLFromQName(repoURL, qname) { + return repoURL + "/artifacttypes/" + getURLFragmentOutOfFullQName(qname) + "/"; + } + + function makeNodeTypeURLFromQName(repoURL, qname) { + return repoURL + "/nodetypes/" + getURLFragmentOutOfFullQName(qname) + "/"; + } + + function makeRelationshipTypeURLFromQName(repoURL, qname) { + return repoURL + "/relationshiptypes/" + getURLFragmentOutOfFullQName(qname) + "/"; + } + + function makeRelationshipTypeURLFromNSAndLocalName(repoURL, nsAndLocalName) { + return repoURL + "/relationshiptypes/" + getURLFragmentOutOfNSAndLocalName(nsAndLocalName) + "/"; + } + + /** + * functionality similar to org.eclipse.winery.common.Util.qname2href(String, Class<? extends TExtensibleElements>, QName) + */ + function qname2href(repositoryUrl, componentPathFragment, qname) { + var nsAndId = getNamespaceAndLocalNameFromQName(qname); + var absoluteURL = repositoryUrl + "/" + componentPathFragment + "/" + getURLFragmentOutOfNSAndLocalName(nsAndId); + var res = "<a target=\"_blank\" data-qname=\"" + qname + "\" href=\"" + absoluteURL + "\">" + nsAndId.localname + "</a>"; + return res; + } + + /** + * Inspired by + * + * @param field is the jquery field + * @param id_to_remove the id to remove + */ + function removeItemFromSelect2Field(field, id_to_remove) { + // nothing can be done currently + // see https://github.com/ivaynberg/select2/issues/535#issuecomment-30210641 for a disucssion + vShowNotification("The select field shows stale data. Refresh the page to get rid of that.") + } + + /** + * Fetches select2 data from the given URL and initializes the field provided by the fieldId + * + * Calls vShowError if something went wrong + * + * @param onSuccess (optional) + * @param allowAdditions (optional) if set to true, select2 is initalized with the functionality to allow additions during the search + */ + function fetchSelect2DataAndInitSelect2(fieldId, url, onSuccess, allowAdditions) { + $.ajax({ + url: url, + dataType: "json" + }).done(function (result) { + var params = {"data": result}; + if (typeof allowAdditions === "boolean") { + params.createSearchChoice = function(term) { + // enables creation of new namespaces + return {id:term, text:term}; + } + } + + // init select2 and select first item + $("#" + fieldId).select2(params); + if (result.length === 0) { + $("#" + fieldId).select2("val", null); + } else { + $("#" + fieldId).select2("val", result[0].id); + } + + if (typeof onSuccess === "function") { + onSuccess(); + } + }).fail(function(jqXHR, textStatus, errorThrown) { + vShowAJAXError("Could not fetch select2 data from " + url, jqXHR, errorThrown); + }); + + } + + + function getDocument(xmlString) { + var xmlDoc = xmlParser.parseFromString(xmlString, "text/xml"); + return xmlDoc; + } + + /** + * Checks given XML string for validity. If it's invalid, an error message is shown + * Relies on the current browser's XML handling returning a HTML document if something went wrong during parsing + * + * @return XMLdocument if XML is valid, false otherwise + */ + function checkXMLValidityAndShowErrorIfInvalid(xmlString) { + var doc = getDocument(xmlString); + var errorMsg = ""; + if (doc.firstChild.localName == "html") { + errorMsg = new XMLSerializer().serializeToString(doc); + } else { + // at Chrome, the error may be nested in the XML + + // quick hack, only "xhtml" is quered + function nsResolover(x) { + return "http://www.w3.org/1999/xhtml"; + } + + var element = doc.evaluate( '//x:parsererror', doc, nsResolover, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; + if (element !== null) { + errorMsg = new XMLSerializer().serializeToString(element); + } + } + + if (errorMsg !== "") { + vShowError(errorMsg); + return false; + } else { + return doc; + } + } + + /** + * Updates the XML in the orion editor based on the values given in the input fields. + * Shows error if XML is invalid + * + * Works only with SELECT2 fields + * + * @param idPrefix: (new|edit)${shortName}, derived names: [idPrefix]Name, [idPrefix]Id, Orion[idPrefix]XML + * @param hasIdField: whether the Id should be read and written + * @param selectFields: array of {attribute, fieldSuffix}, where attribute is a name of an attribute having a QName. + * Each select box is determined by #[idPrefix][fieldSuffix]. + * The select box content is converted to a QName and the result is written to the attribute [name] + * Default: {attribute: "type", fieldSuffix: "Type"} + * + * @return false if XML is invalid, true: an object if id/name/attribute1/attribute2/... (qname + attribute1FullQName: qname object)/xml (to be used in tmpl-${cssClassPrefix}) + * {id:id, name:name, type: "ns5:type", typeFullQName: "{http://www.example.org}type"} + */ + function synchronizeNameAndType(idPrefix, hasIdField, selectFields) { + if (typeof hasIdField === undefined) { + hasIdField = true; + } + if (typeof selectFields === undefined) { + selectFields = [{attribute: "type", fieldSuffix: "Type"}]; + } + + + var val = window.winery.orionareas["Orion" + idPrefix + "XML"].editor.getText(); + var xmlDoc = checkXMLValidityAndShowErrorIfInvalid(val); + if (xmlDoc) { + // handle name + var name = $("#" + idPrefix + "Name").val(); + // initialize result object + var res = { + name: name + }; + xmlDoc.firstChild.setAttribute("name", name); + + // write id and name to XML + if (hasIdField) { + var id = $("#" + idPrefix + "Id").val(); + if (!id) { + // TODO a checking should be done if the id exists + // probably not here, but at caller's side + id = name; + } + xmlDoc.firstChild.setAttribute("id", id); + res.id = id; + } + + // write each selectField to xml + // for that, we have to determine the QName + $(selectFields).each(function(i, m) { + var content = $("#" + idPrefix + m.fieldSuffix).select2("val"); + + if (content == VALUE_OF_NONE_CHOOSEN) { + // if nothing is chosen do not put it into the result + return; + } + + // determine qname of type + //getQNameOutOfFullQName(type, xmlDoc.firstChild) does not always work as xmlDoc.firstChild does not have ALL *available* namespace prefixes + var typeNSAndId = getNamespaceAndLocalNameFromQName(content); + var prefix = xmlDoc.firstChild.lookupPrefix(typeNSAndId.namespace); + if (!prefix) { + // we have to ask the repo for a prefix + $.ajax({ + type: "GET", + async: false, + "url": winery.repositoryURL + "/admin/namespaces/" + encodeID(typeNSAndId.namespace), + dataType: "text", + error: function(jqXHR, textStatus, errorThrown) { + vShowAJAXError("Could not determine prefix", jqXHR, errorThrown); + }, + success: function(resData, textStatus, jqXHR) { + prefix = resData; + } + }); + // new prefix fetched, xmlns attribute has to be written + xmlDoc.firstChild.setAttribute("xmlns:" + prefix, typeNSAndId.namespace); + } + var qname = prefix + ":" + typeNSAndId.localname; + res[m.attribute] = qname; + res[m.attribute + "FullQName"] = typeNSAndId; + xmlDoc.firstChild.setAttribute(m.attribute, qname); + }); + + var xml = new XMLSerializer().serializeToString(xmlDoc); + res.xml = xml; + + return res; + } else { + return false; + } + } + + + } +); diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/winery-topologycompletion.js b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/winery-topologycompletion.js new file mode 100644 index 0000000..6ab66e8 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/winery-topologycompletion.js @@ -0,0 +1,178 @@ +/******************************************************************************* + * Copyright (c) 2013 Pascal Hirmer. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Pascal Hirmer - initial API and implementation + *******************************************************************************/ + +define(function() { + + /** + * This module handles a click on "Complete Topology" in the enterTopologyCompletionInformation dialog. + */ + var module = { + complete: complete, + restartCompletion: restartCompletion + }; + return module; + + /** + * This function start the topology completion. With information from the + * PolicyInformationDialog the topologyCompletion.jsp is called which will invoke the + * Topology Completion java component. The parameters determine if the current topology + * will be overwritten or if a new topology is created. + * + * @param (String) overwriteTopology + * determines how to save the topology. If true, the current topology is overwritten. + * @param (Boolean) openInNewWindow + * determines if the result is opened in a new window + * @param (String) topologyName + * the name of the new topology + * @param (String) topologyNamespace + * the namespace of the new topology + * @param (Boolean) stepByStep + * if true, the completion will be processed step by step + * @param (String) repositoryURL + * the URL to the repository the topology is saved to + * @param (String) toscaNamespace + * the namespace URL of TOSCA + * @param (String) wineryNamespace + * the namespace URL of winery + * @param (String) serviceTemplateName + * the name of the service template containing the displayed topology template + * @param (String) topologyTemplateURL + * the URL to the displayed topology template + */ + function complete(overwriteTopology, openInNewWindow, topologyName, topologyNamespace, stepByStep, repositoryURL, serviceTemplateName, topologyTemplateURL) { + + // if the user wants to create a new topology, a post call is sent to the repository adding + // the new ServiceTemplate + if (!overwriteTopology) { + var dataToSend = "name=" + topologyName + "&namespace=" + topologyNamespace; + var url = repositoryURL + "/servicetemplates/"; + $.ajax( + { + type: "POST", + async: false, + url: url, + "data": dataToSend, + dataType: "text", + error: function(jqXHR, textStatus, errorThrown) { + vShowAJAXError("Could not add Service Template."); + } + } + ); + } + + var topology; + require(["winery-topologymodeler-AMD"], function(wt) { + topology = wt.getTopologyTemplateAsXML(true); + + // call to the completion JSP which will call the java component + $.post("jsp/topologyCompletion/topologyCompletion.jsp", {topology: topology, stName: serviceTemplateName, templateURL: topologyTemplateURL, overwriteTopology: overwriteTopology, topologyName: topologyName, topologyNamespace: topologyNamespace, repositoryURL: repositoryURL, stepByStep: stepByStep, openInNewWindow: openInNewWindow, restarted: "false"}, + /** + * Callback function which will either open a new window if a new topology was created or + * refresh the current browser window + * + * @param (String) data + * the answer of the post call + */ + function(data){ + // checks the message returned by the CompletionInterface + if (data.indexOf("topologyComplete") != -1) { + vShowSuccess('The topology is already complete.'); + } else if (data.indexOf("failure") != -1) { + vShowError(data); + } else if (data.indexOf("userTopologySelection") != -1) { + $(chooseTopologyDiag[0].children[0].children[0].children[1]).html(data); + window.setTimeout(jsPlumb.repaintEverything, JQUERY_ANIMATION_DURATION); + chooseTopologyDiag.modal("show"); + } else if (data.indexOf("topologyComplete") == -1 && data.indexOf("userTopologySelection") == -1 && data.indexOf("userInteraction") != -1) { + $(chooseRelationshipTemplateDiag[0].children[0].children[0].children[1]).html(data); + window.setTimeout(jsPlumb.repaintEverything, JQUERY_ANIMATION_DURATION); + chooseRelationshipTemplateDiag.modal("show"); + } else if (data.indexOf("topologyComplete") == -1 && data.indexOf("stepByStep") != -1) { + $(chooseNodeTemplateDiag[0].children[0].children[0].children[1]).html(data); + window.setTimeout(jsPlumb.repaintEverything, JQUERY_ANIMATION_DURATION); + chooseNodeTemplateDiag.modal("show"); + } else { + if (openInNewWindow) { + // a new topology has been created, open it in a new window + var win=window.open('?repositoryURL=' + repositoryURL + '&ns='+ topologyNamespace + '&id=' + topologyName, '_blank'); + win.focus(); + } else if (overwriteTopology) { + // refresh page + document.location.reload(true); + } + } + } + ); + }); + } + + /** + * This function restarts the topology completion when it has been stopped to get a + * user decision. + * + * @param (String) topology + * the topology as XML string + * @param (String) overwriteTopology + * determines how to save the topology. Can contain the values "createNew" or "overwrite" + * @param (Boolean) openInNewWindow + * determines if the result is opened in a new window + * @param (String) topologyName + * the name of the new topology + * @param (String) topologyNamespace + * the namespace of the new topology + * @param (Boolean) stepByStep + * if true, the completion will be processed step by step + * @param (String) serviceTemplateName + * the name of the service template containing the displayed topology template + * @param (String) topologyTemplateURL + * the URL to the displayed topology template + * @param (String) repositoryURL + * the URL to the repository the topology is saved to + */ + function restartCompletion(topology, overwriteTopology, openInNewWindow, topologyName, topologyNamespace, stepByStep, serviceTemplateName, topologyTemplateURL, repositoryURL) { + + // remove whitespaces in the topology XML string + topology = topology.replace(/\s+/g, ' '); + topology = topology.substr(1); + + // call to the completion JSP which will call the java component + $.post("jsp/topologyCompletion/topologyCompletion.jsp", { topology: topology, stName: serviceTemplateName, templateURL: topologyTemplateURL, overwriteTopology: overwriteTopology, topologyName: topologyName, topologyNamespace: topologyNamespace, repositoryURL: repositoryURL, stepByStep: stepByStep, restarted: "true"}, + /** + * Callback function which will either open a new window if a new topology was created or + * refresh the current browser window + * + * @param (String) data + * the answer of the post call + */ + function(data){ + if (data.indexOf("Successful") == -1 && data.indexOf("stepByStep") == -1) { + $(chooseRelationshipTemplateDiag[0].children[0].children[0].children[1]).html(data); + window.setTimeout(jsPlumb.repaintEverything, JQUERY_ANIMATION_DURATION); + chooseRelationshipTemplateDiag.modal("show"); + } else if (data.indexOf("Successful") == -1 && data.indexOf("stepByStep") != -1) { + $(chooseNodeTemplateDiag[0].children[0].children[0].children[1]).html(data); + window.setTimeout(jsPlumb.repaintEverything, JQUERY_ANIMATION_DURATION); + chooseNodeTemplateDiag.modal("show"); + } else { + if (openInNewWindow) { + // a new topology has been created, open it in a new window + var win = window.open('?repositoryURL=' + "<%=repositoryURL%>" + '&ns='+ topologyNamespace + '&id=' + topologyName, '_blank'); + win.focus(); + } else if (overwriteTopology) { + // refresh page + document.location.reload(true); + } + } + } + ); + } +});
\ No newline at end of file diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/winery-topologymodeler-AMD.js b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/winery-topologymodeler-AMD.js new file mode 100644 index 0000000..a292f43 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/winery-topologymodeler-AMD.js @@ -0,0 +1,234 @@ +/******************************************************************************* + * Copyright (c) 2012-2015 University of Stuttgart. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Oliver Kopp - initial API and implementation and/or initial documentation + *******************************************************************************/ + +/** + * This file contains supporting functions for the topoplogy modeler + */ +define( + // although XMLWriter ist not an AMD module, requirejs does not complain when loading it + ["winery-support-common", "XMLWriter"], + function (w) { + // has to be consistent with {@link org.eclipse.winery.common.constants.Namespaces} + var TOSCA_NAMESPACE = "http://docs.oasis-open.org/tosca/ns/2011/12"; + var TOSCA_WINERY_EXTENSIONS_NAMESPACE ="http://www.opentosca.org/winery/extensions/tosca/2013/02/12"; + + var topologyTemplateURL; + + var module = { + save: save, + setTopologyTemplateURL: function(url) { + topologyTemplateURL = url; + }, + getTopologyTemplateAsXML: getTopologyTemplateAsXML, + + TOSCA_NAMESPACE: TOSCA_NAMESPACE, + TOSCA_WINERY_EXTENSIONS_NAMESPACE: TOSCA_WINERY_EXTENSIONS_NAMESPACE + }; + return module; + + function writeReqOrCaps(elements, xmlw, globalWrapperElementName, singleElementWrapperName) { + if (elements.length != 0) { + xmlw.writeStartElement(globalWrapperElementName); + + $.each(elements, function(i,e) { + xmlw.writeStartElement(singleElementWrapperName); + e = $(e); + xmlw.writeAttributeString("id", e.children(".id").text()); + xmlw.writeAttributeString("name", e.children(".name").text()); + writeType(xmlw, e.children(".type").children("a").data("qname")); + savePropertiesFromDivToXMLWriter(e.children("div.propertiesContainer"), xmlw); + xmlw.writeEndElement(); + }); + + xmlw.writeEndElement(); + } + + } + + /** + * "doSave" + */ + function save() { + $("#saveBtn").button('loading'); + + $.ajax({ + url: topologyTemplateURL, + type: "PUT", + contentType: 'text/xml', + data: getTopologyTemplateAsXML(false), + success: function(data, textStatus, jqXHR) { + $("#saveBtn").button('reset'); + vShowSuccess("successfully saved."); + }, + error: function(jqXHR, textStatus, errorThrown) { + $("#saveBtn").button('reset'); + vShowAJAXError("Could not save", jqXHR, errorThrown); + } + }); + } + + /** + * Creates an XML String of the modelled topology template. + */ + function getTopologyTemplateAsXML(needsDefinitionsTag) { + + var xmlw = new XMLWriter("utf-8"); + xmlw.writeStartDocument(); + + if (needsDefinitionsTag) { + xmlw.writeStartElement("Definitions"); + xmlw.writeAttributeString("xmlns", TOSCA_NAMESPACE); + xmlw.writeAttributeString("xmlns:winery", TOSCA_WINERY_EXTENSIONS_NAMESPACE); + + xmlw.writeStartElement("ServiceTemplate"); + xmlw.writeAttributeString("xmlns", TOSCA_NAMESPACE); + xmlw.writeAttributeString("xmlns:winery", TOSCA_WINERY_EXTENSIONS_NAMESPACE); + } + xmlw.writeStartElement("TopologyTemplate"); + xmlw.writeAttributeString("xmlns", TOSCA_NAMESPACE); + xmlw.writeAttributeString("xmlns:winery", TOSCA_WINERY_EXTENSIONS_NAMESPACE); + $("div.NodeTemplateShape").not(".hidden").each (function() { + xmlw.writeStartElement("NodeTemplate"); + + var id = $(this).attr("id"); + + var headerContainer = $(this).children("div.headerContainer"); + var name = headerContainer.children("div.name").text(); + var typeQNameStr = headerContainer.children("span.typeQName").text(); + var minmaxdiv = headerContainer.children("div.minMaxInstances"); + var min = minmaxdiv.children("span.minInstances").text(); + var max = minmaxdiv.children("span.maxInstances").text(); + if (max == "∞") { + max = "unbounded"; + } + var x = $(this).css("left"); + x = x.substring(0, x.indexOf("px")); + var y = $(this).css("top"); + y = y.substring(0, y.indexOf("px")); + + xmlw.writeAttributeString("id", id); + if (name != "") { + xmlw.writeAttributeString("name", name); + } + writeType(xmlw, typeQNameStr); + if (min != "") { + xmlw.writeAttributeString("minInstances", min); + } + if (max != "") { + xmlw.writeAttributeString("maxInstances", max); + } + xmlw.writeAttributeString("winery:x", x); + xmlw.writeAttributeString("winery:y", y); + + /** Properties **/ + savePropertiesFromDivToXMLWriter($(this).children("div.propertiesContainer"), xmlw); + + /** Requirements **/ + writeReqOrCaps( + $(this).children("div.requirementsContainer").children("div.content").children("div.reqorcap"), + xmlw, + "Requirements", + "Requirement"); + + /** Capabilities **/ + writeReqOrCaps( + $(this).children("div.capabilitiesContainer").children("div.content").children("div.reqorcap"), + xmlw, + "Capabilities", + "Capability"); + + /** Policies **/ + w.writeCollectionDefinedByATextArea(xmlw, + $(this).children("div.policiesContainer").children("div.content").children("div.policy"), + "Policies"); + + /** Deployment Artifacts **/ + var das = $(this).children("div.deploymentArtifactsContainer").children("div.content").children("div.deploymentArtifact"); + if (das.length != 0) { + xmlw.writeStartElement("DeploymentArtifacts"); + das.each(function(i,e) { + // the textarea contains a valid deployment artifact xml + var xml = $(e).children("textarea").val(); + xmlw.writeXML(xml); + }); + xmlw.writeEndElement(); + } + + // End: Nodetemplate + xmlw.writeEndElement(); + }); + jsPlumb.select().each(function(connection) { + xmlw.writeStartElement("RelationshipTemplate"); + var id = connection.id; + var typeQNameStr = connection.getType()[0]; + + var connData = winery.connections[id]; + if (!connData) { + vShowError("Error in the internal data structure: Id " + id + " not found"); + return; + } + + xmlw.writeAttributeString("id", connData.id); + if (connData.name != "") { + xmlw.writeAttributeString("name", connData.name); + } + writeType(xmlw, typeQNameStr); + + if (typeof connData.propertiesContainer !== "undefined") { + savePropertiesFromDivToXMLWriter(connData.propertiesContainer, xmlw); + } + + xmlw.writeStartElement("SourceElement"); + if (connData.req) { + // conn starts at a requirement + xmlw.writeAttributeString("ref", connData.req); + } else { + // conn starts at a node template + xmlw.writeAttributeString("ref", connection.sourceId); + } + xmlw.writeEndElement(); + xmlw.writeStartElement("TargetElement"); + if (connData.cap) { + // conn ends at a capability + xmlw.writeAttributeString("ref", connData.cap); + } else { + // conn ends at a node template + xmlw.writeAttributeString("ref", connection.targetId); + } + xmlw.writeEndElement(); + + xmlw.writeEndElement(); + }); + + if (needsDefinitionsTag) { + xmlw.writeEndElement(); + xmlw.writeEndElement(); + } + + xmlw.writeEndDocument(); + + return xmlw.flush(); + } + + function writeQNameAttribute(w, nsPrefix, qnameStr) { + var qname = getQName(qnameStr); + w.writeAttributeString("xmlns:" + nsPrefix, qname.namespace); + w.writeAttributeString("type", nsPrefix + ":" + qname.localName); + } + + function writeType(w, typeQNameStr) { + writeQNameAttribute(w, "ty", typeQNameStr); + } + + } +); + diff --git a/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/winery-topologymodeler.js b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/winery-topologymodeler.js new file mode 100644 index 0000000..cc5c564 --- /dev/null +++ b/winery/org.eclipse.winery.topologymodeler/src/main/webapp/js/winery-topologymodeler.js @@ -0,0 +1,308 @@ +/******************************************************************************* + * Copyright (c) 2012-2013,2015 University of Stuttgart. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Oliver Kopp - initial API and implementation and/or initial documentation + * Yves Schubert - switch to bootstrap 3 + *******************************************************************************/ + +function getQName(qnameStr) { + var pos = qnameStr.indexOf("}"); + var namespace = qnameStr.substring(1, pos); + var localName = qnameStr.substring(pos+1); + var res = { + "namespace": namespace, + "localName": localName + }; + return res; +} + +/** + * @param attributeName an attribute with a value in the form prefix:localname + * @param xmlElement a DOM element (offering the method lookupNamespaceURI) + * @return { ns: namespace, id: id } + */ +function getNSAndId(attributeName, xmlElement) { + var attributeValue = xmlElement.getAttribute(attributeName); + var i = attributeValue.indexOf(":"); + var prefix = attributeValue.substring(0, i); + var localName = attributeValue.substring(i+1); + var ns = xmlElement.lookupNamespaceURI(prefix); + var res = { + ns : ns, + id: localName + }; + return res; +} + +/** + * @param el a href element + * @param pathComponent the path element "artifacttemplates" or "artifacttypes" + * @param attributeName the name of the attribute to read from the given xmlElement + * @param xmlElement used to resolve a namespace prefix to a full namespace URI + */ +function addHref(el, pathComponent, attributeName, xmlElement) { + var nsAndId = getNSAndId(attributeName, xmlElement); + var loc = winery.repositoryURL + "/" + pathComponent + "/" + encodeID(nsAndId.ns) + "/" + encodeID(nsAndId.id); + el.attr("href", loc); +} + +var currentlySelectedDeploymentArtifactDiv; + +/** + * Sets global variables currentlySelectedNodeTemplate and currentlySelectedDeploymentArtifactDiv + */ +function showDeploymentArtifactInformation(nodeTemplateId, deploymentArtifactName) { + currentlySelectedNodeTemplate = nodeTemplateId; + var daDiv = $("#" + nodeTemplateId).children("div.deploymentArtifactsContainer").children("div.content").children("div.deploymentArtifact").children("div.name:contains(" + deploymentArtifactName + ")").parent(); + currentlySelectedDeploymentArtifactDiv = daDiv; + var xml = daDiv.children("textarea").val(); + + // get values to display directly from the "UI" instead of parsing the XML and asking the server for appropriate names + var daArtifactTemplateName = daDiv.children("div.artifactTemplate").text(); + var daArtifactTypeName = daDiv.children("div.artifactType").text(); + + // determine URLs + require(["winery-support-common"], function(wsc) { + xmlDoc = wsc.getDocument(xml); + da = xmlDoc.firstChild; + + $("#DAname").text(deploymentArtifactName); + + $("#DAArtifactType").text(daArtifactTypeName); + addHref($("#DAArtifactType"), "artifacttypes", "artifactType", da); + + var at = $("#DAArtifactTemplate"); + if (daArtifactTemplateName != "") { + at.text(daArtifactTemplateName); + addHref(at, "artifacttemplates", "artifactRef", da); + } else { + at.text("No template associated"); + at.removeAttr("href"); + } + + $("#DAXML").val(xml); + + $("#DeploymentArtifactInfo").modal("show"); + }); +} + +/** + * Adds the given data to the deployment artifacts table of the currently active node template + * + * @param xmlAsDOM XML DOM document, TDeploymentArtifact. Produced by org.eclipse.winery.resources.artifacts.GenericArtifactsResource.onPost(String, String, String, String, String, String, String, String) + * @param xmlAsString + */ +function addDeploymentArtifact(xmlAsDOM, xmlAsString) { + var da = xmlAsDOM.firstChild; + var daName = da.getAttribute("name"); + + // we do NOT extract artifactType / artifactTemplate from the XML, but use the user input + // showDeploymentArtifactInformation will extract its data directly from the XML without querying some input at the other HTML elements + var daArtifactTemplateName = $("#artifactTemplateName").val(); + var daArtifactTypeName = $("#artifactType option:selected").text(); + + // add information to node template shape + var daData = { + nodeTemplateId : currentlySelectedNodeTemplate, + name : daName, + xml : xmlAsString, + artifactTypeName: daArtifactTypeName + }; + if (daArtifactTemplateName != "") { + daData.artifactTemplateName = daArtifactTemplateName; + } + addDeploymentArtifactInfoToNodeTemplate(daData); +} + +function addDeploymentArtifactInfoToNodeTemplate(daData) { + require(["tmpl"], function(tmpl){ + var data = tmpl("tmpl-deploymentArtifact", daData); + var element = $("#" + currentlySelectedNodeTemplate).children(".deploymentArtifactsContainer").children(".content").children(".addDA:first"); + element.before(data); + }); +} + +/** + * This function directly accesses the fields of the dialog, because the return value of the server is XML and we do not want to parse XML + * + * @param artifactInfo = {name, interfaceName (may be undefined), operationName (may be undefined), artifactTemplate (QName, may be undefined), artifactType} + */ +function artifactAddedSuccessfully(artifactInfo) { + var typeNsAndId = getNamespaceAndLocalNameFromQName(artifactInfo.artifactType); + var artifactTemplateNSAndId; + if (artifactInfo.artifactTemplate) { + artifactTemplateNSAndId = getNamespaceAndLocalNameFromQName(artifactInfo.artifactTemplate); + } else { + artifactTemplateNSAndId = undefined; + } + + var daData = { + nodeTemplateId : currentlySelectedNodeTemplate, + name : artifactInfo.name, + artifactTypeName: typeNsAndId.localname, + artifactTypeNSAndId: typeNsAndId, + artifactTemplateName: artifactInfo.artifactTemplateName, + artifactTemplateNSAndId: artifactTemplateNSAndId + }; + require(["tmpl"], function(tmpl){ + daData.xml = tmpl("tmpl-deploymentArtifactXML", daData); + addDeploymentArtifactInfoToNodeTemplate(daData); + }); +} + +// variables used for creation of deployment artifacts +var artifactTemplateAutoCreationEnabled = true; +var syncDAnameWithATname; + +// introduced by the handling of deployment and implementation artifacts +// holds the ID only (!) +var currentlySelectedNodeTemplate; + +/** + * FIXME: this function is not updated to the the new dialog design and not included any more + * + * It should be used if the checkbox for at creation changes its checked status or if the at name is not valid + * + */ +function updateArtifactTemplateCreationEnablement(value) { + // remove field highlights + // (currently, no intelligent removal and addition is made) + $("#artifactName").removeClass("highlight"); + $("#artifactTemplateName").removeClass("highlight"); + + if (value) { + // enable it + artifactTemplateAutoCreationEnabled = true; + $("#artifactTemplateName").removeAttr("disabled"); + $("#artifactTemplateNS").removeAttr("disabled"); + $("#createWithoutFilesBtn").attr("disabled", "disabled"); + $("#createWithFilesBtn").removeAttr("disabled"); + } else { + // disable it + artifactTemplateAutoCreationEnabled = false; + $("#artifactTemplateName").attr("disabled", "disabled"); + $("#artifactTemplateNS").attr("disabled", "disabled"); + $("#createWithoutFilesBtn").removeAttr("disabled"); + $("#createWithFilesBtn").attr("disabled", "disabled"); + } +} + +function isShownNodeTemplateShapeChangeBoxes(shape) { + return (shape.find(".endpointContainer").is(":visible")); +} + +/** + * @param shape jQuery object + */ +function showNodeTemplateShapeChangeBoxes(shape) { + shape.find(".addDA").show(); + shape.children(".endpointContainer").show(); + shape.find(".addnewreqorcap").show(); + shape.find(".addnewpolicy").show(); +} + +/** + * @param shape jQuery object + */ +function hideNodeTemplateShapeChangeBoxes(shape) { + shape.find(".addDA").hide(); + shape.children(".endpointContainer").hide(); + shape.find(".addnewreqorcap").hide(); + shape.find(".addnewpolicy").hide(); +} + +// indicates if a connection is currently drawn +// used to decide whether the node template boxes should be displayed +var isInConnectionMode = false; + +function wineryMoveSelectedNodeTemplateShapes(dX, dY) { + var shapes = $("div.NodeTemplateShape.selected"); + hideNodeTemplateShapeChangeBoxes(shapes); + shapes.each(function(i, nodeTemplate) { + nodeTemplate = $(nodeTemplate); + var offset = nodeTemplate.offset(); + offset.left += dX; + offset.top += dY; + nodeTemplate.offset(offset); + }); + jsPlumb.repaint(shapes); +} + + +/** + * Simple eventing framework + * + * use + * winery.events.register(name, function) to register on an event + * and + * winery.events.fire(name) to fire all registered functions + */ + +winery = {}; +winery.events = { + _events : {}, + + /** + * Registers a function + * + * @return the registered function + */ + register : function(eventName, f) { + if (!winery.events._events[eventName]) { + winery.events._events[eventName] = {}; + } + winery.events._events[eventName][f] = f; + return f; + }, + + /** + * Fires all functions associated with the given event name + */ + fire : function(eventName) { + if (winery.events._events[eventName]) { + $.each(winery.events._events[eventName], function(index, value) { + value(); + }); + } + return true; + } +}; + +/** + * Determines whether a key combo is allowed. + * + * For instance, when a modal dialog is opened or a input is selected, DEL should not delete node template shapes + */ +function keyComboAllowed() { + return ((!$(document.activeElement).is("input")) && ($("div.modal:visible").size() == 0)); +} + +function keyComboAllowedAndNodeTemplatesSelected() { + return (keyComboAllowed() && ($("div.NodeTemplateShape.selected").size() != 0)); +} + + + + +/* list of event names */ +winery.events.name = {}; +winery.events.name.command = {}; + +winery.events.name.SELECTION_CHANGED = "selectionchanged"; +winery.events.name.command.SELECT_ALL_NODETEMPLATES = "selectAllNodeTemplates"; +winery.events.name.command.UNSELECT_ALL_NODETEMPLATES = "unselectAllNodeTemplates"; +winery.events.name.command.DELETE_SELECTION = "deleteSelection"; + +winery.events.name.command.MOVE_DOWN = "moveDown"; +winery.events.name.command.MOVE_UP = "moveUp"; +winery.events.name.command.MOVE_LEFT = "moveLeft"; +winery.events.name.command.MOVE_RIGHT = "moveRight"; + +winery.events.name.command.SAVE = "save"; + |