[internal #1720434] Minor bugfixes + SVG displays

Signed-off-by: Tamas Levente Kiss <tamas.levente.kiss@ericsson.com>
diff --git a/htdocs/Utils/DsRestAPI/DsRestAPIComm.js b/htdocs/Utils/DsRestAPI/DsRestAPIComm.js
index e610427..9741189 100644
--- a/htdocs/Utils/DsRestAPI/DsRestAPIComm.js
+++ b/htdocs/Utils/DsRestAPI/DsRestAPIComm.js
@@ -56,17 +56,19 @@
                     $('#cll_DsRestAPI_error').addClass("hidden");
                     if (data && data !== "" && data !== " ")
                     {
+                        var contentlist;
                         try {
-                            var contentlist = JSON.parse(data).contentList;
-                            if (contentlist.length == 1 && contentlist[0].node != undefined && contentlist[0].node.val == "timeout")
-                            {
-                                mThis.handleError("Timeout received from server", "Overload", aHandler, end, start);
-                            }
-                            else
-                                aHandler(contentlist);
+                            contentlist = JSON.parse(data).contentList;
                         } catch (e) {
                             mThis.handleError("Badly encoded response received from server", e.message, aHandler, end, start);
+                            return;
                         }
+                        if (contentlist.length == 1 && contentlist[0].node != undefined && contentlist[0].node.val == "timeout")
+                        {
+                            mThis.handleError("Timeout received from server", "Overload", aHandler, end, start);
+                        }
+                        else
+                            aHandler(contentlist);
                     }
                     else
                         aHandler(mNoAnswer);
diff --git a/htdocs/Utils/DsRestAPI/RequestSchema.json b/htdocs/Utils/DsRestAPI/RequestSchema.json
index 7ab295c..58bb5ef 100644
--- a/htdocs/Utils/DsRestAPI/RequestSchema.json
+++ b/htdocs/Utils/DsRestAPI/RequestSchema.json
@@ -105,6 +105,9 @@
                         "ptcname": {
                             "type": "string"
                         },
+                        "clientSideCache": {
+                            "type": "boolean"
+                        },
                         "element": {
                             "type": "string"
                         },
@@ -194,6 +197,9 @@
                         "ptcname": {
                             "type": "string"
                         },
+                        "clientSideCache": {
+                            "type": "boolean"
+                        },
                         "element": {
                             "type": "string"
                         },
diff --git a/htdocs/Utils/DsRestAPI/openapi.yaml b/htdocs/Utils/DsRestAPI/openapi.yaml
index fc1a445..ecfe9f2 100644
--- a/htdocs/Utils/DsRestAPI/openapi.yaml
+++ b/htdocs/Utils/DsRestAPI/openapi.yaml
@@ -71,6 +71,8 @@
               type: string

             ptcname:

               type: string

+            clientSideCache:

+              type: boolean

             element:

               type: string

             params:

@@ -125,6 +127,8 @@
               type: string

             ptcname:

               type: string

+            clientSideCache:

+              type: boolean

             element:

               type: string

             content:

diff --git a/htdocs/Utils/FileHandler.js b/htdocs/Utils/FileHandler.js
index 2682094..3a13066 100644
--- a/htdocs/Utils/FileHandler.js
+++ b/htdocs/Utils/FileHandler.js
@@ -39,6 +39,7 @@
             url: url,
             type: 'LSDIR',
             success: function(aData) {callback(JSON.parse(aData).fileList.sort());},
+            error: function(aData) {callback(0);},
             data: "",
             dataType: 'text',
             contentType: 'text',
diff --git a/htdocs/Utils/Utilities.js b/htdocs/Utils/Utilities.js
index 68b463e..a996844 100644
--- a/htdocs/Utils/Utilities.js
+++ b/htdocs/Utils/Utilities.js
@@ -33,7 +33,7 @@
 

 window.jsErrors = [];

 

-if (!window.onerror) {

+/*if (!window.onerror) {

     window.onerror = function(error, url, line, ch, ex) {

         "use strict";

         var errorMessage = 'Error caught in global context: "{0}", URL: {1}, Line {2}, char index {3} (w/o indent, usually).'.format(error, url, line, ch);

@@ -46,7 +46,7 @@
         window.jsErrors[window.jsErrors.length] = stack;

         return true; // handled.

     };

-}

+}*/

 

 /** mpad - converts a number to string and pads it with leading zeroes */

 function mpad(num, size) {

@@ -295,4 +295,8 @@
     return arr.join('');

 }

 

+function replaceAt(str, index, replacement) {

+    return str.substr(0, index) + replacement+ str.substr(index + replacement.length);

+}

+

 //# sourceURL=Utils\Utilities.js

diff --git a/htdocs/WebApplicationFramework/Res/Main.css b/htdocs/WebApplicationFramework/Res/Main.css
index 6d9ce1d..09fba4d 100644
--- a/htdocs/WebApplicationFramework/Res/Main.css
+++ b/htdocs/WebApplicationFramework/Res/Main.css
@@ -49,10 +49,8 @@
 .line {
 	clear: both;
 	width: 100%;
-	margin-top: 6px;
-	border-top: 1px solid #C4C4C3;
-	border-bottom: 1px solid #ffffff;
-	margin-bottom: 6px;
+	margin-top: 7px;
+	margin-bottom: 7px;
 }
 
 .tabs {
@@ -85,9 +83,9 @@
 }
 
 #webapps BUTTON, #hiddenWebapps BUTTON, #webappToggleButton {
-    height: 30px;
+    height: 26px;
     color: #000;
-    border: 2px solid #c5c5c5;
+    border: 0px solid #c5c5c5;
     background-color: #c5c5c5;
     text-align: center;
     text-decoration: none;
diff --git a/htdocs/WebApplicationFramework/Views/View_ElementTable_blue.css b/htdocs/WebApplicationFramework/Views/View_ElementTable_blue.css
new file mode 100644
index 0000000..856eb8e
--- /dev/null
+++ b/htdocs/WebApplicationFramework/Views/View_ElementTable_blue.css
@@ -0,0 +1,178 @@
+.ElementTable,
+.TableBlue {
+    font-weight: normal;
+    width: 100%;
+    height: 100%;
+    white-space: nowrap;
+    -webkit-box-sizing: border-box;
+       -moz-box-sizing: border-box;
+            box-sizing: border-box;
+}
+
+.ElementTable_HeaderBar,
+.TableBlue_HeaderBar {
+    -webkit-box-sizing: border-box;
+       -moz-box-sizing: border-box;
+            box-sizing: border-box;
+    width: 100%;
+    overflow: hidden;
+    padding-left: 2px;
+    white-space: nowrap;
+    /*border-bottom: 1px solid black;*/
+}
+
+.ElementTable_HeaderBar {
+    background-color: #55BAD4;
+}
+
+.TableBlue_HeaderBar {
+    background-color: #6494e9;
+}
+
+.ElementTable_HeaderBar > *,
+.TableBlue_HeaderBar > * {
+    display: inline-block;
+    vertical-align: middle;
+    overflow: hidden;
+    white-space: nowrap;
+}
+
+.ElementTable_Name{
+    width: 50%;
+    font-weight: bold;
+}
+
+.TableBlue_Name {
+    width: 50%;
+    font-weight: bold;
+}
+
+.ElementTable_SearchLabel,
+.TableBlue_SearchLabel {
+    width: 15%;
+    text-align: right;
+}
+
+.ElementTable_SearchInput,
+.TableBlue_SearchInput {
+    width: 35%;
+}
+
+.ElementTable_TableWrapper,
+.TableBlue_TableWrapper {
+    overflow-y: auto;
+}
+
+.ElementTable table,
+.TableBlue table {
+    width: 100%;
+}
+
+.ElementTable table tr.ElementTable_Selected {
+    background-color: #66a19f;
+}
+
+.TableBlue table tr.TableBlue_Selected {
+    background-color: #6494e9;
+    color: white;
+}
+
+.ElementTable table,
+.TableBlue table {
+    border: none;
+    border-collapse: collapse;
+}
+
+.ElementTable table tr.ElementTable_Header,
+.TableBlue table tr.TableBlue_Header {
+    border-bottom: 1px solid black;
+    color: black;
+    font-weight: bold;
+    /*font-size: 14px;*/
+    text-align: left;
+    padding: 2px;
+}
+
+.ElementTable table tr.ElementTable_Header {
+    background-color: #66CBE5;
+}
+
+.TableBlue table tr.TableBlue_Header {
+    background-color: #6494e9;
+}
+
+.ElementTable_Sort_No,
+.ElementTable_Sort_Asc,
+.ElementTable_Sort_Desc,
+.TableBlue_Sort_No,
+.TableBlue_Sort_Asc,
+.TableBlue_Sort_Desc {
+    background-position: right center;
+    background-repeat: no-repeat;
+}
+.ElementTable_Sort_Asc,
+.TableBlue_Sort_Asc {
+    background-image: url("WebApplicationFramework/Res/sort_asc.png");
+}
+.ElementTable_Sort_Desc,
+.TableBlue_Sort_Desc {
+    background-image: url("WebApplicationFramework/Res/sort_desc.png");
+}
+.ElementTable_Sort_No,
+.TableBlue_Sort_No {
+    background-image: url("WebApplicationFramework/Res/sort_both.png");
+}
+
+.ElementTable td,
+.ElementTable th,
+.TableBlue th,
+.TableBlue td {
+    padding: 2px 2px;
+    font-weight: normal;
+}
+
+.ElementTable tr:nth-child(odd) {
+    background-color: #D4F0F8;
+    color: black;
+}
+
+.ElementTable tr.ElementTable_Odd {
+    background-color: #D4F0F8;
+}
+
+.ElementTable tr:nth-child(even) {
+    background-color: #F2FBFD;
+    color: black;
+}
+
+.ElementTable tr.ElementTable_Even {
+    background-color: #F2FBFD;
+}
+
+.ElementTable tr:hover {
+    background-color: #0066b3;
+    color: white;
+}
+
+.TableBlue tr:nth-child(odd) {
+    color: black;
+    background-color: #c5c5c5;
+}
+
+.TableBlue tr.TableBlue_Odd {
+    background-color: #c5c5c5;
+}
+
+.TableBlue tr:nth-child(even) {
+    color: black;
+    background-color: #c5c5c5;
+}
+
+.TableBlue tr.TableBlue_Even {
+    background-color: #c5c5c5;
+}
+
+.TableBlue tr:hover {
+    background-color: #6494e9;
+    color: white;
+}
\ No newline at end of file
diff --git a/htdocs/WebApplicationFramework/Views/View_MultipliedViewAligner.js b/htdocs/WebApplicationFramework/Views/View_MultipliedViewAligner.js
index a34478f..e05c64b 100644
--- a/htdocs/WebApplicationFramework/Views/View_MultipliedViewAligner.js
+++ b/htdocs/WebApplicationFramework/Views/View_MultipliedViewAligner.js
@@ -103,6 +103,14 @@
                     subview.applicationCreated();
                     ViewUtils.applyCss(customData, id);
                     ViewUtils.processCss(customData, parentId);
+                    $("#" + parentId).on("click", function(a) {
+                        
+                        var index = $(this).index();
+                        if (index >= 0) {
+                            v_dataViewmodel.select(index);
+                        }
+                    });
+                    
                     v_multipliedSubviews.push(subview);
                     subview.refresh(true);
                     
diff --git a/htdocs/WebApplicationFramework/Views/View_SVGDisplay.css b/htdocs/WebApplicationFramework/Views/View_SVGDisplay.css
new file mode 100644
index 0000000..ad1d4b2
--- /dev/null
+++ b/htdocs/WebApplicationFramework/Views/View_SVGDisplay.css
@@ -0,0 +1,26 @@
+.BasicSVGDisplay,
+.HeaderSVGDisplay,
+.StripedSVGDisplay {
+    width: 100%;
+    height: 100%;
+    box-sizing: border-box;
+    padding-right: 5px;
+}
+
+.StripedSVGDisplay {
+    background: repeating-linear-gradient(144deg, #F1F1F1, #F1F1F1 5px, #E8E8E8 5px, #E8E8E8 10px);
+    background: -moz-repeating-linear-gradient(144deg, #F1F1F1, #F1F1F1 5px, #E8E8E8 5px, #E8E8E8 10px);
+}
+
+.HeaderSVGDisplay {
+    background-color: #bbbbbb;
+    /*font-size: 12px;*/
+}
+
+.BasicSVGDisplay > DIV,
+.StripedSVGDisplay > DIV {
+    width: 0px;
+    height: 25px;
+    display: inline-block;
+    vertical-align: middle;
+}
\ No newline at end of file
diff --git a/htdocs/WebApplicationFramework/Views/View_SVGDisplay.js b/htdocs/WebApplicationFramework/Views/View_SVGDisplay.js
new file mode 100644
index 0000000..512fe7a
--- /dev/null
+++ b/htdocs/WebApplicationFramework/Views/View_SVGDisplay.js
@@ -0,0 +1,126 @@
+// Copyright (c) 2000-2019 Ericsson Telecom AB Telecom AB                                                       //
+// All rights reserved. This program and the accompanying materials are made available under the     //
+// terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at //
+// https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html                                        //
+///////////////////////////////////////////////////////////////////////////////////////////////////////
+
+function CView_SVGDisplay(p_viewmodels, p_objId, p_parentId, p_data) {

+    "use strict";

+    

+    /** constructor */

+    var v_viewmodels = ViewUtils.getViewmodelsFromExpectedInterface(p_viewmodels, CView_SVGDisplay);

+    var v_viewmodel = v_viewmodels[0];

+    var v_conditionViewmodel = v_viewmodels[1];

+    var v_writableViewmodel = v_viewmodels[2];

+    

+    var v_objId = p_objId;

+    var v_parentId = p_parentId;

+    var v_customData = p_data;

+    

+    var v_SVGDisplayLabel;

+    var v_currentSVG = "";

+    

+    /** public functions */

+    this.applicationCreated = function() {

+        $("#" + v_parentId).append(getHtml());

+        if (v_customData.isElementLabelPresent) {

+            v_SVGDisplayLabel = ViewUtils.addLabel(v_objId, v_customData.elementText);

+        }

+        if (v_customData.class != undefined) {

+            $("#" + v_objId).addClass(v_customData.class);

+        } else {

+            $("#" + v_objId).addClass("BasicSVGDisplay");

+        }

+

+        ViewUtils.addTooltip(v_objId, v_customData);

+    };

+

+    this.refresh = function(p_fullRefresh) {

+        if (ViewUtils.checkVisibility(v_conditionViewmodel, v_objId) && v_viewmodel != undefined) {

+            var dataObject = v_viewmodel.getListWithElementInfo();

+            if (v_customData.text != undefined || dataObject == undefined || dataObject.values[0] == undefined) {

+                return;

+            }

+

+            if (p_fullRefresh && v_customData.isElementLabelPresent && v_customData.elementText == undefined) {

+                v_SVGDisplayLabel.text(dataObject.values[0].element);

+            }

+            

+            if (v_customData.text == undefined) {

+                var text = "";
+                //console.log(dataObject)

+                if(dataObject.values[0].val != undefined) {

+                    $("#" + v_objId + " > *").css("display", "");

+                    $("#" + v_objId).removeClass("NoData");

+                    text = dataObject.values[0].val;

+                } else {

+                    $("#" + v_objId + " > *").css("display", "none");

+                    $("#" + v_objId).addClass("NoData");

+                }

+                if (text != v_currentSVG)
+                {
+                    v_currentSVG = text;
+                    try {
+                        var dectext = window.atob(text);
+                        dectext = dectext.replace("style=\"", "style=\"transform:scale(0.75);");
+                        $("#" + v_objId).html(dectext);
+                    } catch (e) {
+                        text = text.replace("style=\"", "style=\"transform:scale(0.75);");
+                        $("#" + v_objId).html(text);
+                    }
+                }

+            }

+

+            if (v_customData.disabled !== true && dataObject.values[0] != undefined && dataObject.values[0].isWritable && (v_writableViewmodel == undefined || v_writableViewmodel.isWritable())) {
+            } else {
+            }

+        }

+    };

+

+    /** private functions */

+    function getHtml() {

+        return '<div id="' + v_objId + '"></div>';

+    }

+}

+

+CView_SVGDisplay.getHelp = function() {

+    return "Simple SVG display. Expected interface: \n" + JSON.stringify(CView_SVGDisplay.expectsInterface(), null, 4);

+}

+

+CView_SVGDisplay.expectsInterface = function() {

+    return [

+        {

+            "optional": ["getListWithElementInfo"]

+        },

+        {

+            "optional": ["getState"]

+        },

+        {

+            "optional": ["isWritable"]

+        }

+    ];

+};

+

+CView_SVGDisplay.getCustomDataSchema = function() {

+    var schema = {

+        "$schema": "http://json-schema.org/draft-04/schema#",

+        "title": "Custom data for CView_SVGDisplay",

+        "type": "object",

+        "properties": {

+            "class": {

+                "type": "string",

+                "description": "the css class of the view",

+                "default": "BasicSVGDisplay"

+            },

+            "text": {

+                "type": "string",

+                "description": "the SVGDisplay text"

+            }

+        },

+        "additionalProperties": false

+    };

+    $.extend(true, schema, ViewUtils.commonViewSchema, ViewUtils.commonElementSchema);

+    return schema;

+};
+

+//# sourceURL=WebApplicationFramework\Views\View_SVGDisplay.js
\ No newline at end of file
diff --git a/htdocs/WebApplicationFramework/Views/View_TextArea.js b/htdocs/WebApplicationFramework/Views/View_TextArea.js
new file mode 100644
index 0000000..1d575e0
--- /dev/null
+++ b/htdocs/WebApplicationFramework/Views/View_TextArea.js
@@ -0,0 +1,153 @@
+// Copyright (c) 2000-2019 Ericsson Telecom AB Telecom AB                                                       //
+// All rights reserved. This program and the accompanying materials are made available under the     //
+// terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at //
+// https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html                                                         //
+///////////////////////////////////////////////////////////////////////////////////////////////////////
+function CView_TextArea(p_viewmodels, p_mainId, p_parentId, p_data) {

+    "use strict";

+    

+    /** constructor */

+    

+    var v_viewmodels = ViewUtils.getViewmodelsFromExpectedInterface(p_viewmodels, CView_TextArea);

+    var v_viewmodel = v_viewmodels[0];

+    var v_conditionViewmodel = v_viewmodels[1];

+    var v_writableViewmodel = v_viewmodels[2];

+    

+    var v_mainId = p_mainId;

+    var v_parentId = p_parentId;

+    var v_customData = p_data;

+    

+    var v_textArea;

+    var v_selected = false;

+    var v_currentValue = "";

+    

+    var v_this = this;

+    

+    /** public functions */

+

+    this.applicationCreated = function() {

+        $("#" + v_parentId).append(getHtml());

+        if (v_customData.isElementLabelPresent) {

+            v_label = ViewUtils.addLabel(v_mainId, v_customData.elementText);

+        }

+        if (v_customData.class != undefined) {

+            $("#" + v_mainId).addClass(v_customData.class);

+        } else {

+            $("#" + v_mainId).addClass("BasicLabel");

+        }

+        

+        var lTextArea = $("#" + v_mainId + " textArea");

+        

+        if (v_customData.disabled !== true && v_customData.text == undefined && v_viewmodel != undefined) {

+            lTextArea.on({

+                focus: function() {

+                    v_selected = true;

+                },

+                blur: function() {

+                    var newValue = $(this).val();

+                    if (newValue != v_currentValue) {

+                        v_viewmodel.setValue(0, newValue);

+                    }

+                    v_selected = false;

+                },

+                keydown: function(event) {

+                    if (event.keyCode == 13) {

+                        $(this).blur();

+                    }

+                }

+            });

+        }

+        

+        if (v_viewmodel == undefined || v_customData.text != undefined) {

+            $("#" + v_mainId + " > textArea").prop('disabled', true);

+        }

+        

+        ViewUtils.addTooltip(v_mainId, v_customData);

+    };

+

+    this.refresh = function(p_fullRefresh) {

+        if (ViewUtils.checkVisibility(v_conditionViewmodel, v_mainId) && v_viewmodel != undefined) {

+            var dataObject = v_viewmodel.getListWithElementInfo();

+            if (v_customData.text != undefined || dataObject == undefined || dataObject.values[0] == undefined) {

+                return;

+            }

+            

+            var lTextArea = $("#" + v_mainId + " textArea");

+            

+            if (p_fullRefresh && v_customData.isElementLabelPresent && v_customData.elementText == undefined) {

+                v_label.text(dataObject.values[0].element);

+            }

+            

+            if (!v_selected && v_customData.text == undefined) {

+                var text = "";

+                if(dataObject.values[0].val != undefined) {

+                    $("#" + v_mainId + " > *").css("display", "");

+                    $("#" + v_mainId).removeClass("NoData");

+                    text = dataObject.values[0].val;

+                } else {

+                    $("#" + v_mainId + " > *").css("display", "none");

+                    $("#" + v_mainId).addClass("NoData");

+                }

+                // we need both...

+                lTextArea.val(text);

+                lTextArea[0].setAttribute('value', text);

+                v_currentValue = text;

+            }

+            

+            if (v_customData.disabled !== true && dataObject.values[0] != undefined && dataObject.values[0].isWritable && (v_writableViewmodel == undefined || v_writableViewmodel.isWritable())) {

+                $("#" + v_mainId + " > textArea").prop('disabled', false);

+            } else {

+                $("#" + v_mainId + " > textArea").prop('disabled', true);

+            }

+        }

+    };

+

+    /** private functions */

+    

+    function getHtml() {

+        return '<div id="' + v_mainId + '">' +

+            '<div></div><textarea spellcheck="false" rows="50" cols="40" >' + (v_customData.text != undefined ? v_customData.text : '') + '</textarea>' + 

+        '</div>';

+    }

+}

+

+CView_TextArea.getHelp = function() {

+    return "A single text area that can also be used for input.";

+}

+

+CView_TextArea.expectsInterface = function() {

+    return [

+        {

+            "optional": ["setValue", "getListWithElementInfo"]

+        },

+        {

+            "optional": ["getState"]

+        },

+        {

+            "optional": ["isWritable"]

+        }

+    ];

+};

+

+CView_TextArea.getCustomDataSchema = function() {

+    var schema = {

+        "$schema": "http://json-schema.org/draft-04/schema#",

+        "title": "Custom data for CView_TextArea",

+        "type": "object",

+        "properties": {

+            "class": {

+                "type": "string",

+                "description": "the css class of the view",

+                "default": "BasicLabel"

+            },

+            "text": {

+                "type": "string",

+                "description": "the text area text"

+            }

+        },

+        "additionalProperties": false

+    };

+    $.extend(true, schema, ViewUtils.commonViewSchema, ViewUtils.commonElementSchema);

+    return schema;

+};

+//# sourceURL=WebApplicationFramework\Views\View_TextArea.js
\ No newline at end of file
diff --git a/htdocs/WebApplications/CustomizableApp/Main.js b/htdocs/WebApplications/CustomizableApp/Main.js
index 509ecb7..f207795 100644
--- a/htdocs/WebApplications/CustomizableApp/Main.js
+++ b/htdocs/WebApplications/CustomizableApp/Main.js
@@ -1,8 +1,8 @@
-// Copyright (c) 2000-2019 Ericsson Telecom AB Telecom AB                                                       //
-// All rights reserved. This program and the accompanying materials are made available under the     //
-// terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at //
-// https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html                                                         //
-///////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2000-2019 Ericsson Telecom AB Telecom AB                                                       //

+// All rights reserved. This program and the accompanying materials are made available under the     //

+// terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at //

+// https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html                                                         //

+///////////////////////////////////////////////////////////////////////////////////////////////////////

 /** Common functionality for applications, such as

  - mainLoop,

  - request issuing

@@ -178,8 +178,31 @@
         mInterval = undefined;

     };

 

-    this.notifyChange = function(aDisableViews, aFiredFromLoop)

+    function searchObjecForValue(obj, query)

     {

+        for (var key in obj) 

+        {

+            var value = obj[key];

+            if (typeof value === 'object')

+                return searchObjecForValue(value, query);

+            if (typeof value == "string" && value.startsWith(query)) 

+                return value;

+        }

+        return false;

+    }

+

+    this.notifyChange = function(aDisableViews, aFiredFromLoop, aResponseInCaseOfSetData)

+    {

+        if (aResponseInCaseOfSetData)

+        {

+            var val = searchObjecForValue(aResponseInCaseOfSetData, "GoodbyeMessage:");

+            if (val)

+            {

+                mView.destroy();

+                mView.displayGoodbyeMessage(val.substring(val.indexOf(':') + 1));

+                mThis.stop();

+            }

+        }

         function dataArrived(aData) {

             if (aDisableViews && mViewModel.getUIConfig().overlayEnabledOnSelect) {

                 mView.enableViews();

@@ -224,7 +247,8 @@
     };

 

     this.disableViews = function(aDoNotShowSpinningCircle) {

-        mView.disableViews(aDoNotShowSpinningCircle);

+        if (mRunning == ERunningState.ERunning)

+            mView.disableViews(aDoNotShowSpinningCircle);

     };

 

     this.isRunning = function() {

diff --git a/htdocs/WebApplications/CustomizableApp/View.html b/htdocs/WebApplications/CustomizableApp/View.html
index edc1953..0fcb9d2 100644
--- a/htdocs/WebApplications/CustomizableApp/View.html
+++ b/htdocs/WebApplications/CustomizableApp/View.html
@@ -1,7 +1,7 @@
 <style id="WebAppStyle" type="text/css"></style>

 <style id="SetupStyle" type="text/css"></style>

 

-<div class="applications_header_class" style="position:relative;">

+<div class="applications_header_class" style="position:relative;left:160px">

     <table id="ajaxstats" class="DsRestAPIStatisticsTable">

         <tbody>

             <tr>

diff --git a/htdocs/WebApplications/CustomizableApp/View.js b/htdocs/WebApplications/CustomizableApp/View.js
index 23aaa36..0b1e953 100644
--- a/htdocs/WebApplications/CustomizableApp/View.js
+++ b/htdocs/WebApplications/CustomizableApp/View.js
@@ -167,15 +167,19 @@
             if (aDoNotShowSpinningCircle) {

                 html = '<div class="ui-widget-overlay ui-front" style="z-index: 1000; opacity: ' + opacity + ';"></div>';

             } else {

-                html = '<div class="ui-widget-overlay ui-front" style="z-index: 1000; opacity: ' + opacity + ';"></div><img src="WebApplicationFramework/Res/waiting.gif" class="waitingImage">';

+                html = '<div class="ui-widget-overlay ui-front" style="z-index: 1000; opacity: ' + opacity + ';"></div><center><img src="WebApplicationFramework/Res/waiting.gif" class="waitingImage"></center>';

             }

             $(m_parentDiv).append(html);

         }

     };

     

-    this.enableViews = function() {

-        $(".ui-widget-overlay").remove();

-        $(".waitingImage").remove();

+    this.enableViews = function() {
+        $(".ui-widget-overlay").remove();
+        $(".waitingImage").remove();
+    };
+    
+    this.displayGoodbyeMessage = function(aMessage) {
+        $(m_parentDiv).html('<div style="height: 100px;"></div> <div style="height: 100px; line-height: 100px; text-align: center; font-size: 20px;background-color: #6494e9;">' + aMessage + '</div>');
     };

 

     /** private functions */

diff --git a/htdocs/WebApplications/CustomizableApp/ViewModel.js b/htdocs/WebApplications/CustomizableApp/ViewModel.js
index eb69298..ba0bd56 100644
--- a/htdocs/WebApplications/CustomizableApp/ViewModel.js
+++ b/htdocs/WebApplications/CustomizableApp/ViewModel.js
@@ -22,6 +22,7 @@
     var mResponse;
     var mModel = aModel;
     var mThis = this;
+    var mRequestCache = {};
 
     var mSelectionToChangeToValue = [];
 
@@ -201,7 +202,7 @@
                 mBinder.enableViews();
             if (aCallback != undefined)
                 aCallback(response);
-            mBinder.notifyChange();
+            mBinder.notifyChange(false, false, response);
         }
 
         if (aAdditionalData == undefined) {
@@ -278,6 +279,7 @@
                         mViewIsDisabledBecauseConnectionWasInterrupted = false;
                         mBinder.enableViews();
                     }
+                    processStaticResponses(aResponse, mRequest, []);
                     mResponse = aResponse;
                 } else {
                     mViewIsDisabledBecauseConnectionWasInterrupted = true;
@@ -286,6 +288,7 @@
             }
             aHandler(aResponse);
         }
+        processStaticRequests(mRequest, []);
         mDsRestAPI.getList(mRequest, saveResponse);
     };
 
