thingsboard-aplcache

UI: Minor fixes

2/1/2017 4:34:55 PM

Details

diff --git a/dao/src/main/resources/system-data.cql b/dao/src/main/resources/system-data.cql
index 374a0ba..a687a4e 100644
--- a/dao/src/main/resources/system-data.cql
+++ b/dao/src/main/resources/system-data.cql
@@ -79,7 +79,7 @@ VALUES (  now ( ), minTimeuuid ( 0 ), 'cards', 'label_widget',
 
 INSERT INTO "thingsboard"."widget_type" ( "id", "tenant_id", "bundle_alias", "alias", "descriptor", "name" )
 VALUES ( now ( ), minTimeuuid ( 0 ), 'cards', 'timeseries_table',
-'{"type":"timeseries","sizeX":8,"sizeY":6.5,"resources":[],"templateHtml":"<md-tabs md-selected=\"sourceIndex\" ng-class=\"{''tb-headless'': sources.length === 1}\"\n    id=\"tabs\" md-border-bottom flex class=\"tb-absolute-fill\">\n    <md-tab ng-repeat=\"source in sources\" label=\"{{ source.label }}\">\n        <md-table-container>\n            <table md-table>\n                <thead md-head md-order=\"source.query.order\" md-on-reorder=\"onReorder(source)\">\n                    <tr md-row>\n                        <th ng-show=\"showTimestamp\" md-column md-order-by=\"0\"><span>Timestamp</span></th>\n                        <th md-column md-order-by=\"{{ h.index }}\" ng-repeat=\"h in source.ts.header\"><span>{{ h.label }}</span></th>\n                    </tr>\n                </thead>\n                <tbody md-body>\n                    <tr md-row ng-repeat=\"row in source.ts.data\">\n                        <td ng-show=\"$index > 0 || ($index === 0 && showTimestamp)\" md-cell ng-repeat=\"d in row track by $index\" ng-style=\"cellStyle(source, $index, d)\" ng-bind-html=\"cellContent(source, $index, row, d)\">\n                        </td>\n                    </tr>    \n                </tbody>    \n            </table>\n        </md-table-container>\n        <md-table-pagination md-limit=\"source.query.limit\" md-limit-options=\"[5, 10, 15]\"\n                             md-page=\"source.query.page\" md-total=\"{{source.ts.count}}\"\n                             md-on-paginate=\"onPaginate(source)\" md-page-select>\n        </md-table-pagination>\n    </md-tab>\n</md-tabs>","templateCss":"table.md-table thead.md-head>tr.md-row {\n    height: 40px;\n}\n\ntable.md-table tbody.md-body>tr.md-row, table.md-table tfoot.md-foot>tr.md-row {\n    height: 38px;\n}\n\n.md-table-pagination>* {\n    height: 46px;\n}\n","controllerScript":"var filter;\n\nfns.init = function(containerElement, settings, datasources,\n    data, scope) {\n    \n    filter = scope.$injector.get(\"$filter\");\n    \n    scope.sources = [];\n    scope.sourceIndex = 0;\n    scope.showTimestamp = settings.showTimestamp !== false;\n    \n    var keyOffset = 0;\n    for (var ds in datasources) {\n        var source = {};\n        var datasource = datasources[ds];\n        source.keyStartIndex = keyOffset;\n        keyOffset += datasource.dataKeys.length;\n        source.keyEndIndex = keyOffset;\n        source.label = datasource.name;\n        source.data = [];\n        source.rawData = [];\n        source.query = {\n            limit: 5,\n            page: 1,\n            order: ''-0''\n        }\n        source.ts = {\n            header: [],\n            count: 0,\n            data: [],\n            stylesInfo: [],\n            contentsInfo: [],\n            rowDataTemplate: {}\n        }\n        source.ts.rowDataTemplate[''Timestamp''] = null;\n        for (var a = 0; a < datasource.dataKeys.length; a++ ) {\n            var dataKey = datasource.dataKeys[a];\n            var keySettings = dataKey.settings;\n            source.ts.header.push({\n                index: a+1,\n                label: dataKey.label\n            });\n            source.ts.rowDataTemplate[dataKey.label] = null;\n\n            var cellStyleFunction = null;\n            var useCellStyleFunction = false;\n            \n            if (keySettings.useCellStyleFunction === true) {\n                if (angular.isDefined(keySettings.cellStyleFunction) && keySettings.cellStyleFunction.length > 0) {\n                    try {\n                       cellStyleFunction = new Function(''value'', keySettings.cellStyleFunction);\n                       useCellStyleFunction = true;\n                    } catch (e) {\n                       cellStyleFunction = null;\n                       useCellStyleFunction = false;\n                    }\n                }\n            }\n\n            source.ts.stylesInfo.push({\n                useCellStyleFunction: useCellStyleFunction,\n                cellStyleFunction: cellStyleFunction\n            });\n            \n            var cellContentFunction = null;\n            var useCellContentFunction = false;\n            \n            if (keySettings.useCellContentFunction === true) {\n                if (angular.isDefined(keySettings.cellContentFunction) && keySettings.cellContentFunction.length > 0) {\n                    try {\n                       cellContentFunction = new Function(''value, rowData, filter'', keySettings.cellContentFunction);\n                       useCellContentFunction = true;\n                    } catch (e) {\n                       cellContentFunction = null;\n                       useCellContentFunction = false;\n                    }\n                }\n            }\n            \n            source.ts.contentsInfo.push({\n                useCellContentFunction: useCellContentFunction,\n                cellContentFunction: cellContentFunction\n            });\n            \n        }\n        scope.sources.push(source);\n    }\n\n    scope.onPaginate = function(source) {\n        updatePage(source);\n    }\n    \n    scope.onReorder = function(source) {\n        reorder(source);\n        updatePage(source);\n    }\n    \n    scope.cellStyle = function(source, index, value) {\n        var style = {};\n        if (index > 0) {\n            var styleInfo = source.ts.stylesInfo[index-1];\n            if (styleInfo.useCellStyleFunction && styleInfo.cellStyleFunction) {\n                try {\n                    style = styleInfo.cellStyleFunction(value);\n                } catch (e) {\n                    style = {};\n                }\n            }\n        }\n        return style;\n    }\n    \n    //{{ $index === 0 ? (d | date : ''yyyy-MM-dd HH:mm:ss'') : cellContent(source, $index, row, d) }}\n    \n    scope.cellContent = function(source, index, row, value) {\n        if (index === 0) {\n            return filter(''date'')(value, ''yyyy-MM-dd HH:mm:ss'');\n        } else {\n            var content = ''''+value;\n            if (index > 0) {\n                var contentInfo = source.ts.contentsInfo[index-1];\n                if (contentInfo.useCellContentFunction && contentInfo.cellContentFunction) {\n                    try {\n                        var rowData = source.ts.rowDataTemplate;\n                        rowData[''Timestamp''] = row[0];\n                        for (var h in source.ts.header) {\n                            var headerInfo = source.ts.header[h];\n                            rowData[headerInfo.label] = row[headerInfo.index];\n                        }\n                        content = contentInfo.cellContentFunction(value, rowData, filter);\n                    } catch (e) {\n                        content = ''''+value;\n                    }\n                }            \n            }\n            return content;\n        }\n    }\n    \n    scope.$watch(''sourceIndex'', function(newIndex, oldIndex) {\n       if (newIndex != oldIndex) {\n           updateSourceData(scope.sources[scope.sourceIndex]);\n       } \n    });\n    \n    scope.$apply();\n}\n\nfunction updatePage(source) {\n    var startIndex = source.query.limit * (source.query.page - 1);\n    source.ts.data = source.data.slice(startIndex, startIndex + source.query.limit);\n}\n\nfunction reorder(source) {\n    source.data = filter(''orderBy'')(source.data, source.query.order);\n}\n\nfunction convertData(data) {\n    var rows = [];\n    var count = data[0].data.length;\n    for (var i = 0; i < count; i++) {\n        var row = [];\n        for (var d = 0; d < data.length; d++) {\n            var columnData = data[d].data;\n            var cellData = columnData[i];\n            if (d === 0) {\n                row.push(cellData[0]);\n            }\n            row.push(cellData[1]);\n        }\n        rows.push(row);\n    }\n    return rows;\n}\n\nfunction updateSourceData(source) {\n    source.data = convertData(source.rawData);\n    source.ts.count = source.data.length;\n    reorder(source);\n    updatePage(source);\n}\n\nfns.redraw = function(containerElement, width, height, data,\n    timeWindow, sizeChanged, scope) {\n    for (var s in scope.sources) {\n        var source = scope.sources[s];\n        source.rawData = data.slice(source.keyStartIndex, source.keyEndIndex);\n    }\n    updateSourceData(scope.sources[scope.sourceIndex]);\n    scope.$apply();\n};\n\nfns.destroy = function() {\n};","settingsSchema":"{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"TimeseriesTableSettings\",\n        \"properties\": {\n            \"showTimestamp\": {\n                \"title\": \"Display timestamp column\",\n                \"type\": \"boolean\",\n                \"default\": true\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"showTimestamp\"\n    ]\n}","dataKeySettingsSchema":"{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"DataKeySettings\",\n        \"properties\": {\n            \"useCellStyleFunction\": {\n                \"title\": \"Use cell style function\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"cellStyleFunction\": {\n                \"title\": \"Cell style function: f(value)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"useCellContentFunction\": {\n                \"title\": \"Use cell content function\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"cellContentFunction\": {\n                \"title\": \"Cell content function: f(value, rowData, filter)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"useCellStyleFunction\",\n        {\n            \"key\": \"cellStyleFunction\",\n            \"type\": \"javascript\"\n        },\n        \"useCellContentFunction\",\n        {\n            \"key\": \"cellContentFunction\",\n            \"type\": \"javascript\"\n        }\n    ]\n}","defaultConfig":"{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature  °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"var percent = (value + 60)/120 * 100;\\nvar color = tinycolor.mix(''blue'', ''red'', amount = percent);\\ncolor.setAlpha(.5);\\nreturn {\\n  paddingLeft: ''20px'',\\n  color: ''#ffffff'',\\n  background: color.toRgbString(),\\n  fontSize: ''18px''\\n};\"},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"var percent = value;\\nvar backgroundColor = tinycolor(''blue'');\\nbackgroundColor.setAlpha(value/100);\\nvar color = ''blue'';\\nif (value > 50) {\\n    color = ''white'';\\n}\\n\\nreturn {\\n  paddingLeft: ''20px'',\\n  color: color,\\n  background: backgroundColor.toRgbString(),\\n  fontSize: ''18px''\\n};\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showTimestamp\":true},\"title\":\"Timeseries table\"}"}',
+'{"type":"timeseries","sizeX":8,"sizeY":6.5,"resources":[],"templateHtml":"<md-tabs md-selected=\"sourceIndex\" ng-class=\"{''tb-headless'': sources.length === 1}\"\n    id=\"tabs\" md-border-bottom flex class=\"tb-absolute-fill\">\n    <md-tab ng-repeat=\"source in sources\" label=\"{{ source.label }}\">\n        <md-table-container>\n            <table md-table>\n                <thead md-head md-order=\"source.query.order\" md-on-reorder=\"onReorder(source)\">\n                    <tr md-row>\n                        <th ng-show=\"showTimestamp\" md-column md-order-by=\"0\"><span>Timestamp</span></th>\n                        <th md-column md-order-by=\"{{ h.index }}\" ng-repeat=\"h in source.ts.header\"><span>{{ h.label }}</span></th>\n                    </tr>\n                </thead>\n                <tbody md-body>\n                    <tr md-row ng-repeat=\"row in source.ts.data\">\n                        <td ng-show=\"$index > 0 || ($index === 0 && showTimestamp)\" md-cell ng-repeat=\"d in row track by $index\" ng-style=\"cellStyle(source, $index, d)\" ng-bind-html=\"cellContent(source, $index, row, d)\">\n                        </td>\n                    </tr>    \n                </tbody>    \n            </table>\n        </md-table-container>\n        <md-table-pagination md-limit=\"source.query.limit\" md-limit-options=\"[5, 10, 15]\"\n                             md-page=\"source.query.page\" md-total=\"{{source.ts.count}}\"\n                             md-on-paginate=\"onPaginate(source)\" md-page-select>\n        </md-table-pagination>\n    </md-tab>\n</md-tabs>","templateCss":"table.md-table thead.md-head>tr.md-row {\n    height: 40px;\n}\n\ntable.md-table tbody.md-body>tr.md-row, table.md-table tfoot.md-foot>tr.md-row {\n    height: 38px;\n}\n\n.md-table-pagination>* {\n    height: 46px;\n}\n","controllerScript":"var filter;\n\nfns.init = function(containerElement, settings, datasources,\n    data, scope) {\n    \n    filter = scope.$injector.get(\"$filter\");\n    \n    scope.sources = [];\n    scope.sourceIndex = 0;\n    scope.showTimestamp = settings.showTimestamp !== false;\n    \n    var keyOffset = 0;\n    for (var ds in datasources) {\n        var source = {};\n        var datasource = datasources[ds];\n        source.keyStartIndex = keyOffset;\n        keyOffset += datasource.dataKeys.length;\n        source.keyEndIndex = keyOffset;\n        source.label = datasource.name;\n        source.data = [];\n        source.rawData = [];\n        source.query = {\n            limit: 5,\n            page: 1,\n            order: ''-0''\n        }\n        source.ts = {\n            header: [],\n            count: 0,\n            data: [],\n            stylesInfo: [],\n            contentsInfo: [],\n            rowDataTemplate: {}\n        }\n        source.ts.rowDataTemplate[''Timestamp''] = null;\n        for (var a = 0; a < datasource.dataKeys.length; a++ ) {\n            var dataKey = datasource.dataKeys[a];\n            var keySettings = dataKey.settings;\n            source.ts.header.push({\n                index: a+1,\n                label: dataKey.label\n            });\n            source.ts.rowDataTemplate[dataKey.label] = null;\n\n            var cellStyleFunction = null;\n            var useCellStyleFunction = false;\n            \n            if (keySettings.useCellStyleFunction === true) {\n                if (angular.isDefined(keySettings.cellStyleFunction) && keySettings.cellStyleFunction.length > 0) {\n                    try {\n                       cellStyleFunction = new Function(''value'', keySettings.cellStyleFunction);\n                       useCellStyleFunction = true;\n                    } catch (e) {\n                       cellStyleFunction = null;\n                       useCellStyleFunction = false;\n                    }\n                }\n            }\n\n            source.ts.stylesInfo.push({\n                useCellStyleFunction: useCellStyleFunction,\n                cellStyleFunction: cellStyleFunction\n            });\n            \n            var cellContentFunction = null;\n            var useCellContentFunction = false;\n            \n            if (keySettings.useCellContentFunction === true) {\n                if (angular.isDefined(keySettings.cellContentFunction) && keySettings.cellContentFunction.length > 0) {\n                    try {\n                       cellContentFunction = new Function(''value, rowData, filter'', keySettings.cellContentFunction);\n                       useCellContentFunction = true;\n                    } catch (e) {\n                       cellContentFunction = null;\n                       useCellContentFunction = false;\n                    }\n                }\n            }\n            \n            source.ts.contentsInfo.push({\n                useCellContentFunction: useCellContentFunction,\n                cellContentFunction: cellContentFunction\n            });\n            \n        }\n        scope.sources.push(source);\n    }\n\n    scope.onPaginate = function(source) {\n        updatePage(source);\n    }\n    \n    scope.onReorder = function(source) {\n        reorder(source);\n        updatePage(source);\n    }\n    \n    scope.cellStyle = function(source, index, value) {\n        var style = {};\n        if (index > 0) {\n            var styleInfo = source.ts.stylesInfo[index-1];\n            if (styleInfo.useCellStyleFunction && styleInfo.cellStyleFunction) {\n                try {\n                    style = styleInfo.cellStyleFunction(value);\n                } catch (e) {\n                    style = {};\n                }\n            }\n        }\n        return style;\n    }\n\n    scope.cellContent = function(source, index, row, value) {\n        if (index === 0) {\n            return filter(''date'')(value, ''yyyy-MM-dd HH:mm:ss'');\n        } else {\n            var strContent = '''';\n            if (value) {\n                strContent = ''''+value;\n            }\n            var content = strContent;\n            var contentInfo = source.ts.contentsInfo[index-1];\n            if (contentInfo.useCellContentFunction && contentInfo.cellContentFunction) {\n                try {\n                    var rowData = source.ts.rowDataTemplate;\n                    rowData[''Timestamp''] = row[0];\n                    for (var h in source.ts.header) {\n                        var headerInfo = source.ts.header[h];\n                        rowData[headerInfo.label] = row[headerInfo.index];\n                    }\n                    content = contentInfo.cellContentFunction(value, rowData, filter);\n                } catch (e) {\n                    content = strContent;\n                }\n            }            \n            return content;\n        }\n    }\n    \n    scope.$watch(''sourceIndex'', function(newIndex, oldIndex) {\n       if (newIndex != oldIndex) {\n           updateSourceData(scope.sources[scope.sourceIndex]);\n       } \n    });\n    \n    scope.$apply();\n}\n\nfunction updatePage(source) {\n    var startIndex = source.query.limit * (source.query.page - 1);\n    source.ts.data = source.data.slice(startIndex, startIndex + source.query.limit);\n}\n\nfunction reorder(source) {\n    source.data = filter(''orderBy'')(source.data, source.query.order);\n}\n\nfunction convertData(data) {\n    var rowsMap = [];\n    for (var d = 0; d < data.length; d++) {\n        var columnData = data[d].data;\n        for (var i = 0; i < columnData.length; i++) {\n            var cellData = columnData[i];\n            var timestamp = cellData[0];\n            var row = rowsMap[timestamp];\n            if (!row) {\n                row = [];\n                row[0] = timestamp;\n                for (var c = 0; c < data.length; c++) {\n                    row[c+1] = null;\n                }\n                rowsMap[timestamp] = row;\n            }\n            row[d+1] = cellData[1];\n        }\n    }\n    var rows = [];\n    for (var t in rowsMap) {\n        rows.push(rowsMap[t]);\n    }\n    return rows;\n}\n\nfunction updateSourceData(source) {\n    source.data = convertData(source.rawData);\n    source.ts.count = source.data.length;\n    reorder(source);\n    updatePage(source);\n}\n\nfns.redraw = function(containerElement, width, height, data,\n    timeWindow, sizeChanged, scope) {\n    for (var s in scope.sources) {\n        var source = scope.sources[s];\n        source.rawData = data.slice(source.keyStartIndex, source.keyEndIndex);\n    }\n    updateSourceData(scope.sources[scope.sourceIndex]);\n    scope.$apply();\n};\n\nfns.destroy = function() {\n};","settingsSchema":"{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"TimeseriesTableSettings\",\n        \"properties\": {\n            \"showTimestamp\": {\n                \"title\": \"Display timestamp column\",\n                \"type\": \"boolean\",\n                \"default\": true\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"showTimestamp\"\n    ]\n}","dataKeySettingsSchema":"{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"DataKeySettings\",\n        \"properties\": {\n            \"useCellStyleFunction\": {\n                \"title\": \"Use cell style function\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"cellStyleFunction\": {\n                \"title\": \"Cell style function: f(value)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"useCellContentFunction\": {\n                \"title\": \"Use cell content function\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"cellContentFunction\": {\n                \"title\": \"Cell content function: f(value, rowData, filter)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"useCellStyleFunction\",\n        {\n            \"key\": \"cellStyleFunction\",\n            \"type\": \"javascript\"\n        },\n        \"useCellContentFunction\",\n        {\n            \"key\": \"cellContentFunction\",\n            \"type\": \"javascript\"\n        }\n    ]\n}","defaultConfig":"{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature  °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n    var percent = (value + 60)/120 * 100;\\n    var color = tinycolor.mix(''blue'', ''red'', amount = percent);\\n    color.setAlpha(.5);\\n    return {\\n      paddingLeft: ''20px'',\\n      color: ''#ffffff'',\\n      background: color.toRgbString(),\\n      fontSize: ''18px''\\n    };\\n} else {\\n    return {};\\n}\"},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n    var percent = value;\\n    var backgroundColor = tinycolor(''blue'');\\n    backgroundColor.setAlpha(value/100);\\n    var color = ''blue'';\\n    if (value > 50) {\\n        color = ''white'';\\n    }\\n    \\n    return {\\n      paddingLeft: ''20px'',\\n      color: color,\\n      background: backgroundColor.toRgbString(),\\n      fontSize: ''18px''\\n    };\\n} else {\\n    return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":70000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showTimestamp\":true},\"title\":\"Timeseries table\"}"}',
 'Timeseries table' );
 
 INSERT INTO "thingsboard"."widget_type" ( "id", "tenant_id", "bundle_alias", "alias", "descriptor", "name" )
diff --git a/ui/src/app/api/datasource.service.js b/ui/src/app/api/datasource.service.js
index a519d8b..2da5224 100644
--- a/ui/src/app/api/datasource.service.js
+++ b/ui/src/app/api/datasource.service.js
@@ -357,23 +357,8 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
         return data;
     }
 
