Commit 87c2574acde4408b2b9cd3b9fa857f56c9f16f70
1 parent
81ccdf7b
TB-70: Keep dashboard state as base64 url parameter.
Showing
7 changed files
with
173 additions
and
15 deletions
@@ -45,6 +45,7 @@ | @@ -45,6 +45,7 @@ | ||
45 | "angular-ui-ace": "^0.2.3", | 45 | "angular-ui-ace": "^0.2.3", |
46 | "angular-ui-router": "^0.3.1", | 46 | "angular-ui-router": "^0.3.1", |
47 | "angular-websocket": "^2.0.1", | 47 | "angular-websocket": "^2.0.1", |
48 | + "base64-js": "^1.2.1", | ||
48 | "brace": "^0.8.0", | 49 | "brace": "^0.8.0", |
49 | "canvas-gauges": "^2.0.9", | 50 | "canvas-gauges": "^2.0.9", |
50 | "clipboard": "^1.5.15", | 51 | "clipboard": "^1.5.15", |
ui/src/app/common/utf8-support.js
0 → 100644
1 | +/* | ||
2 | + * Copyright © 2016-2017 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | + | ||
17 | +export function utf8Encode(str) { | ||
18 | + var result; | ||
19 | + | ||
20 | + if (angular.isUndefined(Uint8Array)) { // eslint-disable-line no-undef | ||
21 | + result = utf8ToBytes(str); | ||
22 | + } else { | ||
23 | + result = new Uint8Array(utf8ToBytes(str)); // eslint-disable-line no-undef | ||
24 | + } | ||
25 | + | ||
26 | + return result; | ||
27 | +} | ||
28 | + | ||
29 | +export function utf8Decode(bytes) { | ||
30 | + return utf8Slice(bytes, 0, bytes.length); | ||
31 | +} | ||
32 | + | ||
33 | +function utf8Slice (buf, start, end) { | ||
34 | + var res = '' | ||
35 | + var tmp = '' | ||
36 | + end = Math.min(buf.length, end || Infinity) | ||
37 | + start = start || 0; | ||
38 | + | ||
39 | + for (var i = start; i < end; i++) { | ||
40 | + if (buf[i] <= 0x7F) { | ||
41 | + res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i]) | ||
42 | + tmp = '' | ||
43 | + } else { | ||
44 | + tmp += '%' + buf[i].toString(16) | ||
45 | + } | ||
46 | + } | ||
47 | + | ||
48 | + return res + decodeUtf8Char(tmp) | ||
49 | +} | ||
50 | + | ||
51 | +function decodeUtf8Char (str) { | ||
52 | + try { | ||
53 | + return decodeURIComponent(str) | ||
54 | + } catch (err) { | ||
55 | + return String.fromCharCode(0xFFFD) // UTF 8 invalid char | ||
56 | + } | ||
57 | +} | ||
58 | + | ||
59 | +function utf8ToBytes (string, units) { | ||
60 | + units = units || Infinity | ||
61 | + var codePoint | ||
62 | + var length = string.length | ||
63 | + var leadSurrogate = null | ||
64 | + var bytes = [] | ||
65 | + var i = 0 | ||
66 | + | ||
67 | + for (; i < length; i++) { | ||
68 | + codePoint = string.charCodeAt(i) | ||
69 | + | ||
70 | + // is surrogate component | ||
71 | + if (codePoint > 0xD7FF && codePoint < 0xE000) { | ||
72 | + // last char was a lead | ||
73 | + if (leadSurrogate) { | ||
74 | + // 2 leads in a row | ||
75 | + if (codePoint < 0xDC00) { | ||
76 | + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) | ||
77 | + leadSurrogate = codePoint | ||
78 | + continue | ||
79 | + } else { | ||
80 | + // valid surrogate pair | ||
81 | + codePoint = leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00 | 0x10000 | ||
82 | + leadSurrogate = null | ||
83 | + } | ||
84 | + } else { | ||
85 | + // no lead yet | ||
86 | + | ||
87 | + if (codePoint > 0xDBFF) { | ||
88 | + // unexpected trail | ||
89 | + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) | ||
90 | + continue | ||
91 | + } else if (i + 1 === length) { | ||
92 | + // unpaired lead | ||
93 | + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) | ||
94 | + continue | ||
95 | + } else { | ||
96 | + // valid lead | ||
97 | + leadSurrogate = codePoint | ||
98 | + continue | ||
99 | + } | ||
100 | + } | ||
101 | + } else if (leadSurrogate) { | ||
102 | + // valid bmp char, but last char was a lead | ||
103 | + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) | ||
104 | + leadSurrogate = null | ||
105 | + } | ||
106 | + | ||
107 | + // encode utf8 | ||
108 | + if (codePoint < 0x80) { | ||
109 | + if ((units -= 1) < 0) break | ||
110 | + bytes.push(codePoint) | ||
111 | + } else if (codePoint < 0x800) { | ||
112 | + if ((units -= 2) < 0) break | ||
113 | + bytes.push( | ||
114 | + codePoint >> 0x6 | 0xC0, | ||
115 | + codePoint & 0x3F | 0x80 | ||
116 | + ) | ||
117 | + } else if (codePoint < 0x10000) { | ||
118 | + if ((units -= 3) < 0) break | ||
119 | + bytes.push( | ||
120 | + codePoint >> 0xC | 0xE0, | ||
121 | + codePoint >> 0x6 & 0x3F | 0x80, | ||
122 | + codePoint & 0x3F | 0x80 | ||
123 | + ) | ||
124 | + } else if (codePoint < 0x200000) { | ||
125 | + if ((units -= 4) < 0) break | ||
126 | + bytes.push( | ||
127 | + codePoint >> 0x12 | 0xF0, | ||
128 | + codePoint >> 0xC & 0x3F | 0x80, | ||
129 | + codePoint >> 0x6 & 0x3F | 0x80, | ||
130 | + codePoint & 0x3F | 0x80 | ||
131 | + ) | ||
132 | + } else { | ||
133 | + throw new Error('Invalid code point') | ||
134 | + } | ||
135 | + } | ||
136 | + | ||
137 | + return bytes | ||
138 | +} |
@@ -20,9 +20,12 @@ import materialIconsCodepoints from 'raw-loader!material-design-icons/iconfont/c | @@ -20,9 +20,12 @@ import materialIconsCodepoints from 'raw-loader!material-design-icons/iconfont/c | ||
20 | 20 | ||
21 | /* eslint-enable import/no-unresolved, import/default */ | 21 | /* eslint-enable import/no-unresolved, import/default */ |
22 | 22 | ||
23 | -import tinycolor from "tinycolor2"; | ||
24 | -import jsonSchemaDefaults from "json-schema-defaults"; | ||
25 | -import thingsboardTypes from "./types.constant"; | 23 | +import tinycolor from 'tinycolor2'; |
24 | +import jsonSchemaDefaults from 'json-schema-defaults'; | ||
25 | +import base64js from 'base64-js'; | ||
26 | +import {utf8Encode, utf8Decode} from './utf8-support'; | ||
27 | + | ||
28 | +import thingsboardTypes from './types.constant'; | ||
26 | 29 | ||
27 | export default angular.module('thingsboard.utils', [thingsboardTypes]) | 30 | export default angular.module('thingsboard.utils', [thingsboardTypes]) |
28 | .factory('utils', Utils) | 31 | .factory('utils', Utils) |
@@ -153,7 +156,9 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t | @@ -153,7 +156,9 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t | ||
153 | createKey: createKey, | 156 | createKey: createKey, |
154 | createLabelFromDatasource: createLabelFromDatasource, | 157 | createLabelFromDatasource: createLabelFromDatasource, |
155 | insertVariable: insertVariable, | 158 | insertVariable: insertVariable, |
156 | - customTranslation: customTranslation | 159 | + customTranslation: customTranslation, |
160 | + objToBase64: objToBase64, | ||
161 | + base64toObj: base64toObj | ||
157 | } | 162 | } |
158 | 163 | ||
159 | return service; | 164 | return service; |
@@ -486,4 +491,18 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t | @@ -486,4 +491,18 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t | ||
486 | return result; | 491 | return result; |
487 | } | 492 | } |
488 | 493 | ||
494 | + function objToBase64(obj) { | ||
495 | + var json = angular.toJson(obj); | ||
496 | + var encoded = utf8Encode(json); | ||
497 | + var b64Encoded = base64js.fromByteArray(encoded); | ||
498 | + return b64Encoded; | ||
499 | + } | ||
500 | + | ||
501 | + function base64toObj(b64Encoded) { | ||
502 | + var encoded = base64js.toByteArray(b64Encoded); | ||
503 | + var json = utf8Decode(encoded); | ||
504 | + var obj = angular.fromJson(json); | ||
505 | + return obj; | ||
506 | + } | ||
507 | + | ||
489 | } | 508 | } |
@@ -473,7 +473,7 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele | @@ -473,7 +473,7 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele | ||
473 | } | 473 | } |
474 | var stateParams = { | 474 | var stateParams = { |
475 | dashboardId: targetDashboardId, | 475 | dashboardId: targetDashboardId, |
476 | - state: angular.toJson([ stateObject ]) | 476 | + state: utils.objToBase64([ stateObject ]) |
477 | } | 477 | } |
478 | $state.go('home.dashboards.dashboard', stateParams); | 478 | $state.go('home.dashboards.dashboard', stateParams); |
479 | break; | 479 | break; |
@@ -121,11 +121,11 @@ export default function DefaultStateController($scope, $location, $state, $state | @@ -121,11 +121,11 @@ export default function DefaultStateController($scope, $location, $state, $state | ||
121 | return utils.customTranslation(state.name, id); | 121 | return utils.customTranslation(state.name, id); |
122 | } | 122 | } |
123 | 123 | ||
124 | - function parseState(stateJson) { | 124 | + function parseState(stateBase64) { |
125 | var result; | 125 | var result; |
126 | - if (stateJson) { | 126 | + if (stateBase64) { |
127 | try { | 127 | try { |
128 | - result = angular.fromJson(stateJson); | 128 | + result = utils.base64toObj(stateBase64); |
129 | } catch (e) { | 129 | } catch (e) { |
130 | result = [ { id: null, params: {} } ]; | 130 | result = [ { id: null, params: {} } ]; |
131 | } | 131 | } |
@@ -205,7 +205,7 @@ export default function DefaultStateController($scope, $location, $state, $state | @@ -205,7 +205,7 @@ export default function DefaultStateController($scope, $location, $state, $state | ||
205 | 205 | ||
206 | function updateLocation() { | 206 | function updateLocation() { |
207 | if (vm.stateObject[0].id) { | 207 | if (vm.stateObject[0].id) { |
208 | - $location.search({state : angular.toJson(vm.stateObject)}); | 208 | + $location.search({state : utils.objToBase64(vm.stateObject)}); |
209 | } | 209 | } |
210 | } | 210 | } |
211 | } | 211 | } |
@@ -168,11 +168,11 @@ export default function EntityStateController($scope, $location, $state, $stateP | @@ -168,11 +168,11 @@ export default function EntityStateController($scope, $location, $state, $stateP | ||
168 | return deferred.promise; | 168 | return deferred.promise; |
169 | } | 169 | } |
170 | 170 | ||
171 | - function parseState(stateJson) { | 171 | + function parseState(stateBase64) { |
172 | var result; | 172 | var result; |
173 | - if (stateJson) { | 173 | + if (stateBase64) { |
174 | try { | 174 | try { |
175 | - result = angular.fromJson(stateJson); | 175 | + result = utils.base64toObj(stateBase64); |
176 | } catch (e) { | 176 | } catch (e) { |
177 | result = [ { id: null, params: {} } ]; | 177 | result = [ { id: null, params: {} } ]; |
178 | } | 178 | } |
@@ -278,7 +278,7 @@ export default function EntityStateController($scope, $location, $state, $stateP | @@ -278,7 +278,7 @@ export default function EntityStateController($scope, $location, $state, $stateP | ||
278 | 278 | ||
279 | function updateLocation() { | 279 | function updateLocation() { |
280 | if (vm.stateObject[vm.stateObject.length-1].id) { | 280 | if (vm.stateObject[vm.stateObject.length-1].id) { |
281 | - $location.search({state : angular.toJson(vm.stateObject)}); | 281 | + $location.search({state : utils.objToBase64(vm.stateObject)}); |
282 | } | 282 | } |
283 | } | 283 | } |
284 | 284 |
@@ -23,7 +23,7 @@ import selectTargetLayoutTemplate from '../../dashboard/layouts/select-target-la | @@ -23,7 +23,7 @@ import selectTargetLayoutTemplate from '../../dashboard/layouts/select-target-la | ||
23 | 23 | ||
24 | /*@ngInject*/ | 24 | /*@ngInject*/ |
25 | export default function AddWidgetToDashboardDialogController($scope, $mdDialog, $state, $q, $document, dashboardUtils, | 25 | export default function AddWidgetToDashboardDialogController($scope, $mdDialog, $state, $q, $document, dashboardUtils, |
26 | - types, itembuffer, dashboardService, entityId, entityType, entityName, widget) { | 26 | + utils, types, itembuffer, dashboardService, entityId, entityType, entityName, widget) { |
27 | 27 | ||
28 | var vm = this; | 28 | var vm = this; |
29 | 29 | ||
@@ -143,7 +143,7 @@ export default function AddWidgetToDashboardDialogController($scope, $mdDialog, | @@ -143,7 +143,7 @@ export default function AddWidgetToDashboardDialogController($scope, $mdDialog, | ||
143 | var stateIds = Object.keys(dashboard.configuration.states); | 143 | var stateIds = Object.keys(dashboard.configuration.states); |
144 | var stateIndex = stateIds.indexOf(targetState); | 144 | var stateIndex = stateIds.indexOf(targetState); |
145 | if (stateIndex > 0) { | 145 | if (stateIndex > 0) { |
146 | - stateParams.state = angular.toJson([ {id: targetState, params: {}} ]); | 146 | + stateParams.state = utils.objToBase64([ {id: targetState, params: {}} ]); |
147 | } | 147 | } |
148 | $state.go('home.dashboards.dashboard', stateParams); | 148 | $state.go('home.dashboards.dashboard', stateParams); |
149 | } | 149 | } |