@@ -376,6 +379,24 @@
         }
     }
 
+    function getRequestKeyVMS(p_path) {
+        var key = '';
+        var currentPath = [];
+        for (var i = 0; i < p_path.length - 1; ++i) {
+            currentPath.push(p_path[i]);
+            var request = mThis.getRequestFromPath(currentPath);
+            if (request.getData.selectionValues != undefined) {
+                key += request.getData.selectionValues + "SV_";
+            } else if (request.getData.selection != undefined) {
+                key += request.getData.selection[0] + 'S_';
+            }
+        }
+
+        key += JSON.stringify(p_path) + 'P_';
+
+        return key;
+    }
+    
     function getRequestKey(p_path, p_type) {
         var key = '';
         var currentPath = [];
@@ -512,5 +533,89 @@
             p_path.pop(i);
         }
     }
+    
+    function processStaticResponses(p_contentList, p_requests, p_path, indxInList) 
+    {
+        for (var i = 0; i < p_requests.length; ++i) 
+        {
+            var request = p_requests[i].getData;
+            var response = p_contentList[i];
+            p_path.push(i);
+            if (request.clientSideCache != undefined && request.clientSideCache)
+            {
+                var key = getRequestKeyVMS(p_path);
+                if (mRequestCache[key] == undefined && indxInList == undefined)
+                {
+                    if (response.node != undefined && response.node.val.includes('Unhandled request'))
+                    {}
+                    else
+                        mRequestCache[key] = response;
+                }
+                else if (indxInList != undefined) 
+                {
+                    if (mRequestCache[key] == undefined)
+                        mRequestCache[key] = {};
+                    if (mRequestCache[key][indxInList] == undefined)
+                    {
+                        if (response.node != undefined && response.node.val.includes('Unhandled request'))
+                            mRequestCache[key] = undefined;
+                        else
+                            mRequestCache[key][indxInList] = response;
+                    }
+                }
+                
+                if (mRequestCache[key] != undefined)
+                {
+                    if (indxInList == undefined)
+                        p_contentList[i] = mRequestCache[key];
+                    else 
+                        p_contentList[i] = mRequestCache[key][indxInList];
+                }
+            }
+         
+            if (response.node != undefined)
+            {
+                if (request.children != undefined && response.node.childVals != undefined)
+                    processStaticResponses(response.node.childVals, request.children, p_path);
+            }
+            else if (response.list != undefined) 
+            {
+                for (var j = 0; j < response.list.length; ++j) 
+                {
+                    var responseNode = response.list[j];
+                    if (responseNode.node.childVals != undefined && responseNode.node.childVals.length > 0) 
+                        processStaticResponses(responseNode.node.childVals, request.children, p_path, j);
+                }
+            }
+            p_path.pop(i);
+        }
+    }
+    
+    function processStaticRequests(p_requests, p_path) 
+    {        
+        for (var i = 0; i < p_requests.length; ++i) 
+        {
+            var request = p_requests[i].getData;
+            p_path.push(i);
+            if (request.clientSideCache != undefined && request.clientSideCache)
+            {
+                var key = getRequestKeyVMS(p_path, "selection");
+                if (mRequestCache[key] != undefined)
+                {
+                    if (request.element != "")
+                    {
+                        request.StoredRqElement = request.element;
+                        request.element = "";
+                    }
+                }
+                else if (request.element == "")
+                    request.element = request.StoredRqElement;
+            }
+
+            if (request.children != undefined) 
+                processStaticRequests(request.children, p_path);
+            p_path.pop(i);
+        }
+    }
 }
 //# sourceURL=CustomizableApp\ViewModel.js