-    function generateSeries(dataKey) {
-
+    function generateSeries(dataKey, startTime, endTime) {
         var data = [];
-        var startTime;
-        var endTime;
-
-        if (realtime) {
-            endTime = (new Date).getTime();
-            if (dataKey.lastUpdateTime) {
-                startTime = dataKey.lastUpdateTime + frequency;
-            } else {
-                startTime = endTime - datasourceSubscription.subscriptionTimewindow.realtimeWindowMs;
-            }
-        } else {
-            startTime = datasourceSubscription.subscriptionTimewindow.fixedWindow.startTimeMs;
-            endTime = datasourceSubscription.subscriptionTimewindow.fixedWindow.endTimeMs;
-        }
         var prevSeries;
         var datasourceKeyData = datasourceData[dataKey.key];
         if (datasourceKeyData.length > 0) {
@@ -429,9 +414,33 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
     }
 
     function onTick() {
-        for (var key in dataKeys) {
-            dataGenFunction(dataKeys[key]);
+        var key;
+        if (datasourceSubscription.type === types.widgetType.timeseries.value) {
+            var startTime;
+            var endTime;
+            for (key in dataKeys) {
+                var dataKey = dataKeys[key];
+                if (!startTime) {
+                    if (realtime) {
+                        endTime = (new Date).getTime();
+                        if (dataKey.lastUpdateTime) {
+                            startTime = dataKey.lastUpdateTime + frequency;
+                        } else {
+                            startTime = endTime - datasourceSubscription.subscriptionTimewindow.realtimeWindowMs;
+                        }
+                    } else {
+                        startTime = datasourceSubscription.subscriptionTimewindow.fixedWindow.startTimeMs;
+                        endTime = datasourceSubscription.subscriptionTimewindow.fixedWindow.endTimeMs;
+                    }
+                }
+                generateSeries(dataKey, startTime, endTime);
+            }
+        } else if (datasourceSubscription.type === types.widgetType.latest.value) {
+            for (key in dataKeys) {
+                generateLatest(dataKeys[key]);
+            }
         }
+
         if (!history) {
             timer = $timeout(onTick, frequency / 2, false);
         }
diff --git a/ui/src/app/components/menu-toggle.tpl.html b/ui/src/app/components/menu-toggle.tpl.html
index fb7fdf2..454b462 100644
--- a/ui/src/app/components/menu-toggle.tpl.html
+++ b/ui/src/app/components/menu-toggle.tpl.html
@@ -26,7 +26,7 @@
 	class=" pull-right fa fa-chevron-down md-toggle-icon"
 	ng-class="{'tb-toggled' : sectionActive()}"></span>
 </md-button>
-<ul id="docs-menu-{{section.name | nospace}}" class="tb-menu-toggle-list" style="height: {{sectionHeight()}};">
+<ul id="docs-menu-{{section.name | nospace}}" class="tb-menu-toggle-list" ng-style="{height: sectionHeight()}">
 	<li ng-repeat="page in section.pages">
      	<tb-menu-link section="page"></tb-menu-link>
 	</li>