Merge "[master] IFrame view added, minor fixes in rangeFilter handling - WebGUI_Improvements"
diff --git a/htdocs/Utils/Utilities.js b/htdocs/Utils/Utilities.js
index eea9d4f..23d7f60 100644
--- a/htdocs/Utils/Utilities.js
+++ b/htdocs/Utils/Utilities.js
@@ -1,8 +1,8 @@
-// Copyright (c) 2000-2017 Ericsson Telecom AB                                                       //
-// All rights reserved. This program and the accompanying materials are made available under the     //
-// terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at //
-// http://www.eclipse.org/legal/epl-v10.html                                                         //
-///////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2000-2017 Ericsson Telecom AB                                                       //

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

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

+// http://www.eclipse.org/legal/epl-v10.html                                                         //

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

 

 /**

  * printf() clone - String.prototype.format(formatStr, args...)

@@ -245,6 +245,38 @@
     return undefined;

 }

 

+// No modification if ip has http prefix and no port is given.

+// Adds http prefix if there isn't one.

+// Joins ip and port if both are given.

+// If there is an url with a trailing slash and a port is given, the trailing slash will be removed and the port will be added on.

+// If relativeURL is true, current hostname and port is used as the base address

+function urlifier(ip, port, relativeURL) {

+    var httpPrefix = 'http://';

+    if (ip == undefined) {

+    	ip = '';

+    }

+    if (ip != '' && relativeURL != undefined && relativeURL == true) {

+      currentAddress=window.location.hostname;

+      if (window.location.port != undefined && window.location.port != '') {

+        currentAddress = currentAddress + ":" + window.location.port;

+      }

+      ip = currentAddress + '/' + ip;

+      port=undefined;

+    }

+    if (ip != '' && ip.substr(0, httpPrefix.length).toLowerCase() !== httpPrefix) {

+        ip = httpPrefix + ip;

+    }

+    if (port != undefined) {

+        port = ':' + port;

+        if (ip.substr(ip.length - 1) === '/') {

+            ip = ip.substring(0, ip.length - 1);

+        }

+    } else {

+        port = '';

+    }

+    return ip + port;

+}

+

 // string.encode("hex")

 function hex2a(hex) {

     var str = '';

diff --git a/htdocs/WebApplicationFramework/Views/View_Iframe.js b/htdocs/WebApplicationFramework/Views/View_Iframe.js
new file mode 100644
index 0000000..7e90e66
--- /dev/null
+++ b/htdocs/WebApplicationFramework/Views/View_Iframe.js
@@ -0,0 +1,100 @@
+function CView_Iframe(p_viewmodels, p_mainId, p_parentId, p_data) {
+    "use strict";
+    
+    /** constructor */
+    
+    var v_mainId = p_mainId;
+    var v_parentId = p_parentId;
+    var v_customData = p_data;
+
+    var v_viewmodel = ViewUtils.getViewmodelsFromExpectedInterface(p_viewmodels, CView_Iframe)[0];
+    var v_conditionViewmodel = ViewUtils.getViewmodelsFromExpectedInterface(p_viewmodels, CView_Iframe)[1];
+    
+    var v_currentUrl = urlifier(v_customData.ip, v_customData.port, v_customData.relativeURL);
+    var v_this = this;
+
+    /** public functions */
+
+    this.applicationCreated = function() {
+        $("#" + v_parentId).append(getHtml());
+        if (v_customData.isElementLabelPresent) {
+            ViewUtils.addLabel(v_mainId, v_customData.elementText);
+        }
+        if (v_customData.class != undefined) {
+            $("#" + v_mainId).addClass(v_customData.class);
+        }
+    };
+
+    this.refresh = function() {   
+        if (ViewUtils.checkVisibility(v_conditionViewmodel, v_mainId) && v_viewmodel != undefined && v_viewmodel.getList() != undefined) {
+            var dataObject = v_viewmodel.getList().values;
+            var vl_ip;
+            if (dataObject == undefined || dataObject[0] == undefined) {
+                return;
+            }
+            if (dataObject.length>0 && dataObject[0].length>0 && dataObject[0][0].length>0) {
+              vl_ip = dataObject[0][0][0];
+            }
+            var vl_port;
+            if (dataObject.length>1 && dataObject[1].length>0 && dataObject[1][0].length>0) {
+              vl_port=dataObject[1][0][0]
+            }
+            var refreshedUrl = urlifier(vl_ip, vl_port, v_customData.relativeURL);
+
+            if (v_currentUrl != refreshedUrl) {
+                v_currentUrl = refreshedUrl;
+                $("#" + v_mainId).replaceWith(getHtml());
+            }
+        }
+    };
+
+    /** private functions */
+    
+    function getHtml() {
+        return '<iframe id="' + v_mainId + '" src="' + v_currentUrl + '"></iframe>';
+    }
+}
+
+CView_Iframe.getHelp = function() {
+    return "An iframe element. Any kind of locally avaliable url can be inserted as an iframe, by using a list input with the ip or url as the first member and port as optional second member.";
+}
+
+CView_Iframe.expectsInterface = function() {
+    return [
+        {
+            "optional": ["getList"]
+        }
+    ];
+};
+
+CView_Iframe.getCustomDataSchema = function() {
+    var schema = {
+        "$schema": "http://json-schema.org/draft-04/schema#",
+        "title": "Custom data for CView_Iframe",
+        "type": "object",
+        "properties": {
+            "class": {
+                "type": "string",
+                "description": "the css class of the iframe"
+            },
+            "ip": {
+                "type": "string",
+                "description": "the address (ip or url) of the iframe"
+            },
+            "port": {
+                "type": "string",
+                "description": "the optional port of the iframe"
+            },
+            "relativeURL": {
+                "description": "If true: current address is used as the base URL. Effective only when ip/port is not specified (default true)",
+                "type": "boolean",
+                "format": "checkbox",
+                "default": true
+            }
+        },
+        "additionalProperties": false
+    };
+    $.extend(true, schema, ViewUtils.commonViewSchema, ViewUtils.commonElementLabelSchema);
+    return schema;
+};
+//# sourceURL=WebApplicationFramework\Views\View_Iframe.js
\ No newline at end of file
diff --git a/htdocs/WebApplicationFramework/Views/View_ValueScale.css b/htdocs/WebApplicationFramework/Views/View_ValueScale.css
new file mode 100644
index 0000000..bec4898
--- /dev/null
+++ b/htdocs/WebApplicationFramework/Views/View_ValueScale.css
@@ -0,0 +1,4 @@
+.ValueScale svg {
+    width: 100%;
+    height: 100%;
+}
diff --git a/htdocs/WebApplicationFramework/Views/View_ValueScale.js b/htdocs/WebApplicationFramework/Views/View_ValueScale.js
new file mode 100644
index 0000000..9350bba
--- /dev/null
+++ b/htdocs/WebApplicationFramework/Views/View_ValueScale.js
@@ -0,0 +1,177 @@
+function CView_ValueScale(p_viewmodels, p_mainId, p_parentId, p_data) {
+    "use strict";
+    
+    /** constructor */
+    
+    var v_mainId = p_mainId;
+    var v_parentId = p_parentId;
+    var v_customData = p_data;
+
+    var defaultData = {
+		stroke : "#4CAF50",
+		strokeWidth : 7,
+		radius : 28
+    }
+    if (v_customData.minimumValue == undefined) {
+    	v_customData.minimumValue = 0;
+    }
+    if (v_customData.maximumValue == undefined) {
+    	v_customData.maximumValue = 100;
+    }
+    if (v_customData.yellowZone == undefined) {
+    	v_customData.yellowZone = 60;
+    }
+    if (v_customData.redZone == undefined) {
+    	v_customData.redZone = 80;
+    }
+
+    var v_viewmodel = ViewUtils.getViewmodelsFromExpectedInterface(p_viewmodels, CView_ValueScale)[0];
+    var v_this = this;
+
+    /** public functions */
+
+    this.applicationCreated = function() {
+        $("#" + v_parentId).append(getHtml());
+        if (v_customData.isElementLabelPresent) {
+            ViewUtils.addLabel(v_mainId, v_customData.elementText);
+        }
+        if (v_customData.class != undefined) {
+            $("#" + v_mainId).addClass(v_customData.class);
+        } else {
+            $("#" + v_mainId).addClass("ValueScale");
+        }
+
+    };
+
+    this.refresh = function() {
+        if (v_viewmodel != undefined) {
+            var list = v_viewmodel.getList();
+        	list.values[0][0] = (list.values[0][0]) / 100 * Math.random() + 20;
+            updateCircle(list.values[0][0]);
+        }
+    };
+
+    /** private functions */
+
+    function getHtml() {
+        return '<div id="' + v_mainId + '"><svg><path fill="none"' + defaultData.stroke + '" stroke-width="' + defaultData.strokeWidth + '"></path><text class="scalePercentage" x="0" y="21" fill="black"></text></svg><label class="scaleText"></label></div>';
+    }
+
+    function updateCircle(value) {
+    	var percent = getPercetageFromValue(value);
+        $("#" + v_mainId + " path").attr("d", getArc(percent));
+        $("#" + v_mainId + " path").attr("stroke", calculateColor(percent));
+        if (isNaN(percent)) {
+        	$("#" + v_mainId + " .scalePercentage").text("--%");
+        } else {
+        	$("#" + v_mainId + " .scalePercentage").text(Math.floor(percent) + "%");
+        	var currentWidth = $("#" + v_mainId).width() / 2 - 12;
+          $("#" + v_mainId + " text").attr("x", currentWidth);
+        }
+    }
+
+    function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
+        var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;
+
+        return {
+            x: centerX + (radius * Math.cos(angleInRadians)),
+            y: centerY + (radius * Math.sin(angleInRadians))
+        };
+    }
+    
+    function getPercetageFromValue(value) {
+    	var maxValue = v_customData.maximumValue;
+    	if (v_customData.maximumValue > v_customData.minimumValue && v_customData.minimumValue >= 0 && v_customData.maximumValue >= 0 && v_customData.maximumValue >= value && v_customData.minimumValue <= value) {
+    		value = value - v_customData.minimumValue;
+    		maxValue = maxValue - v_customData.minimumValue
+    	return (value / maxValue) * 100;
+    	}
+    }
+
+    function getArc(percent){
+        if (percent == 100.0) {
+            percent = 99.99;
+        }
+        
+        var radius = defaultData.radius;
+        var startAngle = -60;
+        var endAngle = 240.0 * percent / 100.0 / 2 - 60;
+        
+        var centerY = 33;
+        var centerX = $("#" + v_mainId).width() / 2;
+                
+        var start = polarToCartesian(centerX, centerY, radius, endAngle);
+        var end = polarToCartesian(centerX, centerY, radius, startAngle);
+
+        var arcSweep = endAngle - startAngle <= 180 ? "0" : "1";
+        var d = [
+            "M", start.x, start.y, 
+            "A", radius, radius, 0, arcSweep, 0, end.x, end.y
+        ].join(" ");
+
+        return d;       
+    }
+    
+    function calculateColor(percent){
+        var color;
+        if (percent != undefined) {
+        	color = "#4CAF50";
+            if (percent > v_customData.redZone) {
+            	color = "#DC143C";
+            } else if (percent > v_customData.yellowZone) {
+            	color = "#FFD700";
+            }
+        }
+        return color;
+    }
+}
+
+CView_ValueScale.getHelp = function() {
+    return "A value scale view. Requires a list whose first element is a percentage. Optionally, non percentage numbers can be given, but minimum and maximum range should be defined in custom data. Defaults are minimum 0 and maximum 100.";
+}
+
+CView_ValueScale.expectsInterface = function() {
+    return [
+        {
+            "mandatory": ["getList"]
+        }
+    ];
+};
+
+CView_ValueScale.getCustomDataSchema = function() {
+    var schema = {
+        "$schema": "http://json-schema.org/draft-04/schema#",
+        "title": "Custom data for CView_ValueScale",
+        "type": "object",
+        "properties": {
+            "class": {
+                "type": "string",
+                "description": "the css class of the value scale bar"
+            },
+            "minimumValue": {
+            	"type": "integer",
+            	"description": "the value that will serve as the minimum on the scale",
+            	"default": 0
+            },
+            "maximumValue": {
+            	"type": "integer",
+            	"description": "the value that will serve as the maximum on the scale",
+            	"default": 100
+            },
+            "yellowZone": {
+            	"type": "integer",
+            	"description": "value in percent where the scale will turn yellow",
+            	"default": 60
+            },
+            "redZone": {
+            	"type": "integer",
+            	"description": "value in percent where the scale will turn red",
+            	"default": 80
+            }
+        },
+        "additionalProperties": false
+    };
+    $.extend(true, schema, ViewUtils.commonViewSchema);
+    return schema;
+};
+//# sourceURL=WebApplicationFramework\Views\View_ValueScale.js
\ No newline at end of file
diff --git a/htdocs/WebApplications/CustomizableApp/ViewModel.js b/htdocs/WebApplications/CustomizableApp/ViewModel.js
index eec5f69..a8159e5 100644
--- a/htdocs/WebApplications/CustomizableApp/ViewModel.js
+++ b/htdocs/WebApplications/CustomizableApp/ViewModel.js
@@ -494,7 +494,15 @@
                 if (rangeFilter != undefined) {
                     p_requests[i].getData.rangeFilter = JSON.parse(rangeFilter);
                 } else {
-                    p_requests[i].getData.rangeFilter.offset = 0;
+                    var origKey = JSON.stringify(p_path)+"origRangeFilter"+mWebAppModel.getSetupModel().getSetup().name;
+                    rangeFilter = sessionStorage.getItem(origKey);
+                    if (rangeFilter == undefined) {
+                      sessionStorage.setItem(origKey, JSON.stringify(p_requests[i].getData.rangeFilter));
+                      rangeFilter = p_requests[i].getData.rangeFilter;
+                    } else {
+                      rangeFilter = JSON.parse(rangeFilter);
+                    }
+                    p_requests[i].getData.rangeFilter = rangeFilter;
                 }
             }
 