diff --git a/htdocs/WebApplications/CustomizableApp/ViewModels/ViewModel_Condition.js b/htdocs/WebApplications/CustomizableApp/ViewModels/ViewModel_Condition.js
index 4af5dac..a3d4b5a 100644
--- a/htdocs/WebApplications/CustomizableApp/ViewModels/ViewModel_Condition.js
+++ b/htdocs/WebApplications/CustomizableApp/ViewModels/ViewModel_Condition.js
@@ -38,7 +38,7 @@
         if (response != undefined && response.node != undefined) {
             if (response.node.tp == 0) {
                 returnValue = false;
-            } else if (response.node.tp == 3 && request.getData.filter == undefined) {
+            } else if ((response.node.tp == 3 || response.node.tp == -3) && request.getData.filter == undefined) {
                 returnValue = response.node.val === "true";
             } else {
                 returnValue = true;
diff --git a/htdocs/WebApplications/CustomizableApp/ViewModels/ViewModel_ElementRelay.js b/htdocs/WebApplications/CustomizableApp/ViewModels/ViewModel_ElementRelay.js
index b897c16..af92ed0 100644
--- a/htdocs/WebApplications/CustomizableApp/ViewModels/ViewModel_ElementRelay.js
+++ b/htdocs/WebApplications/CustomizableApp/ViewModels/ViewModel_ElementRelay.js
@@ -89,6 +89,23 @@
         };
     }
 
