TB-70: Keep dashboard state as base64 url parameter.

This commit is contained in:
Igor Kulikov 2017-07-12 21:03:51 +03:00
parent 81ccdf7bfa
commit 87c2574acd
7 changed files with 173 additions and 15 deletions

View File

@ -45,6 +45,7 @@
"angular-ui-ace": "^0.2.3",
"angular-ui-router": "^0.3.1",
"angular-websocket": "^2.0.1",
"base64-js": "^1.2.1",
"brace": "^0.8.0",
"canvas-gauges": "^2.0.9",
"clipboard": "^1.5.15",

View File

@ -0,0 +1,138 @@
/*
* Copyright © 2016-2017 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export function utf8Encode(str) {
var result;
if (angular.isUndefined(Uint8Array)) { // eslint-disable-line no-undef
result = utf8ToBytes(str);
} else {
result = new Uint8Array(utf8ToBytes(str)); // eslint-disable-line no-undef
}
return result;
}
export function utf8Decode(bytes) {
return utf8Slice(bytes, 0, bytes.length);
}
function utf8Slice (buf, start, end) {
var res = ''
var tmp = ''
end = Math.min(buf.length, end || Infinity)
start = start || 0;
for (var i = start; i < end; i++) {
if (buf[i] <= 0x7F) {
res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i])
tmp = ''
} else {
tmp += '%' + buf[i].toString(16)
}
}
return res + decodeUtf8Char(tmp)
}
function decodeUtf8Char (str) {
try {
return decodeURIComponent(str)
} catch (err) {
return String.fromCharCode(0xFFFD) // UTF 8 invalid char
}
}
function utf8ToBytes (string, units) {
units = units || Infinity
var codePoint
var length = string.length
var leadSurrogate = null
var bytes = []
var i = 0
for (; i < length; i++) {
codePoint = string.charCodeAt(i)
// is surrogate component
if (codePoint > 0xD7FF && codePoint < 0xE000) {
// last char was a lead
if (leadSurrogate) {
// 2 leads in a row
if (codePoint < 0xDC00) {
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
leadSurrogate = codePoint
continue
} else {
// valid surrogate pair
codePoint = leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00 | 0x10000
leadSurrogate = null
}
} else {
// no lead yet
if (codePoint > 0xDBFF) {
// unexpected trail
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
continue
} else if (i + 1 === length) {
// unpaired lead
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
continue
} else {
// valid lead
leadSurrogate = codePoint
continue
}
}
} else if (leadSurrogate) {
// valid bmp char, but last char was a lead
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
leadSurrogate = null
}
// encode utf8
if (codePoint < 0x80) {
if ((units -= 1) < 0) break
bytes.push(codePoint)
} else if (codePoint < 0x800) {
if ((units -= 2) < 0) break
bytes.push(
codePoint >> 0x6 | 0xC0,
codePoint & 0x3F | 0x80
)
} else if (codePoint < 0x10000) {
if ((units -= 3) < 0) break
bytes.push(
codePoint >> 0xC | 0xE0,
codePoint >> 0x6 & 0x3F | 0x80,
codePoint & 0x3F | 0x80
)
} else if (codePoint < 0x200000) {
if ((units -= 4) < 0) break
bytes.push(
codePoint >> 0x12 | 0xF0,
codePoint >> 0xC & 0x3F | 0x80,
codePoint >> 0x6 & 0x3F | 0x80,
codePoint & 0x3F | 0x80
)
} else {
throw new Error('Invalid code point')
}
}
return bytes
}

View File

@ -20,9 +20,12 @@ import materialIconsCodepoints from 'raw-loader!material-design-icons/iconfont/c
/* eslint-enable import/no-unresolved, import/default */
import tinycolor from "tinycolor2";
import jsonSchemaDefaults from "json-schema-defaults";
import thingsboardTypes from "./types.constant";
import tinycolor from 'tinycolor2';
import jsonSchemaDefaults from 'json-schema-defaults';
import base64js from 'base64-js';
import {utf8Encode, utf8Decode} from './utf8-support';
import thingsboardTypes from './types.constant';
export default angular.module('thingsboard.utils', [thingsboardTypes])
.factory('utils', Utils)
@ -153,7 +156,9 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
createKey: createKey,
createLabelFromDatasource: createLabelFromDatasource,
insertVariable: insertVariable,
customTranslation: customTranslation
customTranslation: customTranslation,
objToBase64: objToBase64,
base64toObj: base64toObj
}
return service;
@ -486,4 +491,18 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
return result;
}
function objToBase64(obj) {
var json = angular.toJson(obj);
var encoded = utf8Encode(json);
var b64Encoded = base64js.fromByteArray(encoded);
return b64Encoded;
}
function base64toObj(b64Encoded) {
var encoded = base64js.toByteArray(b64Encoded);
var json = utf8Decode(encoded);
var obj = angular.fromJson(json);
return obj;
}
}

View File

@ -473,7 +473,7 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele
}
var stateParams = {
dashboardId: targetDashboardId,
state: angular.toJson([ stateObject ])
state: utils.objToBase64([ stateObject ])
}
$state.go('home.dashboards.dashboard', stateParams);
break;

View File

@ -121,11 +121,11 @@ export default function DefaultStateController($scope, $location, $state, $state
return utils.customTranslation(state.name, id);
}
function parseState(stateJson) {
function parseState(stateBase64) {
var result;
if (stateJson) {
if (stateBase64) {
try {
result = angular.fromJson(stateJson);
result = utils.base64toObj(stateBase64);
} catch (e) {
result = [ { id: null, params: {} } ];
}
@ -205,7 +205,7 @@ export default function DefaultStateController($scope, $location, $state, $state
function updateLocation() {
if (vm.stateObject[0].id) {
$location.search({state : angular.toJson(vm.stateObject)});
$location.search({state : utils.objToBase64(vm.stateObject)});
}
}
}

View File

@ -168,11 +168,11 @@ export default function EntityStateController($scope, $location, $state, $stateP
return deferred.promise;
}
function parseState(stateJson) {
function parseState(stateBase64) {
var result;
if (stateJson) {
if (stateBase64) {
try {
result = angular.fromJson(stateJson);
result = utils.base64toObj(stateBase64);
} catch (e) {
result = [ { id: null, params: {} } ];
}
@ -278,7 +278,7 @@ export default function EntityStateController($scope, $location, $state, $stateP
function updateLocation() {
if (vm.stateObject[vm.stateObject.length-1].id) {
$location.search({state : angular.toJson(vm.stateObject)});
$location.search({state : utils.objToBase64(vm.stateObject)});
}
}

View File

@ -23,7 +23,7 @@ import selectTargetLayoutTemplate from '../../dashboard/layouts/select-target-la
/*@ngInject*/
export default function AddWidgetToDashboardDialogController($scope, $mdDialog, $state, $q, $document, dashboardUtils,
types, itembuffer, dashboardService, entityId, entityType, entityName, widget) {
utils, types, itembuffer, dashboardService, entityId, entityType, entityName, widget) {
var vm = this;
@ -143,7 +143,7 @@ export default function AddWidgetToDashboardDialogController($scope, $mdDialog,
var stateIds = Object.keys(dashboard.configuration.states);
var stateIndex = stateIds.indexOf(targetState);
if (stateIndex > 0) {
stateParams.state = angular.toJson([ {id: targetState, params: {}} ]);
stateParams.state = utils.objToBase64([ {id: targetState, params: {}} ]);
}
$state.go('home.dashboards.dashboard', stateParams);
}