Commit e3dcc9691396b63b20c41a87e62b1110b8052a9b

Authored by Igor Kulikov
Committed by GitHub
2 parents 8e3c3990 86a99116

Merge pull request #3353 from vvlladd28/feature/encode-state-params

Improvment encode-state params for dashboard
@@ -35,13 +35,11 @@ @@ -35,13 +35,11 @@
35 "@ngrx/effects": "^10.0.0", 35 "@ngrx/effects": "^10.0.0",
36 "@ngrx/store": "^10.0.0", 36 "@ngrx/store": "^10.0.0",
37 "@ngrx/store-devtools": "^10.0.0", 37 "@ngrx/store-devtools": "^10.0.0",
38 - "ngx-sharebuttons": "^8.0.1",  
39 "@ngx-translate/core": "^13.0.0", 38 "@ngx-translate/core": "^13.0.0",
40 "@ngx-translate/http-loader": "^6.0.0", 39 "@ngx-translate/http-loader": "^6.0.0",
41 "ace-builds": "^1.4.12", 40 "ace-builds": "^1.4.12",
42 "angular-gridster2": "^10.1.3", 41 "angular-gridster2": "^10.1.3",
43 "angular2-hotkeys": "^2.2.0", 42 "angular2-hotkeys": "^2.2.0",
44 - "base64-js": "^1.3.1",  
45 "canvas-gauges": "^2.1.7", 43 "canvas-gauges": "^2.1.7",
46 "compass-sass-mixins": "^0.12.7", 44 "compass-sass-mixins": "^0.12.7",
47 "core-js": "^3.6.5", 45 "core-js": "^3.6.5",
@@ -70,6 +68,7 @@ @@ -70,6 +68,7 @@
70 "ngx-daterangepicker-material": "^3.0.4", 68 "ngx-daterangepicker-material": "^3.0.4",
71 "ngx-flowchart": "git://github.com/thingsboard/ngx-flowchart.git#master", 69 "ngx-flowchart": "git://github.com/thingsboard/ngx-flowchart.git#master",
72 "ngx-hm-carousel": "^2.0.0-rc.1", 70 "ngx-hm-carousel": "^2.0.0-rc.1",
  71 + "ngx-sharebuttons": "^8.0.1",
