Commit 87c2574acde4408b2b9cd3b9fa857f56c9f16f70
1 parent
81ccdf7b
TB-70: Keep dashboard state as base64 url parameter.
Showing
7 changed files
with
173 additions
and
15 deletions
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 | +} | |
\ No newline at end of file | ... | ... |
... | ... | @@ -20,9 +20,12 @@ import materialIconsCodepoints from 'raw-loader!material-design-icons/iconfont/c |
20 | 20 | |
21 | 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 | 30 | export default angular.module('thingsboard.utils', [thingsboardTypes]) |
28 | 31 | .factory('utils', Utils) |
... | ... | @@ -153,7 +156,9 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t |
153 | 156 | createKey: createKey, |
154 | 157 | createLabelFromDatasource: createLabelFromDatasource, |
155 | 158 | insertVariable: insertVariable, |
156 | - customTranslation: customTranslation | |
159 | + customTranslation: customTranslation, | |
160 | + objToBase64: objToBase64, | |
161 | + base64toObj: base64toObj | |
157 | 162 | } |
158 | 163 | |
159 | 164 | return service; |
... | ... | @@ -486,4 +491,18 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t |
486 | 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 | 473 | } |
474 | 474 | var stateParams = { |
475 | 475 | dashboardId: targetDashboardId, |
476 | - state: angular.toJson([ stateObject ]) | |
476 | + state: utils.objToBase64([ stateObject ]) | |
477 | 477 | } |
478 | 478 | $state.go('home.dashboards.dashboard', stateParams); |
479 | 479 | break; | ... | ... |
... | ... | @@ -121,11 +121,11 @@ export default function DefaultStateController($scope, $location, $state, $state |
121 | 121 | return utils.customTranslation(state.name, id); |
122 | 122 | } |
123 | 123 | |
124 | - function parseState(stateJson) { | |
124 | + function parseState(stateBase64) { | |
125 | 125 | var result; |
126 | - if (stateJson) { | |
126 | + if (stateBase64) { | |
127 | 127 | try { |
128 | - result = angular.fromJson(stateJson); | |
128 | + result = utils.base64toObj(stateBase64); | |
129 | 129 | } catch (e) { |
130 | 130 | result = [ { id: null, params: {} } ]; |
131 | 131 | } |
... | ... | @@ -205,7 +205,7 @@ export default function DefaultStateController($scope, $location, $state, $state |
205 | 205 | |
206 | 206 | function updateLocation() { |
207 | 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 | 168 | return deferred.promise; |
169 | 169 | } |
170 | 170 | |
171 | - function parseState(stateJson) { | |
171 | + function parseState(stateBase64) { | |
172 | 172 | var result; |
173 | - if (stateJson) { | |
173 | + if (stateBase64) { | |
174 | 174 | try { |
175 | - result = angular.fromJson(stateJson); | |
175 | + result = utils.base64toObj(stateBase64); | |
176 | 176 | } catch (e) { |
177 | 177 | result = [ { id: null, params: {} } ]; |
178 | 178 | } |
... | ... | @@ -278,7 +278,7 @@ export default function EntityStateController($scope, $location, $state, $stateP |
278 | 278 | |
279 | 279 | function updateLocation() { |
280 | 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 | 23 | |
24 | 24 | /*@ngInject*/ |
25 | 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 | 28 | var vm = this; |
29 | 29 | |
... | ... | @@ -143,7 +143,7 @@ export default function AddWidgetToDashboardDialogController($scope, $mdDialog, |
143 | 143 | var stateIds = Object.keys(dashboard.configuration.states); |
144 | 144 | var stateIndex = stateIds.indexOf(targetState); |
145 | 145 | if (stateIndex > 0) { |
146 | - stateParams.state = angular.toJson([ {id: targetState, params: {}} ]); | |
146 | + stateParams.state = utils.objToBase64([ {id: targetState, params: {}} ]); | |
147 | 147 | } |
148 | 148 | $state.go('home.dashboards.dashboard', stateParams); |
149 | 149 | } | ... | ... |