+    function getEncodedVal(aVal)
+    {
+        if (mOptions.decodeBase64) 
+        {
+            try
+            {
+                return window.atob(aVal);
+            }
+            catch (e)
+            {
+                return aVal;
+                console.log("Base64 decode error", aVal);
+            }
+        }
+        else
+            return aVal;
+    }
     
     function getRow(node)
     {
@@ -101,13 +118,15 @@
             {
                 if (node.childVals[j].node)
                 {
-                    lChildren.push(node.childVals[j].node.val);
+                    lChildren.push(getEncodedVal(node.childVals[j].node.val));
                 }
                 else if (node.childVals[j].list)
                 {
                     var lList = [];
                     for (var i = 0; i < node.childVals[j].list.length; ++i)
-                        lList.push(node.childVals[j].list[i].node.val);
+                    {
+                        lList.push(getEncodedVal(node.childVals[j].list[i].node.val));
+                    }
                     lChildren.push(lList);
                     //console.log ("lListVM::", lList);
                 }
@@ -184,7 +203,7 @@
                 lData.error = lElement.error;
             else if (lElement.node)
             {
-                lData.val = lElement.node.val;
+                lData.val = getEncodedVal(lElement.node.val);
                 lData.isWritable = lElement.node.tp > 0;
                 lData.children = getChildren(lElement.node, aRq.getData)[1];
             }
@@ -201,7 +220,7 @@
                             
                             node = lElement.list[aRq.getData.selection[0]].node;
                         }
-                        lData.val = node.val;
+                        lData.val = getEncodedVal(node.val);
                         if (aRq.getData.children) {
                             var lChildren = getChildren(node);
                             if (aRq.getData.children) {
@@ -224,7 +243,7 @@
                     var lChildList = [];
                     
                     for (var i = 0; i < lElement.list.length; ++i) {
-                        lList.push(lElement.list[i].node.val);
+                        lList.push(getEncodedVal(lElement.list[i].node.val));
                         var lChildren = getChildren(lElement.list[i].node);
                         if (aRq.getData.children)
                         {
@@ -277,7 +296,13 @@
             "showOnlySelected": {
                 "type": "boolean",
                 "format": "checkbox",
-                "description": "whther we only show the selected element from an iterator",
+                "description": "whether we only show the selected element from an iterator",
+                "default": true
+            },
+            "decodeBase64": {
+                "type": "boolean",
+                "format": "checkbox",
+                "description": "if true, the returned data will be base64 decoded",
                 "default": true
             }
         }
diff --git a/htdocs/WebApplications/CustomizableApp/ViewModels/ViewModel_FSM_SVG.js b/htdocs/WebApplications/CustomizableApp/ViewModels/ViewModel_FSM_SVG.js
new file mode 100644
index 0000000..38e4af7
--- /dev/null
+++ b/htdocs/WebApplications/CustomizableApp/ViewModels/ViewModel_FSM_SVG.js
@@ -0,0 +1,128 @@
+// Copyright (c) 2000-2019 Ericsson Telecom AB Telecom AB                                                       //
+// All rights reserved. This program and the accompanying materials are made available under the     //
+// terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at //
+// https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html                                                         //
+///////////////////////////////////////////////////////////////////////////////////////////////////////
+
+function CViewModel_FSM_SVG(p_viewmodel, p_options) {
+    "use strict";
+
+    /** constructor */
+
+    var v_viewmodel = p_viewmodel;
+    var v_options = p_options;
+    var v_dataPaths = [];
+    var mDecodedSVGs = {};
+    
+    /** public functions - interface for parent */
+
+    this.setSelectionToControl = function(p_selection) {};
+    this.setBinder = function(p_binder) {};
+    this.setReponseDataPath = function(p_index, p_path) {
+        v_dataPaths[p_index] = p_path;
+    };
+
+    /** public functions - interface for views */
+    
+    this.getListWithElementInfo = function() {
+        var lValues = [];
+        var svgenc = v_viewmodel.getResponseElement(v_dataPaths[0]);
+        var state;
+        if (v_dataPaths[1] != undefined)
+            state = v_viewmodel.getResponseElement(v_dataPaths[1]);
+                
+        var lKey = getRequestKeyS(v_dataPaths[0]);
+
+        if (svgenc != undefined)
+        {
+            if (mDecodedSVGs[lKey] == undefined)
+            {
+                try
+                {
+                    mDecodedSVGs[lKey] = window.atob(svgenc.node.val);
+                }
+                catch (e)
+                {
+                    console.log("svg base64 decode error", svgenc);
+                }
+            }
+
+            var svgdec = mDecodedSVGs[lKey];
+            if (svgdec != undefined)
+            {
+                var lsvgdec = mcopy(svgdec);
+                if (state != undefined)
+                {
+                    state = state.node.val;
+                    var activeIndx = lsvgdec.indexOf(state);
+                    var colorindx = lsvgdec.lastIndexOf("<rect fill=\"#FEFECE\"", activeIndx);
+                    lsvgdec = replaceAt(lsvgdec, colorindx, "<rect fill=\"#ffafaf\"");
+                }
+                lValues[0] = {val: lsvgdec};
+            }
+        }
+        return {values: lValues};
+    };
+    
+    /** private functions */
+    
+    function getRequestKeyS(p_path) {
+        var key = '';
+        var currentPath = [];
+        for (var i = 0; i < p_path.length - 1; ++i) {
+            currentPath.push(p_path[i]);
+            var request = v_viewmodel.getRequestFromPath(currentPath);
+            if (request.getData.selectionValues != undefined) {
+                key += request.getData.selectionValues + "SV_";
+            } else if (request.getData.selection != undefined) {
+                key += request.getData.selection[0] + 'S_';
+            }
+        }
+        key += JSON.stringify(p_path) + 'P_';
+        return key;
+    }
+}
+
+CViewModel_FSM_SVG.getHelp = function() {
+    return "A viewmodel that can be used to display FSMs with colored active state.";
+}
+
+CViewModel_FSM_SVG.providesInterface = function() {
+    return ["getListWithElementInfo"];
+};
+
+CViewModel_FSM_SVG.getCustomDataSchema = function() {
+    return {
+        "$schema": "http://json-schema.org/draft-04/schema#",
+        "title": "Custom data for CViewModel_FSM_SVG",
+        "type": "object",
+        "properties": {
+            "schema": {
+                "description": "The json schema that the FSM SVG will use if a second data connection is not present.",
+                "type": "object"
+            }
+        },
+        "additionalProperties": false
+    };
+};
+
+CViewModel_FSM_SVG.expectsConnection = function() {
+    var dataConnections = [
+        {
+            "valueType": ["charstringType", "octetstringType"]
+        },
+        {
+            "valueType": ["charstringType"],
+            "optional": true
+        }
+    ];
+
+    var selectionConnections = [];
+
+    return {
+        "dataConnections": dataConnections,
+        "selectionConnections": selectionConnections
+    };
+};
+
+//# sourceURL=CustomizableApp\ViewModels\ViewModel_FSM_SVG.js
\ No newline at end of file
diff --git a/htdocs/WebApplications/CustomizableApp/ViewModels/ViewModel_Multiplier.js b/htdocs/WebApplications/CustomizableApp/ViewModels/ViewModel_Multiplier.js
index 7408508..1ca73d4 100644
--- a/htdocs/WebApplications/CustomizableApp/ViewModels/ViewModel_Multiplier.js
+++ b/htdocs/WebApplications/CustomizableApp/ViewModels/ViewModel_Multiplier.js
@@ -13,14 +13,17 @@
     
     var v_binder;
     var v_dataPaths = [];
-    
+    var v_selections = [];
+
     var v_currentBundle = [];
     
     var v_this = this;
     
     /** public functions - interface for parent */ 
 
-    this.setSelectionToControl = function() {};
+    this.setSelectionToControl = function(p_selection) {
+        v_selections.push(p_selection);
+    };
     
     this.setReponseDataPath = function(p_index, p_path) {
         v_dataPaths[p_index] = p_path;
@@ -32,6 +35,15 @@
 	
 	/** public functions - interface for views */ 
 
+    this.select = function(p_index) {
+        for (var i = 0; i < v_selections.length; ++i) {
+            v_viewmodel.select(v_selections[i], p_index);
+        }
+        if (v_selections.length > 0) {
+            v_binder.notifyChange(true);
+        }
+    };
+
     this.getViewmodelBundle = function() {
         var response = v_viewmodel.getResponseElement(v_dataPaths[0]);
         var numberOfBundles = 0;
@@ -207,8 +219,14 @@
         }
     ];
     
-    var selectionConnections = [];
-    
+    var selectionConnections = [
+        {
+            "dataElementOfDataConnection": 0,
+            "multiple": true,
+            "optional": true
+        }
+    ];
+ 
     return {
         "dataConnections": dataConnections,
         "selectionConnections": selectionConnections
diff --git a/htdocs/WebApplications/GuiEditor/ViewModels/ViewModel_SanityChecker.js b/htdocs/WebApplications/GuiEditor/ViewModels/ViewModel_SanityChecker.js
index 99151a7..bebc980 100644
--- a/htdocs/WebApplications/GuiEditor/ViewModels/ViewModel_SanityChecker.js
+++ b/htdocs/WebApplications/GuiEditor/ViewModels/ViewModel_SanityChecker.js
@@ -281,20 +281,21 @@
         var bestIndex;
         var bestScore = 0;
         var source = request.getData.source;
-        for (var i = 0; i < v_help.sources.length; ++i) {
-            if (v_help.sources[i].source == source) {
-                for (var j = 0; j < v_help.sources[i].dataElements.length; ++j) {
-                    var score = isCorrectElementForRequest(v_help.sources[i].dataElements[j].dataElement, request);
-                    if (score > bestScore) {
-                        result = {
-                            "index": j,
-                            "dataElement": v_help.sources[i].dataElements[j].dataElement
-                        };
-                        bestScore = score;
+        if (v_help)
+            for (var i = 0; i < v_help.sources.length; ++i) {
+                if (v_help.sources[i].source == source) {
+                    for (var j = 0; j < v_help.sources[i].dataElements.length; ++j) {
+                        var score = isCorrectElementForRequest(v_help.sources[i].dataElements[j].dataElement, request);
+                        if (score > bestScore) {
+                            result = {
+                                "index": j,
+                                "dataElement": v_help.sources[i].dataElements[j].dataElement
+                            };
+                            bestScore = score;
+                        }
                     }
                 }
             }
-        }
 
         if (bestScore == 0) {
             result = {
diff --git a/htdocs/WebApplications/GuiEditor/Views/View_ElementEditor.js b/htdocs/WebApplications/GuiEditor/Views/View_ElementEditor.js
index a160496..673063f 100644
--- a/htdocs/WebApplications/GuiEditor/Views/View_ElementEditor.js
+++ b/htdocs/WebApplications/GuiEditor/Views/View_ElementEditor.js
@@ -97,7 +97,8 @@
         v_request.getData.source = newRequest.source;

         v_request.getData.element = newRequest.element;

         v_parentView.requestRenamed(v_path, newRequest.element);

-        v_request.getData.ptcname = newRequest.ptcname;

+        v_request.getData.ptcname = newRequest.ptcname;
+        v_request.getData.clientSideCache = newRequest.clientSideCache;

         v_request.getData.params = newRequest.params;

         v_request.getData.selection = newRequest.selection;

         v_request.getData.selectionValues = newRequest.selectionValues;

@@ -126,6 +127,10 @@
             "ptcname": {

                 "description": "The ptc name",

                 "type": "string"

+            },
+            "clientSideCache": {
+                "description": "Set to true, the data is get only once from the server.",
+                "type": "boolean"
             },

             "params": {

                 "type": "array",

diff --git a/htdocs/WebApplications/RequestConsole/Views/View_ElementEditor.js b/htdocs/WebApplications/RequestConsole/Views/View_ElementEditor.js
index 911985b..738b597 100644
--- a/htdocs/WebApplications/RequestConsole/Views/View_ElementEditor.js
+++ b/htdocs/WebApplications/RequestConsole/Views/View_ElementEditor.js
@@ -101,7 +101,8 @@
         v_request[lGetOrSetData].source = newRequest.source;

         v_request[lGetOrSetData].element = newRequest.element;

         v_parentView.requestRenamed(v_path, newRequest.element);

-        v_request[lGetOrSetData].ptcname = newRequest.ptcname;

+        v_request[lGetOrSetData].ptcname = newRequest.ptcname;
+        v_request[lGetOrSetData].clientSideCache = newRequest.clientSideCache;

         v_request[lGetOrSetData].params = newRequest.params;

         v_request[lGetOrSetData].selection = newRequest.selection;

         v_request[lGetOrSetData].selectionValues = newRequest.selectionValues;

@@ -222,6 +223,10 @@
             "ptcname": {

                 "description": "The ptc name",

                 "type": "string"

+            },
+            "clientSideCache": {
+                "description": "Set to true, the data is get only once from the server.",
+                "type": "boolean"
             },

             "params": {

                 "type": "array",