73 "ngx-translate-messageformat-compiler": "^4.8.0", 72 "ngx-translate-messageformat-compiler": "^4.8.0",
74 "objectpath": "^2.0.0", 73 "objectpath": "^2.0.0",
75 "prettier": "^2.0.5", 74 "prettier": "^2.0.5",
@@ -26,7 +26,6 @@ import { @@ -26,7 +26,6 @@ import {
26 import { DOCUMENT } from '@angular/common'; 26 import { DOCUMENT } from '@angular/common';
27 import { forkJoin, Observable, ReplaySubject, throwError } from 'rxjs'; 27 import { forkJoin, Observable, ReplaySubject, throwError } from 'rxjs';
28 import { HttpClient } from '@angular/common/http'; 28 import { HttpClient } from '@angular/common/http';
29 -import { objToBase64 } from '@core/utils';  
30 29
31 declare const SystemJS; 30 declare const SystemJS;
32 31
@@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
17 import _ from 'lodash'; 17 import _ from 'lodash';
18 import { Observable, Subject } from 'rxjs'; 18 import { Observable, Subject } from 'rxjs';
19 import { finalize, share } from 'rxjs/operators'; 19 import { finalize, share } from 'rxjs/operators';
20 -import base64js from 'base64-js';  
21 import { Datasource } from '@app/shared/models/widget.models'; 20 import { Datasource } from '@app/shared/models/widget.models';
22 21
23 const varsRegex = /\${([^}]*)}/g; 22 const varsRegex = /\${([^}]*)}/g;
@@ -123,7 +122,8 @@ export function isEmpty(obj: any): boolean { @@ -123,7 +122,8 @@ export function isEmpty(obj: any): boolean {
123 } 122 }
124 123
125 export function formatValue(value: any, dec?: number, units?: string, showZeroDecimals?: boolean): string | undefined { 124 export function formatValue(value: any, dec?: number, units?: string, showZeroDecimals?: boolean): string | undefined {
126 - if (isDefinedAndNotNull(value) && isNumeric(value) && (isDefinedAndNotNull(dec) || isDefinedAndNotNull(units) || Number(value).toString() === value)) { 125 + if (isDefinedAndNotNull(value) && isNumeric(value) &&
  126 + (isDefinedAndNotNull(dec) || isDefinedAndNotNull(units) || Number(value).toString() === value)) {
127 let formatted: string | number = Number(value); 127 let formatted: string | number = Number(value);
128 if (isDefinedAndNotNull(dec)) { 128 if (isDefinedAndNotNull(dec)) {
129 formatted = formatted.toFixed(dec); 129 formatted = formatted.toFixed(dec);
@@ -164,28 +164,21 @@ export function deleteNullProperties(obj: any) { @@ -164,28 +164,21 @@ export function deleteNullProperties(obj: any) {
164 164
165 export function objToBase64(obj: any): string { 165 export function objToBase64(obj: any): string {
166 const json = JSON.stringify(obj); 166 const json = JSON.stringify(obj);
167 - const encoded = utf8Encode(json);  
168 - return base64js.fromByteArray(encoded); 167 + return btoa(encodeURIComponent(json).replace(/%([0-9A-F]{2})/g,
  168 + function toSolidBytes(match, p1) {
  169 + return String.fromCharCode(Number('0x' + p1));
  170 + }));
169 } 171 }
170 172
171 -export function base64toObj(b64Encoded: string): any {  
172 - const encoded: Uint8Array | number[] = base64js.toByteArray(b64Encoded);  
173 - const json = utf8Decode(encoded);  
174 - return JSON.parse(json);  
175 -}  
176 -  
177 -function utf8Encode(str: string): Uint8Array | number[] {  
178 - let result: Uint8Array | number[];  
179 - if (isUndefined(Uint8Array)) {  
180 - result = utf8ToBytes(str);  
181 - } else {  
182 - result = new Uint8Array(utf8ToBytes(str));  
183 - }  
184 - return result; 173 +export function objToBase64URI(obj: any): string {
  174 + return encodeURIComponent(objToBase64(obj));
185 } 175 }
186 176
187 -function utf8Decode(bytes: Uint8Array | number[]): string {  
188 - return utf8Slice(bytes, 0, bytes.length); 177 +export function base64toObj(b64Encoded: string): any {
  178 + const json = decodeURIComponent(atob(b64Encoded).split('').map((c) => {
  179 + return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
  180 + }).join(''));
  181 + return JSON.parse(json);
189 } 182 }
190 183
191 const scrollRegex = /(auto|scroll)/; 184 const scrollRegex = /(auto|scroll)/;
@@ -275,129 +268,6 @@ function easeInOut( @@ -275,129 +268,6 @@ function easeInOut(
275 ); 268 );
276 } 269 }
277 270
278 -function utf8Slice(buf: Uint8Array | number[], start: number, end: number): string {  
279 - let res = '';  
280 - let tmp = '';  
281 - end = Math.min(buf.length, end || Infinity);  
282 - start = start || 0;  
283 -  
284 - for (let i = start; i < end; i++) {  
285 - if (buf[i] <= 0x7F) {  
286 - res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i]);  
287 - tmp = '';  
288 - } else {  
289 - tmp += '%' + buf[i].toString(16);  
290 - }  
291 - }  
292 - return res + decodeUtf8Char(tmp);  
293 -}  
294 -  
295 -function decodeUtf8Char(str: string): string {  
296 - try {  
297 - return decodeURIComponent(str);  
298 - } catch (err) {  
299 - return String.fromCharCode(0xFFFD); // UTF 8 invalid char  
300 - }  
301 -}  
302 -  
303 -function utf8ToBytes(input: string, units?: number): number[] {  
304 - units = units || Infinity;  
305 - let codePoint: number;  
306 - const length = input.length;  
307 - let leadSurrogate: number = null;  
308 - const bytes: number[] = [];  
309 - let i = 0;  
310 -  
311 - for (; i < length; i++) {  
312 - codePoint = input.charCodeAt(i);  
313 -  
314 - // is surrogate component  
315 - if (codePoint > 0xD7FF && codePoint < 0xE000) {  
316 - // last char was a lead  
317 - if (leadSurrogate) {  
318 - // 2 leads in a row  
319 - if (codePoint < 0xDC00) {  
320 - units -= 3;  
321 - if (units > -1) { bytes.push(0xEF, 0xBF, 0xBD); }  
322 - leadSurrogate = codePoint;  
323 - continue;  
324 - } else {  
325 - // valid surrogate pair  
326 - // tslint:disable-next-line:no-bitwise  
327 - codePoint = leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00 | 0x10000;  
328 - leadSurrogate = null;  
329 - }  
330 - } else {  
331 - // no lead yet  
332 -  
333 - if (codePoint > 0xDBFF) {  
334 - // unexpected trail  
335 - units -= 3;  
336 - if (units > -1) { bytes.push(0xEF, 0xBF, 0xBD); }  
337 - continue;  
338 - } else if (i + 1 === length) {  
339 - // unpaired lead  
340 - units -= 3;  
341 - if (units > -1) { bytes.push(0xEF, 0xBF, 0xBD); }  
342 - continue;  
343 - } else {  
344 - // valid lead  
345 - leadSurrogate = codePoint;  
346 - continue;  
347 - }  
348 - }  
349 - } else if (leadSurrogate) {  
350 - // valid bmp char, but last char was a lead  
351 - units -= 3;  
352 - if (units > -1) { bytes.push(0xEF, 0xBF, 0xBD); }  
353 - leadSurrogate = null;  
354 - }  
355 -  
356 - // encode utf8  
357 - if (codePoint < 0x80) {  
358 - units -= 1;  
359 - if (units < 0) { break; }  
360 - bytes.push(codePoint);  
361 - } else if (codePoint < 0x800) {  
362 - units -= 2;  
363 - if (units < 0) { break; }  
364 - bytes.push(  
365 - // tslint:disable-next-line:no-bitwise  
366 - codePoint >> 0x6 | 0xC0,  
367 - // tslint:disable-next-line:no-bitwise  
368 - codePoint & 0x3F | 0x80  
369 - );  
370 - } else if (codePoint < 0x10000) {  
371 - units -= 3;  
372 - if (units < 0) { break; }  
373 - bytes.push(  
374 - // tslint:disable-next-line:no-bitwise  
375 - codePoint >> 0xC | 0xE0,  
376 - // tslint:disable-next-line:no-bitwise  
377 - codePoint >> 0x6 & 0x3F | 0x80,  
378 - // tslint:disable-next-line:no-bitwise  
379 - codePoint & 0x3F | 0x80  
380 - );  
381 - } else if (codePoint < 0x200000) {  
382 - units -= 4;  
383 - if (units < 0) { break; }  
384 - bytes.push(  
385 - // tslint:disable-next-line:no-bitwise  
386 - codePoint >> 0x12 | 0xF0,  
387 - // tslint:disable-next-line:no-bitwise  
388 - codePoint >> 0xC & 0x3F | 0x80,  
389 - // tslint:disable-next-line:no-bitwise  
390 - codePoint >> 0x6 & 0x3F | 0x80,  
391 - // tslint:disable-next-line:no-bitwise  
392 - codePoint & 0x3F | 0x80  
393 - );  
394 - } else {  
395 - throw new Error('Invalid code point');  
396 - }  
397 - }  
398 - return bytes;  
399 -}  
400 -  
401 export function deepClone<T>(target: T, ignoreFields?: string[]): T { 271 export function deepClone<T>(target: T, ignoreFields?: string[]): T {
402 if (target === null) { 272 if (target === null) {
403 return target; 273 return target;
@@ -24,7 +24,7 @@ import { Router } from '@angular/router'; @@ -24,7 +24,7 @@ import { Router } from '@angular/router';
24 import { DialogComponent } from '@app/shared/components/dialog.component'; 24 import { DialogComponent } from '@app/shared/components/dialog.component';
25 import { UtilsService } from '@core/services/utils.service'; 25 import { UtilsService } from '@core/services/utils.service';
26 import { Dashboard, DashboardLayoutId } from '@app/shared/models/dashboard.models'; 26 import { Dashboard, DashboardLayoutId } from '@app/shared/models/dashboard.models';
27 -import { objToBase64 } from '@core/utils'; 27 +import { objToBase64URI } from '@core/utils';
28 import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; 28 import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
29 import { EntityId } from '@app/shared/models/id/entity-id'; 29 import { EntityId } from '@app/shared/models/id/entity-id';
30 import { Widget } from '@app/shared/models/widget.models'; 30 import { Widget } from '@app/shared/models/widget.models';
@@ -205,7 +205,7 @@ export class AddWidgetToDashboardDialogComponent extends @@ -205,7 +205,7 @@ export class AddWidgetToDashboardDialogComponent extends
205 id: targetState, 205 id: targetState,
206 params: {} 206 params: {}
207 }; 207 };
208 - const state = objToBase64([ stateObject ]); 208 + const state = objToBase64URI([ stateObject ]);
209 url = `/dashboards/${theDashboard.id.id}?state=${state}`; 209 url = `/dashboards/${theDashboard.id.id}?state=${state}`;
210 } else { 210 } else {
211 url = `/dashboards/${theDashboard.id.id}`; 211 url = `/dashboards/${theDashboard.id.id}`;
@@ -35,7 +35,6 @@ import { @@ -35,7 +35,6 @@ import {
35 } from '@angular/core'; 35 } from '@angular/core';
36 import { DashboardWidget } from '@home/models/dashboard-component.models'; 36 import { DashboardWidget } from '@home/models/dashboard-component.models';
37 import { 37 import {
38 - Datasource,  
39 defaultLegendConfig, 38 defaultLegendConfig,
40 LegendConfig, 39 LegendConfig,
41 LegendData, 40 LegendData,
@@ -55,7 +54,7 @@ import { AppState } from '@core/core.state'; @@ -55,7 +54,7 @@ import { AppState } from '@core/core.state';
55 import { WidgetService } from '@core/http/widget.service'; 54 import { WidgetService } from '@core/http/widget.service';
56 import { UtilsService } from '@core/services/utils.service'; 55 import { UtilsService } from '@core/services/utils.service';
57 import { forkJoin, Observable, of, ReplaySubject, Subscription, throwError } from 'rxjs'; 56 import { forkJoin, Observable, of, ReplaySubject, Subscription, throwError } from 'rxjs';
58 -import { deepClone, isDefined, objToBase64 } from '@core/utils'; 57 +import { deepClone, isDefined, objToBase64URI } from '@core/utils';
59 import { 58 import {
60 IDynamicWidgetComponent, 59 IDynamicWidgetComponent,
61 WidgetContext, 60 WidgetContext,
@@ -68,7 +67,8 @@ import { @@ -68,7 +67,8 @@ import {
68 StateObject, 67 StateObject,
69 StateParams, 68 StateParams,
70 SubscriptionEntityInfo, 69 SubscriptionEntityInfo,
71 - SubscriptionInfo, SubscriptionMessage, 70 + SubscriptionInfo,
  71 + SubscriptionMessage,
72 WidgetSubscriptionContext, 72 WidgetSubscriptionContext,
73 WidgetSubscriptionOptions 73 WidgetSubscriptionOptions
74 } from '@core/api/widget-api.models'; 74 } from '@core/api/widget-api.models';
@@ -80,11 +80,9 @@ import { catchError, switchMap } from 'rxjs/operators'; @@ -80,11 +80,9 @@ import { catchError, switchMap } from 'rxjs/operators';
80 import { ActionNotificationShow } from '@core/notification/notification.actions'; 80 import { ActionNotificationShow } from '@core/notification/notification.actions';
81 import { TimeService } from '@core/services/time.service'; 81 import { TimeService } from '@core/services/time.service';
82 import { DeviceService } from '@app/core/http/device.service'; 82 import { DeviceService } from '@app/core/http/device.service';
83 -import { AlarmService } from '@app/core/http/alarm.service';  
84 import { ExceptionData } from '@shared/models/error.models'; 83 import { ExceptionData } from '@shared/models/error.models';
85 import { WidgetComponentService } from './widget-component.service'; 84 import { WidgetComponentService } from './widget-component.service';
86 import { Timewindow } from '@shared/models/time/time.models'; 85 import { Timewindow } from '@shared/models/time/time.models';
87 -import { AlarmSearchStatus } from '@shared/models/alarm.models';  
88 import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; 86 import { CancelAnimationFrame, RafService } from '@core/services/raf.service';
89 import { DashboardService } from '@core/http/dashboard.service'; 87 import { DashboardService } from '@core/http/dashboard.service';
90 import { WidgetSubscription } from '@core/api/widget-subscription'; 88 import { WidgetSubscription } from '@core/api/widget-subscription';
@@ -688,7 +686,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI @@ -688,7 +686,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
688 686
689 private destroyDynamicWidgetComponent() { 687 private destroyDynamicWidgetComponent() {
690 if (this.widgetContext.$containerParent && this.widgetResize$) { 688 if (this.widgetContext.$containerParent && this.widgetResize$) {
691 - this.widgetResize$.disconnect() 689 + this.widgetResize$.disconnect();
692 } 690 }
693 if (this.dynamicWidgetComponentRef) { 691 if (this.dynamicWidgetComponentRef) {
694 this.dynamicWidgetComponentRef.destroy(); 692 this.dynamicWidgetComponentRef.destroy();
@@ -1023,7 +1021,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI @@ -1023,7 +1021,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
1023 if (targetDashboardStateId) { 1021 if (targetDashboardStateId) {
1024 stateObject.id = targetDashboardStateId; 1022 stateObject.id = targetDashboardStateId;
1025 } 1023 }
1026 - const state = objToBase64([ stateObject ]); 1024 + const state = objToBase64URI([ stateObject ]);
1027 const isSinglePage = this.route.snapshot.data.singlePageMode; 1025 const isSinglePage = this.route.snapshot.data.singlePageMode;
1028 let url; 1026 let url;
1029 if (isSinglePage) { 1027 if (isSinglePage) {
@@ -23,7 +23,7 @@ import { StateControllerComponent } from './state-controller.component'; @@ -23,7 +23,7 @@ import { StateControllerComponent } from './state-controller.component';
23 import { StatesControllerService } from '@home/pages/dashboard/states/states-controller.service'; 23 import { StatesControllerService } from '@home/pages/dashboard/states/states-controller.service';
24 import { EntityId } from '@app/shared/models/id/entity-id'; 24 import { EntityId } from '@app/shared/models/id/entity-id';
25 import { UtilsService } from '@core/services/utils.service'; 25 import { UtilsService } from '@core/services/utils.service';
26 -import { base64toObj, objToBase64 } from '@app/core/utils'; 26 +import { base64toObj, objToBase64URI } from '@app/core/utils';
27 import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; 27 import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
28 import { EntityService } from '@core/http/entity.service'; 28 import { EntityService } from '@core/http/entity.service';
29 29
@@ -237,7 +237,7 @@ export class DefaultStateControllerComponent extends StateControllerComponent im @@ -237,7 +237,7 @@ export class DefaultStateControllerComponent extends StateControllerComponent im
237 237
238 private updateLocation() { 238 private updateLocation() {
239 if (this.stateObject[0].id) { 239 if (this.stateObject[0].id) {
240 - const newState = objToBase64(this.stateObject); 240 + const newState = objToBase64URI(this.stateObject);
241 this.updateStateParam(newState); 241 this.updateStateParam(newState);
242 } 242 }
243 } 243 }
@@ -23,7 +23,7 @@ import { StateControllerComponent } from './state-controller.component'; @@ -23,7 +23,7 @@ import { StateControllerComponent } from './state-controller.component';
23 import { StatesControllerService } from '@home/pages/dashboard/states/states-controller.service'; 23 import { StatesControllerService } from '@home/pages/dashboard/states/states-controller.service';
24 import { EntityId } from '@app/shared/models/id/entity-id'; 24 import { EntityId } from '@app/shared/models/id/entity-id';
25 import { UtilsService } from '@core/services/utils.service'; 25 import { UtilsService } from '@core/services/utils.service';
26 -import { base64toObj, insertVariable, isEmpty, objToBase64 } from '@app/core/utils'; 26 +import { base64toObj, insertVariable, isEmpty, objToBase64URI } from '@app/core/utils';
27 import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; 27 import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
28 import { EntityService } from '@core/http/entity.service'; 28 import { EntityService } from '@core/http/entity.service';
29 import { EntityType } from '@shared/models/entity-type.models'; 29 import { EntityType } from '@shared/models/entity-type.models';
@@ -281,7 +281,7 @@ export class EntityStateControllerComponent extends StateControllerComponent imp @@ -281,7 +281,7 @@ export class EntityStateControllerComponent extends StateControllerComponent imp
281 if (this.isDefaultState()) { 281 if (this.isDefaultState()) {
282 newState = null; 282 newState = null;
283 } else { 283 } else {
284 - newState = objToBase64(this.stateObject); 284 + newState = objToBase64URI(this.stateObject);
285 } 285 }
286 this.updateStateParam(newState); 286 this.updateStateParam(newState);
287 } 287 }
@@ -2138,7 +2138,7 @@ base64-arraybuffer@0.1.5: @@ -2138,7 +2138,7 @@ base64-arraybuffer@0.1.5:
2138 resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" 2138 resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
2139 integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg= 2139 integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg=
2140 2140
2141 -base64-js@^1.0.2, base64-js@^1.3.1: 2141 +base64-js@^1.0.2:
2142 version "1.3.1" 2142 version "1.3.1"
2143 resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" 2143 resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
2144 integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== 2144 integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==