Merge "Fix build errors in autorelease full clean build"
[vfc/nfvo/wfengine.git] / winery / org.eclipse.winery.topologymodeler / src / main / webapp / js / winery-support-common.js
1 /*******************************************************************************
2  * Copyright (c) 2012-2014 University of Stuttgart.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * and the Apache License 2.0 which both accompany this distribution,
6  * and are available at http://www.eclipse.org/legal/epl-v10.html
7  * and http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Contributors:
10  *    Oliver Kopp - initial API and implementation and/or initial documentation
11  *******************************************************************************/
12
13 /**
14  * Functions copied from winery-common.js to replace it in the long term
15  *
16  * Shared between topology modeler and repository
17  */
18 define([], function() {
19                 var xmlParser = new DOMParser();
20                 var VALUE_OF_NONE_CHOOSEN = "(none)"; // constant to indicate that nothing is chosen in a select2
21
22                 return {
23                         encodeId: encodeId,
24                         getNamespaceAndLocalNameFromQName: getNamespaceAndLocalNameFromQName,
25                         replaceDialogShownHookForOrionUpdate: replaceDialogShownHookForOrionUpdate,
26                         writeCollectionDefinedByATextArea: writeCollectionDefinedByATextArea,
27                         getDocument: getDocument,
28
29                         getURLFragmentOutOfFullQName: getURLFragmentOutOfFullQName,
30                         makeArtifactTypeURLFromQName: makeArtifactTypeURLFromQName,
31                         makeNodeTypeURLFromQName: makeNodeTypeURLFromQName,
32                         makeRelationshipTypeURLFromQName: makeRelationshipTypeURLFromQName,
33                         makeRelationshipTypeURLFromNSAndLocalName: makeRelationshipTypeURLFromNSAndLocalName,
34
35                         qname2href: qname2href,
36
37                         fetchSelect2DataAndInitSelect2: fetchSelect2DataAndInitSelect2,
38                         removeItemFromSelect2Field: removeItemFromSelect2Field,
39
40                         checkXMLValidityAndShowErrorIfInvalid: checkXMLValidityAndShowErrorIfInvalid,
41                         synchronizeNameAndType: synchronizeNameAndType,
42
43                         VALUE_OF_NONE_CHOOSEN: VALUE_OF_NONE_CHOOSEN
44                 };
45
46                 /**
47                  * OriginalName: encodeID
48                  */
49                 function encodeId(id) {
50                         // the URL sent to the server should be the encoded id
51                         id = encodeURIComponent(id);
52                         // therefore, we have to encode it twice
53                         id = encodeURIComponent(id);
54                         return id;
55                 }
56
57                 /**
58                  * @param qname a QName in the form {namespace}localname
59                  * @return { namespace: namespace, localname: localname }
60                  */
61                 function getNamespaceAndLocalNameFromQName(qname) {
62                         var i = qname.indexOf("}");
63                         var res = {
64                                 namespace : qname.substr(1,i-1),
65                                 localname : qname.substr(i+1)
66                         };
67                         return res;
68                 }
69
70                 /**
71                  * Orion does not update content if field not fully shown
72                  * therefore, we hook in into the "shown" event
73                  */
74                 function replaceDialogShownHookForOrionUpdate(diag, orionAreaId, content) {
75                         diag.off("shown.bs.modal");
76                         diag.on("shown.bs.modal", function() {
77                                 var area = window.winery.orionareas[orionAreaId];
78                                 area.editor.setText(content);
79                                 area.fixEditorHeight();
80                         });
81                 }
82
83                 function getURLFragmentOutOfNSAndLocalName(nsAndLocalName) {
84                         var res;
85                         res = encodeID(nsAndLocalName.namespace);
86                         res = res + "/";
87                         res = res + encodeID(nsAndLocalName.localname);
88                         return res;
89                 }
90
91                 /**
92                  * Extracts an URL fragment of the form <encoded namespace>/<encoded id> out of a full QName
93                  *
94                  * @param qname a QName in the form {namespace}localname
95                  */
96                 function getURLFragmentOutOfFullQName(qname) {
97                         var d = getNamespaceAndLocalNameFromQName(qname);
98                         return getURLFragmentOutOfNSAndLocalName(d);
99                 }
100
101                 /**
102                  * @param w the XMLwriter
103                  * @param elementSet the set of HTML elements to write
104                  * @param elementName the name of the wrapper element (e.g., "Requirements", "Policies")
105                  */
106                 function writeCollectionDefinedByATextArea(w, elementSet, elementName) {
107                         if (elementSet.length !== 0) {
108                                 w.writeStartElement(elementName);
109                                 elementSet.each(function(i, element) {
110                                         // XML contains element completely
111                                         // we do not have to parse reqorcap.children("div.id").children("span.id").text() or the span.name
112                                         var text = $(element).children("textarea").val();
113                                         w.writeXML(text);
114                                 });
115                                 w.writeEndElement();
116                         }
117                 }
118
119                 function makeArtifactTypeURLFromQName(repoURL, qname) {
120                         return repoURL + "/artifacttypes/" + getURLFragmentOutOfFullQName(qname) + "/";
121                 }
122
123                 function makeNodeTypeURLFromQName(repoURL, qname) {
124                         return repoURL + "/nodetypes/" + getURLFragmentOutOfFullQName(qname) + "/";
125                 }
126
127                 function makeRelationshipTypeURLFromQName(repoURL, qname) {
128                         return repoURL + "/relationshiptypes/" + getURLFragmentOutOfFullQName(qname) + "/";
129                 }
130
131                 function makeRelationshipTypeURLFromNSAndLocalName(repoURL, nsAndLocalName) {
132                         return repoURL + "/relationshiptypes/" + getURLFragmentOutOfNSAndLocalName(nsAndLocalName) + "/";
133                 }
134
135                 /**
136                  * functionality similar to org.eclipse.winery.common.Util.qname2href(String, Class<? extends TExtensibleElements>, QName)
137                  */
138                 function qname2href(repositoryUrl, componentPathFragment, qname) {
139                         var nsAndId = getNamespaceAndLocalNameFromQName(qname);
140                         var absoluteURL = repositoryUrl + "/" + componentPathFragment + "/" + getURLFragmentOutOfNSAndLocalName(nsAndId);
141                         var res = "<a target=\"_blank\" data-qname=\"" + qname + "\" href=\"" + absoluteURL + "\">" + nsAndId.localname + "</a>";
142                         return res;
143                 }
144
145                 /**
146                  * Inspired by
147                  *
148                  * @param field is the jquery field
149                  * @param id_to_remove the id to remove
150                  */
151                 function removeItemFromSelect2Field(field, id_to_remove) {
152                         // nothing can be done currently
153                         // see https://github.com/ivaynberg/select2/issues/535#issuecomment-30210641 for a disucssion
154                         vShowNotification("The select field shows stale data. Refresh the page to get rid of that.")
155                 }
156
157                 /**
158                  * Fetches select2 data from the given URL and initializes the field provided by the fieldId
159                  *
160                  * Calls vShowError if something went wrong
161                  *
162                  * @param onSuccess (optional)
163                  * @param allowAdditions (optional) if set to true, select2 is initalized with the functionality to allow additions during the search
164                  */
165                 function fetchSelect2DataAndInitSelect2(fieldId, url, onSuccess, allowAdditions) {
166                         $.ajax({
167                                 url: url,
168                                 dataType: "json"
169                         }).done(function (result) {
170                                 var params = {"data": result};
171                                 if (typeof allowAdditions === "boolean") {
172                                         params.createSearchChoice = function(term) {
173                                                 // enables creation of new namespaces
174                                                 return {id:term, text:term};
175                                         }
176                                 }
177
178                                 // init select2 and select first item
179                                 $("#" + fieldId).select2(params);
180                                 if (result.length === 0) {
181                                         $("#" + fieldId).select2("val", null);
182                                 } else {
183                                         $("#" + fieldId).select2("val", result[0].id);
184                                 }
185
186                                 if (typeof onSuccess === "function") {
187                                         onSuccess();
188                                 }
189                         }).fail(function(jqXHR, textStatus, errorThrown) {
190                                 vShowAJAXError("Could not fetch select2 data from " + url, jqXHR, errorThrown);
191                         });
192
193                 }
194
195
196                 function getDocument(xmlString) {
197                         var xmlDoc = xmlParser.parseFromString(xmlString, "text/xml");
198                         return xmlDoc;
199                 }
200
201                 /**
202                  * Checks given XML string for validity. If it's invalid, an error message is shown
203                  * Relies on the current browser's XML handling returning a HTML document if something went wrong during parsing
204                  *
205                  * @return XMLdocument if XML is valid, false otherwise
206                  */
207                 function checkXMLValidityAndShowErrorIfInvalid(xmlString) {
208                         var doc = getDocument(xmlString);
209                         var errorMsg = "";
210                         if (doc.firstChild.localName == "html") {
211                                 errorMsg = new XMLSerializer().serializeToString(doc);
212                         } else {
213                                 // at Chrome, the error may be nested in the XML
214
215                                 // quick hack, only "xhtml" is quered
216                                 function nsResolover(x) {
217                                         return "http://www.w3.org/1999/xhtml";
218                                 }
219
220                                 var element = doc.evaluate( '//x:parsererror', doc, nsResolover, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
221                                 if (element !== null) {
222                                         errorMsg = new XMLSerializer().serializeToString(element);
223                                 }
224                         }
225
226                         if (errorMsg !== "") {
227                                 vShowError(errorMsg);
228                                 return false;
229                         } else {
230                                 return doc;
231                         }
232                 }
233
234                                 /**
235                  * Updates the XML in the orion editor based on the values given in the input fields.
236                  * Shows error if XML is invalid
237                  *
238                  * Works only with SELECT2 fields
239                  *
240                  * @param idPrefix: (new|edit)${shortName}, derived names: [idPrefix]Name, [idPrefix]Id, Orion[idPrefix]XML
241                  * @param hasIdField: whether the Id should be read and written
242                  * @param selectFields: array of {attribute, fieldSuffix}, where attribute is a name of an attribute having a QName.
243                  *              Each select box is determined by #[idPrefix][fieldSuffix].
244                  *              The select box content is converted to a QName and the result is written to the attribute [name]
245                  *              Default: {attribute: "type", fieldSuffix: "Type"}
246                  *
247                  * @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})
248                  *      {id:id, name:name, type: "ns5:type", typeFullQName: "{http://www.example.org}type"}
249                  */
250                 function synchronizeNameAndType(idPrefix, hasIdField, selectFields) {
251                         if (typeof hasIdField === undefined) {
252                                 hasIdField = true;
253                         }
254                         if (typeof selectFields === undefined) {
255                                 selectFields = [{attribute: "type", fieldSuffix: "Type"}];
256                         }
257
258
259                         var val = window.winery.orionareas["Orion" + idPrefix + "XML"].editor.getText();
260                         var xmlDoc = checkXMLValidityAndShowErrorIfInvalid(val);
261                         if (xmlDoc) {
262                                 // handle name
263                                 var name = $("#" + idPrefix + "Name").val();
264                                 // initialize result object
265                                 var res = {
266                                         name: name
267                                 };
268                                 xmlDoc.firstChild.setAttribute("name", name);
269
270                                 // write id and name to XML
271                                 if (hasIdField) {
272                                         var id = $("#" + idPrefix + "Id").val();
273                                         if (!id) {
274                                                 // TODO a checking should be done if the id exists
275                                                 // probably not here, but at caller's side
276                                                 id = name;
277                                         }
278                                         xmlDoc.firstChild.setAttribute("id", id);
279                                         res.id = id;
280                                 }
281
282                                 // write each selectField to xml
283                                 // for that, we have to determine the QName
284                                 $(selectFields).each(function(i, m) {
285                                         var content = $("#" + idPrefix + m.fieldSuffix).select2("val");
286
287                                         if (content == VALUE_OF_NONE_CHOOSEN) {
288                                                 // if nothing is chosen do not put it into the result
289                                                 return;
290                                         }
291
292                                         // determine qname of type
293                                         //getQNameOutOfFullQName(type, xmlDoc.firstChild) does not always work as xmlDoc.firstChild does not have ALL *available* namespace prefixes
294                                         var typeNSAndId = getNamespaceAndLocalNameFromQName(content);
295                                         var prefix = xmlDoc.firstChild.lookupPrefix(typeNSAndId.namespace);
296                                         if (!prefix) {
297                                                 // we have to ask the repo for a prefix
298                                                 $.ajax({
299                                                         type: "GET",
300                                                         async: false,
301                                                         "url": winery.repositoryURL + "/admin/namespaces/" + encodeID(typeNSAndId.namespace),
302                                                         dataType: "text",
303                                                         error: function(jqXHR, textStatus, errorThrown) {
304                                                                 vShowAJAXError("Could not determine prefix", jqXHR, errorThrown);
305                                                         },
306                                                         success: function(resData, textStatus, jqXHR) {
307                                                                 prefix = resData;
308                                                         }
309                                                 });
310                                                 // new prefix fetched, xmlns attribute has to be written
311                                                 xmlDoc.firstChild.setAttribute("xmlns:" + prefix, typeNSAndId.namespace);
312                                         }
313                                         var qname = prefix + ":" + typeNSAndId.localname;
314                                         res[m.attribute] = qname;
315                                         res[m.attribute + "FullQName"] = typeNSAndId;
316                                         xmlDoc.firstChild.setAttribute(m.attribute, qname);
317                                 });
318
319                                 var xml = new XMLSerializer().serializeToString(xmlDoc);
320                                 res.xml = xml;
321
322                                 return res;
323                         } else {
324                                 return false;
325                         }
326                 }
327
328
329         }
330 );