diff --git a/htdocs/WebApplications/CustomizableApp/ViewModels/Viewmodel_ScrollForRangeFilter.js b/htdocs/WebApplications/CustomizableApp/ViewModels/Viewmodel_ScrollForRangeFilter.js
index 04e9a41..00fb327 100644
--- a/htdocs/WebApplications/CustomizableApp/ViewModels/Viewmodel_ScrollForRangeFilter.js
+++ b/htdocs/WebApplications/CustomizableApp/ViewModels/Viewmodel_ScrollForRangeFilter.js
@@ -22,10 +22,10 @@
 	/** public functions - interface for views */
     
     this.getRange = function() {
-        var size = paging.getSize();
+        var size = base.getSize();
         var max = size;
         if (v_options.allowScrollBelow == false) {
-            max = max - paging.getRangeFilter().count + 1;
+            max = max - base.getRangeFilter().count + 1;
         }
         return {
             "min": 0,
@@ -56,7 +56,7 @@
 
 CViewModel_ScrollForRangeFilter.getCustomDataSchema = function() {
     var schema = CViewModel_Paging.getCustomDataSchema();
-    scema.title = "Custom data for CViewModel_ScrollForRangeFilter";
+    schema.title = "Custom data for CViewModel_ScrollForRangeFilter";
     return schema;
 };
 
@@ -64,4 +64,4 @@
     return CViewModel_Paging.expectsConnection();
 };
 
-//# sourceURL=CustomizableApp\ViewModels\ViewModel_Scroll.js
\ No newline at end of file
+//# sourceURL=CustomizableApp\ViewModels\ViewModel_ScrollForRangeFilter.js