gridstack 8.1.2 → 8.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/angular/package.json +1 -1
- package/angular/projects/demo/src/app/app.component.ts +8 -8
- package/angular/projects/demo/src/app/app.module.ts +4 -3
- package/angular/projects/demo/src/app/dummy.component.ts +12 -6
- package/angular/projects/lib/package.json +1 -1
- package/angular/projects/lib/src/lib/base-widget.ts +28 -0
- package/angular/projects/lib/src/lib/gridstack-item.component.ts +10 -2
- package/angular/projects/lib/src/lib/gridstack.component.ts +83 -36
- package/angular/projects/lib/src/lib/gridstack.module.ts +1 -1
- package/angular/projects/lib/src/public-api.ts +1 -0
- package/dist/angular/esm2020/lib/base-widget.mjs +30 -0
- package/dist/angular/esm2020/lib/gridstack-item.component.mjs +3 -2
- package/dist/angular/esm2020/lib/gridstack.component.mjs +79 -34
- package/dist/angular/esm2020/lib/gridstack.module.mjs +2 -2
- package/dist/angular/esm2020/public-api.mjs +2 -1
- package/dist/angular/fesm2015/gridstack-angular.mjs +110 -38
- package/dist/angular/fesm2015/gridstack-angular.mjs.map +1 -1
- package/dist/angular/fesm2020/gridstack-angular.mjs +108 -37
- package/dist/angular/fesm2020/gridstack-angular.mjs.map +1 -1
- package/dist/angular/lib/base-widget.d.ts +16 -0
- package/dist/angular/lib/gridstack-item.component.d.ts +7 -2
- package/dist/angular/lib/gridstack.component.d.ts +15 -7
- package/dist/angular/package.json +1 -1
- package/dist/angular/public-api.d.ts +1 -0
- package/dist/angular/src/base-widget.ts +28 -0
- package/dist/angular/src/gridstack-item.component.ts +10 -2
- package/dist/angular/src/gridstack.component.ts +83 -36
- package/dist/angular/src/gridstack.module.ts +1 -1
- package/dist/dd-base-impl.d.ts +1 -1
- package/dist/dd-base-impl.js +1 -1
- package/dist/dd-base-impl.js.map +1 -1
- package/dist/dd-draggable.d.ts +1 -1
- package/dist/dd-draggable.js +1 -1
- package/dist/dd-draggable.js.map +1 -1
- package/dist/dd-droppable.d.ts +1 -1
- package/dist/dd-droppable.js +1 -1
- package/dist/dd-droppable.js.map +1 -1
- package/dist/dd-element.d.ts +1 -1
- package/dist/dd-element.js +1 -1
- package/dist/dd-element.js.map +1 -1
- package/dist/dd-gridstack.d.ts +1 -1
- package/dist/dd-gridstack.js +1 -1
- package/dist/dd-gridstack.js.map +1 -1
- package/dist/dd-manager.d.ts +1 -1
- package/dist/dd-manager.js +1 -1
- package/dist/dd-manager.js.map +1 -1
- package/dist/dd-resizable-handle.d.ts +1 -1
- package/dist/dd-resizable-handle.js +1 -1
- package/dist/dd-resizable-handle.js.map +1 -1
- package/dist/dd-resizable.d.ts +1 -1
- package/dist/dd-resizable.js +1 -1
- package/dist/dd-resizable.js.map +1 -1
- package/dist/dd-touch.d.ts +1 -1
- package/dist/dd-touch.js +1 -1
- package/dist/dd-touch.js.map +1 -1
- package/dist/es5/dd-base-impl.d.ts +1 -1
- package/dist/es5/dd-base-impl.js +1 -1
- package/dist/es5/dd-base-impl.js.map +1 -1
- package/dist/es5/dd-draggable.d.ts +1 -1
- package/dist/es5/dd-draggable.js +1 -1
- package/dist/es5/dd-draggable.js.map +1 -1
- package/dist/es5/dd-droppable.d.ts +1 -1
- package/dist/es5/dd-droppable.js +1 -1
- package/dist/es5/dd-droppable.js.map +1 -1
- package/dist/es5/dd-element.d.ts +1 -1
- package/dist/es5/dd-element.js +1 -1
- package/dist/es5/dd-element.js.map +1 -1
- package/dist/es5/dd-gridstack.d.ts +1 -1
- package/dist/es5/dd-gridstack.js +1 -1
- package/dist/es5/dd-gridstack.js.map +1 -1
- package/dist/es5/dd-manager.d.ts +1 -1
- package/dist/es5/dd-manager.js +1 -1
- package/dist/es5/dd-manager.js.map +1 -1
- package/dist/es5/dd-resizable-handle.d.ts +1 -1
- package/dist/es5/dd-resizable-handle.js +1 -1
- package/dist/es5/dd-resizable-handle.js.map +1 -1
- package/dist/es5/dd-resizable.d.ts +1 -1
- package/dist/es5/dd-resizable.js +1 -1
- package/dist/es5/dd-resizable.js.map +1 -1
- package/dist/es5/dd-touch.d.ts +1 -1
- package/dist/es5/dd-touch.js +1 -1
- package/dist/es5/dd-touch.js.map +1 -1
- package/dist/es5/gridstack-all.js +1 -1
- package/dist/es5/gridstack-all.js.LICENSE.txt +1 -1
- package/dist/es5/gridstack-all.js.map +1 -1
- package/dist/es5/gridstack-engine.d.ts +1 -1
- package/dist/es5/gridstack-engine.js +1 -1
- package/dist/es5/gridstack-engine.js.map +1 -1
- package/dist/es5/gridstack-poly.js +1 -1
- package/dist/es5/gridstack.d.ts +8 -5
- package/dist/es5/gridstack.js +13 -7
- package/dist/es5/gridstack.js.map +1 -1
- package/dist/es5/types.d.ts +1 -1
- package/dist/es5/types.js +1 -1
- package/dist/es5/types.js.map +1 -1
- package/dist/es5/utils.d.ts +1 -1
- package/dist/es5/utils.js +1 -1
- package/dist/es5/utils.js.map +1 -1
- package/dist/gridstack-all.js +1 -1
- package/dist/gridstack-all.js.LICENSE.txt +1 -1
- package/dist/gridstack-all.js.map +1 -1
- package/dist/gridstack-engine.d.ts +1 -1
- package/dist/gridstack-engine.js +1 -1
- package/dist/gridstack-engine.js.map +1 -1
- package/dist/gridstack.css +1 -1
- package/dist/gridstack.d.ts +8 -5
- package/dist/gridstack.js +13 -7
- package/dist/gridstack.js.map +1 -1
- package/dist/src/gridstack.scss +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +1 -1
- package/dist/utils.js.map +1 -1
- package/doc/CHANGES.md +9 -2
- package/package.json +1 -1
package/angular/package.json
CHANGED
|
@@ -4,9 +4,9 @@ import { AngularSimpleComponent } from './simple';
|
|
|
4
4
|
import { AngularNgForTestComponent } from './ngFor';
|
|
5
5
|
import { AngularNgForCmdTestComponent } from './ngFor_cmd';
|
|
6
6
|
|
|
7
|
-
// NOTE: local testing of file
|
|
8
|
-
import { GridstackComponent, NgGridStackOptions, NgGridStackWidget, elementCB, gsCreateNgComponents, nodesCB } from '
|
|
9
|
-
|
|
7
|
+
// NOTE: local testing of file
|
|
8
|
+
// import { GridstackComponent, NgGridStackOptions, NgGridStackWidget, elementCB, gsCreateNgComponents, nodesCB } from './gridstack.component';
|
|
9
|
+
import { GridstackComponent, NgGridStackOptions, NgGridStackWidget, elementCB, gsCreateNgComponents, nodesCB } from 'gridstack/dist/angular';
|
|
10
10
|
|
|
11
11
|
// unique ids sets for each item for correct ngFor updating
|
|
12
12
|
let ids = 1;
|
|
@@ -25,7 +25,7 @@ export class AppComponent implements OnInit {
|
|
|
25
25
|
@ViewChild('textArea', {static: true}) textEl?: ElementRef<HTMLTextAreaElement>;
|
|
26
26
|
|
|
27
27
|
// which sample to show
|
|
28
|
-
public show =
|
|
28
|
+
public show = 5;
|
|
29
29
|
|
|
30
30
|
/** sample grid options and items to load... */
|
|
31
31
|
public items: GridStackWidget[] = [
|
|
@@ -38,9 +38,9 @@ export class AppComponent implements OnInit {
|
|
|
38
38
|
float: true,
|
|
39
39
|
minRow: 1,
|
|
40
40
|
}
|
|
41
|
-
public gridOptionsFull:
|
|
41
|
+
public gridOptionsFull: NgGridStackOptions = {
|
|
42
42
|
...this.gridOptions,
|
|
43
|
-
children:
|
|
43
|
+
children: [{x:0, y:0, selector:'app-a'}, {x:1, y:0, selector:'app-b'}, {x:2, y:0, content:'plain html'}],
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
// nested grid options
|
|
@@ -50,7 +50,7 @@ export class AppComponent implements OnInit {
|
|
|
50
50
|
acceptWidgets: true, // will accept .grid-stack-item by default
|
|
51
51
|
margin: 5,
|
|
52
52
|
};
|
|
53
|
-
private sub1: NgGridStackWidget[] = [ {x:0, y:0,
|
|
53
|
+
private sub1: NgGridStackWidget[] = [ {x:0, y:0, selector:'app-a'}, {x:1, y:0, selector:'app-b'}, {x:2, y:0, selector:'app-c'}, {x:3, y:0}, {x:0, y:1}, {x:1, y:1}];
|
|
54
54
|
private sub2: NgGridStackWidget[] = [ {x:0, y:0}, {x:0, y:1, w:2}];
|
|
55
55
|
private subChildren: NgGridStackWidget[] = [
|
|
56
56
|
{x:0, y:0, content: 'regular item'},
|
|
@@ -71,7 +71,7 @@ export class AppComponent implements OnInit {
|
|
|
71
71
|
constructor() {
|
|
72
72
|
// give them content and unique id to make sure we track them during changes below...
|
|
73
73
|
[...this.items, ...this.subChildren, ...this.sub1, ...this.sub2].forEach((w: NgGridStackWidget) => {
|
|
74
|
-
if (!w.
|
|
74
|
+
if (!w.selector && !w.subGridOpts) w.content = `item ${ids}`;
|
|
75
75
|
w.id = String(ids++);
|
|
76
76
|
});
|
|
77
77
|
}
|
|
@@ -7,9 +7,10 @@ import { AngularNgForCmdTestComponent } from './ngFor_cmd';
|
|
|
7
7
|
import { AngularSimpleComponent } from './simple';
|
|
8
8
|
import { AComponent, BComponent, CComponent } from './dummy.component';
|
|
9
9
|
|
|
10
|
-
// local
|
|
11
|
-
import { GridstackModule
|
|
12
|
-
// import {
|
|
10
|
+
// local testing
|
|
11
|
+
// import { GridstackModule } from './gridstack.module';
|
|
12
|
+
// import { GridstackComponent } from './gridstack.component';
|
|
13
|
+
import { GridstackModule, GridstackComponent } from 'gridstack/dist/angular';
|
|
13
14
|
|
|
14
15
|
@NgModule({
|
|
15
16
|
imports: [
|
|
@@ -1,29 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* gridstack.component.ts 8.
|
|
2
|
+
* gridstack.component.ts 8.2.0
|
|
3
3
|
* Copyright (c) 2022 Alain Dumesny - see GridStack root license
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
// dummy testing component that will be grid items content
|
|
7
7
|
|
|
8
|
-
import { Component } from '@angular/core';
|
|
8
|
+
import { Component, OnDestroy, Input } from '@angular/core';
|
|
9
|
+
import { BaseWidget, NgCompInputs } from 'gridstack/dist/angular';
|
|
9
10
|
|
|
10
11
|
@Component({
|
|
11
12
|
selector: 'app-a',
|
|
12
|
-
template: 'Comp A',
|
|
13
|
+
template: 'Comp A {{text}}',
|
|
13
14
|
})
|
|
14
|
-
export class AComponent {
|
|
15
|
+
export class AComponent extends BaseWidget implements OnDestroy {
|
|
16
|
+
@Input() text: string = 'foo'; // test custom input data
|
|
17
|
+
public override serialize(): NgCompInputs | undefined { return this.text ? {text: this.text} : undefined; }
|
|
18
|
+
ngOnDestroy() {
|
|
19
|
+
console.log('Comp A destroyed'); // test to make sure cleanup happens
|
|
20
|
+
}
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
@Component({
|
|
18
24
|
selector: 'app-b',
|
|
19
25
|
template: 'Comp B',
|
|
20
26
|
})
|
|
21
|
-
export class BComponent {
|
|
27
|
+
export class BComponent extends BaseWidget {
|
|
22
28
|
}
|
|
23
29
|
|
|
24
30
|
@Component({
|
|
25
31
|
selector: 'app-c',
|
|
26
32
|
template: 'Comp C',
|
|
27
33
|
})
|
|
28
|
-
export class CComponent {
|
|
34
|
+
export class CComponent extends BaseWidget {
|
|
29
35
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gridstack-item.component.ts 8.2.0
|
|
3
|
+
* Copyright (c) 2022 Alain Dumesny - see GridStack root license
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Base interface that all widgets need to implement in order for GridstackItemComponent to correctly save/load/delete/..
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Injectable } from '@angular/core';
|
|
11
|
+
import { NgCompInputs, NgGridStackWidget } from './gridstack.component';
|
|
12
|
+
|
|
13
|
+
@Injectable()
|
|
14
|
+
export abstract class BaseWidget {
|
|
15
|
+
/**
|
|
16
|
+
* REDEFINE to return an object representing the data needed to re-create yourself, other than `selector` already handled.
|
|
17
|
+
* This should map directly to the @Input() fields of this objects on create, so a simple apply can be used on read
|
|
18
|
+
*/
|
|
19
|
+
public serialize(): NgCompInputs | undefined { return; }
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* REDEFINE this if your widget needs to read from saved data and transform it to create itself - you do this for
|
|
23
|
+
* things that are not mapped directly into @Input() fields for example.
|
|
24
|
+
*/
|
|
25
|
+
public deserialize(w: NgGridStackWidget) {
|
|
26
|
+
if (w.input) Object.assign(this, w.input);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* gridstack-item.component.ts 8.
|
|
2
|
+
* gridstack-item.component.ts 8.2.0
|
|
3
3
|
* Copyright (c) 2022 Alain Dumesny - see GridStack root license
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { Component, ElementRef, Input, ViewChild, ViewContainerRef, OnDestroy } from '@angular/core';
|
|
6
|
+
import { Component, ElementRef, Input, ViewChild, ViewContainerRef, OnDestroy, ComponentRef } from '@angular/core';
|
|
7
7
|
import { GridItemHTMLElement, GridStackNode } from 'gridstack';
|
|
8
|
+
import { BaseWidget } from './base-widget';
|
|
8
9
|
|
|
9
10
|
/** store element to Ng Class pointer back */
|
|
10
11
|
export interface GridItemCompHTMLElement extends GridItemHTMLElement {
|
|
@@ -35,6 +36,12 @@ export class GridstackItemComponent implements OnDestroy {
|
|
|
35
36
|
/** container to append items dynamically */
|
|
36
37
|
@ViewChild('container', { read: ViewContainerRef, static: true}) public container?: ViewContainerRef;
|
|
37
38
|
|
|
39
|
+
/** ComponentRef of ourself - used by dynamic object to correctly get removed */
|
|
40
|
+
public ref: ComponentRef<GridstackItemComponent> | undefined;
|
|
41
|
+
|
|
42
|
+
/** child component so we can save/restore additional data to be saved along */
|
|
43
|
+
public childWidget: BaseWidget | undefined;
|
|
44
|
+
|
|
38
45
|
/** list of options for creating/updating this item */
|
|
39
46
|
@Input() public set options(val: GridStackNode) {
|
|
40
47
|
if (this.el.gridstackNode?.grid) {
|
|
@@ -65,6 +72,7 @@ export class GridstackItemComponent implements OnDestroy {
|
|
|
65
72
|
}
|
|
66
73
|
|
|
67
74
|
public ngOnDestroy(): void {
|
|
75
|
+
delete this.ref;
|
|
68
76
|
delete this.el._gridItemComp;
|
|
69
77
|
}
|
|
70
78
|
}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* gridstack.component.ts 8.
|
|
2
|
+
* gridstack.component.ts 8.2.0
|
|
3
3
|
* Copyright (c) 2022 Alain Dumesny - see GridStack root license
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { AfterContentInit, Component, ContentChildren, ElementRef, EventEmitter, Input,
|
|
7
|
-
OnDestroy, OnInit, Output, QueryList, Type, ViewChild, ViewContainerRef, reflectComponentType } from '@angular/core';
|
|
7
|
+
OnDestroy, OnInit, Output, QueryList, Type, ViewChild, ViewContainerRef, reflectComponentType, ComponentRef } from '@angular/core';
|
|
8
8
|
import { Subject } from 'rxjs';
|
|
9
9
|
import { takeUntil } from 'rxjs/operators';
|
|
10
10
|
import { GridHTMLElement, GridItemHTMLElement, GridStack, GridStackNode, GridStackOptions, GridStackWidget } from 'gridstack';
|
|
11
11
|
|
|
12
12
|
import { GridItemCompHTMLElement, GridstackItemComponent } from './gridstack-item.component';
|
|
13
|
+
import { BaseWidget } from './base-widget';
|
|
13
14
|
|
|
14
15
|
/** events handlers emitters signature for different events */
|
|
15
16
|
export type eventCB = {event: Event};
|
|
@@ -17,12 +18,15 @@ export type elementCB = {event: Event, el: GridItemHTMLElement};
|
|
|
17
18
|
export type nodesCB = {event: Event, nodes: GridStackNode[]};
|
|
18
19
|
export type droppedCB = {event: Event, previousNode: GridStackNode, newNode: GridStackNode};
|
|
19
20
|
|
|
21
|
+
export type NgCompInputs = {[key: string]: any};
|
|
22
|
+
|
|
20
23
|
/** extends to store Ng Component selector, instead/inAddition to content */
|
|
21
24
|
export interface NgGridStackWidget extends GridStackWidget {
|
|
22
|
-
|
|
25
|
+
selector?: string; // component type to create as content
|
|
26
|
+
input?: NgCompInputs; // serialized data for the component input fields
|
|
23
27
|
}
|
|
24
28
|
export interface NgGridStackNode extends GridStackNode {
|
|
25
|
-
|
|
29
|
+
selector?: string; // component type to create as content
|
|
26
30
|
}
|
|
27
31
|
export interface NgGridStackOptions extends GridStackOptions {
|
|
28
32
|
children?: NgGridStackWidget[];
|
|
@@ -96,6 +100,9 @@ export class GridstackComponent implements OnInit, AfterContentInit, OnDestroy {
|
|
|
96
100
|
/** return the GridStack class */
|
|
97
101
|
public get grid(): GridStack | undefined { return this._grid; }
|
|
98
102
|
|
|
103
|
+
/** ComponentRef of ourself - used by dynamic object to correctly get removed */
|
|
104
|
+
public ref: ComponentRef<GridstackComponent> | undefined;
|
|
105
|
+
|
|
99
106
|
/**
|
|
100
107
|
* stores the selector -> Type mapping, so we can create items dynamically from a string.
|
|
101
108
|
* Unfortunately Ng doesn't provide public access to that mapping.
|
|
@@ -107,8 +114,7 @@ export class GridstackComponent implements OnInit, AfterContentInit, OnDestroy {
|
|
|
107
114
|
}
|
|
108
115
|
/** return the ng Component selector */
|
|
109
116
|
public static getSelector(type: Type<Object>): string {
|
|
110
|
-
|
|
111
|
-
return mirror.selector;
|
|
117
|
+
return reflectComponentType(type)!.selector;
|
|
112
118
|
}
|
|
113
119
|
|
|
114
120
|
private _options?: GridStackOptions;
|
|
@@ -145,6 +151,7 @@ export class GridstackComponent implements OnInit, AfterContentInit, OnDestroy {
|
|
|
145
151
|
}
|
|
146
152
|
|
|
147
153
|
public ngOnDestroy(): void {
|
|
154
|
+
delete this.ref;
|
|
148
155
|
this.ngUnsubscribe.next();
|
|
149
156
|
this.ngUnsubscribe.complete();
|
|
150
157
|
this.grid?.destroy();
|
|
@@ -197,44 +204,84 @@ export class GridstackComponent implements OnInit, AfterContentInit, OnDestroy {
|
|
|
197
204
|
/**
|
|
198
205
|
* can be used when a new item needs to be created, which we do as a Angular component, or deleted (skip)
|
|
199
206
|
**/
|
|
200
|
-
export function gsCreateNgComponents(host: GridCompHTMLElement | HTMLElement, w: NgGridStackWidget |
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
if (gridItemComp) {
|
|
209
|
-
grid = gridItemComp.container?.createComponent(GridstackComponent)?.instance;
|
|
210
|
-
} else {
|
|
207
|
+
export function gsCreateNgComponents(host: GridCompHTMLElement | HTMLElement, w: NgGridStackWidget | GridStackNode, add: boolean, isGrid: boolean): HTMLElement | undefined {
|
|
208
|
+
if (add) {
|
|
209
|
+
//
|
|
210
|
+
// create the component dynamically - see https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html
|
|
211
|
+
//
|
|
212
|
+
if (!host) return;
|
|
213
|
+
if (isGrid) {
|
|
214
|
+
const container = (host.parentElement as GridItemCompHTMLElement)?._gridItemComp?.container;
|
|
211
215
|
// TODO: figure out how to create ng component inside regular Div. need to access app injectors...
|
|
212
|
-
//
|
|
213
|
-
//
|
|
214
|
-
//
|
|
216
|
+
// if (!container) {
|
|
217
|
+
// const hostElement: Element = host;
|
|
218
|
+
// const environmentInjector: EnvironmentInjector;
|
|
219
|
+
// grid = createComponent(GridstackComponent, {environmentInjector, hostElement})?.instance;
|
|
220
|
+
// }
|
|
221
|
+
const gridRef = container?.createComponent(GridstackComponent);
|
|
222
|
+
const grid = gridRef?.instance;
|
|
223
|
+
if (!grid) return;
|
|
224
|
+
grid.ref = gridRef;
|
|
225
|
+
grid.options = w as GridStackOptions;
|
|
226
|
+
return grid.el;
|
|
227
|
+
} else {
|
|
228
|
+
const gridComp = (host as GridCompHTMLElement)._gridComp;
|
|
229
|
+
const gridItemRef = gridComp?.container?.createComponent(GridstackItemComponent);
|
|
230
|
+
const gridItem = gridItemRef?.instance;
|
|
231
|
+
if (!gridItem) return;
|
|
232
|
+
gridItem.ref = gridItemRef
|
|
233
|
+
|
|
234
|
+
// IFF we're not a subGrid, define what type of component to create as child, OR you can do it GridstackItemComponent template, but this is more generic
|
|
235
|
+
const selector = (w as NgGridStackWidget).selector;
|
|
236
|
+
const type = selector ? GridstackComponent.selectorToType[selector] : undefined;
|
|
237
|
+
if (!w.subGridOpts && type) {
|
|
238
|
+
const childWidget = gridItem.container?.createComponent(type)?.instance as BaseWidget;
|
|
239
|
+
if (typeof childWidget?.serialize === 'function' && typeof childWidget?.deserialize === 'function') {
|
|
240
|
+
// proper BaseWidget subclass, save it and load additional data
|
|
241
|
+
gridItem.childWidget = childWidget;
|
|
242
|
+
childWidget.deserialize(w);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return gridItem.el;
|
|
215
247
|
}
|
|
216
|
-
if (grid) grid.options = w as GridStackOptions;
|
|
217
|
-
return grid?.el;
|
|
218
248
|
} else {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
//
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
249
|
+
//
|
|
250
|
+
// REMOVE - have to call ComponentRef:destroy() for dynamic objects to correctly remove themselves
|
|
251
|
+
// Note: this will destroy all children dynamic components as well: gridItem -> childWidget
|
|
252
|
+
//
|
|
253
|
+
const n = w as GridStackNode;
|
|
254
|
+
if (isGrid) {
|
|
255
|
+
const grid = (n.el as GridCompHTMLElement)?._gridComp;
|
|
256
|
+
if (grid?.ref) grid.ref.destroy();
|
|
257
|
+
else grid?.ngOnDestroy();
|
|
258
|
+
} else {
|
|
259
|
+
const gridItem = (n.el as GridItemCompHTMLElement)?._gridItemComp;
|
|
260
|
+
if (gridItem?.ref) gridItem.ref.destroy();
|
|
261
|
+
else gridItem?.ngOnDestroy();
|
|
227
262
|
}
|
|
228
|
-
|
|
229
|
-
return gridItem?.el;
|
|
230
263
|
}
|
|
264
|
+
return;
|
|
231
265
|
}
|
|
232
266
|
|
|
233
267
|
/**
|
|
234
|
-
*
|
|
235
|
-
*
|
|
268
|
+
* called for each item in the grid - check if additional information needs to be saved.
|
|
269
|
+
* Note: since this is options minus gridstack private members using Utils.removeInternalForSave(),
|
|
270
|
+
* this typically doesn't need to do anything. However your custom Component @Input() are now supported
|
|
271
|
+
* using BaseWidget.serialize()
|
|
236
272
|
*/
|
|
237
273
|
export function gsSaveAdditionalNgInfo(n: NgGridStackNode, w: NgGridStackWidget) {
|
|
238
|
-
|
|
239
|
-
|
|
274
|
+
const gridItem = (n.el as GridItemCompHTMLElement)?._gridItemComp;
|
|
275
|
+
if (gridItem) {
|
|
276
|
+
const input = gridItem.childWidget?.serialize();
|
|
277
|
+
if (input) {
|
|
278
|
+
w.input = input;
|
|
279
|
+
}
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
// else check if Grid
|
|
283
|
+
const grid = (n.el as GridCompHTMLElement)?._gridComp;
|
|
284
|
+
if (grid) {
|
|
285
|
+
//.... save any custom data
|
|
286
|
+
}
|
|
240
287
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gridstack-item.component.ts 8.2.0
|
|
3
|
+
* Copyright (c) 2022 Alain Dumesny - see GridStack root license
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Base interface that all widgets need to implement in order for GridstackItemComponent to correctly save/load/delete/..
|
|
7
|
+
*/
|
|
8
|
+
import { Injectable } from '@angular/core';
|
|
9
|
+
import * as i0 from "@angular/core";
|
|
10
|
+
export class BaseWidget {
|
|
11
|
+
/**
|
|
12
|
+
* REDEFINE to return an object representing the data needed to re-create yourself, other than `selector` already handled.
|
|
13
|
+
* This should map directly to the @Input() fields of this objects on create, so a simple apply can be used on read
|
|
14
|
+
*/
|
|
15
|
+
serialize() { return; }
|
|
16
|
+
/**
|
|
17
|
+
* REDEFINE this if your widget needs to read from saved data and transform it to create itself - you do this for
|
|
18
|
+
* things that are not mapped directly into @Input() fields for example.
|
|
19
|
+
*/
|
|
20
|
+
deserialize(w) {
|
|
21
|
+
if (w.input)
|
|
22
|
+
Object.assign(this, w.input);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
BaseWidget.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BaseWidget, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
26
|
+
BaseWidget.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BaseWidget });
|
|
27
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BaseWidget, decorators: [{
|
|
28
|
+
type: Injectable
|
|
29
|
+
}] });
|
|
30
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFzZS13aWRnZXQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9hbmd1bGFyL3Byb2plY3RzL2xpYi9zcmMvbGliL2Jhc2Utd2lkZ2V0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUVIOztHQUVHO0FBRUgsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQzs7QUFJMUMsTUFBTSxPQUFnQixVQUFVO0lBQy9COzs7T0FHRztJQUNJLFNBQVMsS0FBZ0MsT0FBTyxDQUFDLENBQUM7SUFFekQ7OztPQUdHO0lBQ0ksV0FBVyxDQUFDLENBQW9CO1FBQ3JDLElBQUksQ0FBQyxDQUFDLEtBQUs7WUFBRyxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDN0MsQ0FBQzs7dUdBYm9CLFVBQVU7MkdBQVYsVUFBVTsyRkFBVixVQUFVO2tCQUQvQixVQUFVIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXHJcbiAqIGdyaWRzdGFjay1pdGVtLmNvbXBvbmVudC50cyA4LjIuMFxyXG4gKiBDb3B5cmlnaHQgKGMpIDIwMjIgQWxhaW4gRHVtZXNueSAtIHNlZSBHcmlkU3RhY2sgcm9vdCBsaWNlbnNlXHJcbiAqL1xyXG5cclxuLyoqXHJcbiAqIEJhc2UgaW50ZXJmYWNlIHRoYXQgYWxsIHdpZGdldHMgbmVlZCB0byBpbXBsZW1lbnQgaW4gb3JkZXIgZm9yIEdyaWRzdGFja0l0ZW1Db21wb25lbnQgdG8gY29ycmVjdGx5IHNhdmUvbG9hZC9kZWxldGUvLi5cclxuICovXHJcblxyXG5pbXBvcnQgeyBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7IE5nQ29tcElucHV0cywgTmdHcmlkU3RhY2tXaWRnZXQgfSBmcm9tICcuL2dyaWRzdGFjay5jb21wb25lbnQnO1xyXG5cclxuIEBJbmplY3RhYmxlKClcclxuIGV4cG9ydCBhYnN0cmFjdCBjbGFzcyBCYXNlV2lkZ2V0IHtcclxuICAvKipcclxuICAgKiBSRURFRklORSB0byByZXR1cm4gYW4gb2JqZWN0IHJlcHJlc2VudGluZyB0aGUgZGF0YSBuZWVkZWQgdG8gcmUtY3JlYXRlIHlvdXJzZWxmLCBvdGhlciB0aGFuIGBzZWxlY3RvcmAgYWxyZWFkeSBoYW5kbGVkLlxyXG4gICAqIFRoaXMgc2hvdWxkIG1hcCBkaXJlY3RseSB0byB0aGUgQElucHV0KCkgZmllbGRzIG9mIHRoaXMgb2JqZWN0cyBvbiBjcmVhdGUsIHNvIGEgc2ltcGxlIGFwcGx5IGNhbiBiZSB1c2VkIG9uIHJlYWRcclxuICAgKi9cclxuICBwdWJsaWMgc2VyaWFsaXplKCk6IE5nQ29tcElucHV0cyB8IHVuZGVmaW5lZCAgeyByZXR1cm47IH1cclxuXHJcbiAgLyoqXHJcbiAgICogUkVERUZJTkUgdGhpcyBpZiB5b3VyIHdpZGdldCBuZWVkcyB0byByZWFkIGZyb20gc2F2ZWQgZGF0YSBhbmQgdHJhbnNmb3JtIGl0IHRvIGNyZWF0ZSBpdHNlbGYgLSB5b3UgZG8gdGhpcyBmb3JcclxuICAgKiB0aGluZ3MgdGhhdCBhcmUgbm90IG1hcHBlZCBkaXJlY3RseSBpbnRvIEBJbnB1dCgpIGZpZWxkcyBmb3IgZXhhbXBsZS5cclxuICAgKi9cclxuICBwdWJsaWMgZGVzZXJpYWxpemUodzogTmdHcmlkU3RhY2tXaWRnZXQpICB7XHJcbiAgICBpZiAody5pbnB1dCkgIE9iamVjdC5hc3NpZ24odGhpcywgdy5pbnB1dCk7XHJcbiAgfVxyXG4gfVxyXG4iXX0=
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* gridstack-item.component.ts 8.
|
|
2
|
+
* gridstack-item.component.ts 8.2.0
|
|
3
3
|
* Copyright (c) 2022 Alain Dumesny - see GridStack root license
|
|
4
4
|
*/
|
|
5
5
|
import { Component, Input, ViewChild, ViewContainerRef } from '@angular/core';
|
|
@@ -34,6 +34,7 @@ export class GridstackItemComponent {
|
|
|
34
34
|
delete this._options;
|
|
35
35
|
}
|
|
36
36
|
ngOnDestroy() {
|
|
37
|
+
delete this.ref;
|
|
37
38
|
delete this.el._gridItemComp;
|
|
38
39
|
}
|
|
39
40
|
}
|
|
@@ -64,4 +65,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
|
|
|
64
65
|
}], options: [{
|
|
65
66
|
type: Input
|
|
66
67
|
}] } });
|
|
67
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
68
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3JpZHN0YWNrLWl0ZW0uY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vYW5ndWxhci9wcm9qZWN0cy9saWIvc3JjL2xpYi9ncmlkc3RhY2staXRlbS5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBRUgsT0FBTyxFQUFFLFNBQVMsRUFBYyxLQUFLLEVBQUUsU0FBUyxFQUFFLGdCQUFnQixFQUEyQixNQUFNLGVBQWUsQ0FBQzs7QUFTbkg7O0dBRUc7QUFpQkgsTUFBTSxPQUFPLHNCQUFzQjtJQW9DakMsWUFBNkIsVUFBMkM7UUFBM0MsZUFBVSxHQUFWLFVBQVUsQ0FBaUM7UUFDdEUsSUFBSSxDQUFDLEVBQUUsQ0FBQyxhQUFhLEdBQUcsSUFBSSxDQUFDO0lBQy9CLENBQUM7SUEzQkQsc0RBQXNEO0lBQ3RELElBQW9CLE9BQU8sQ0FBQyxHQUFrQjtRQUM1QyxJQUFJLElBQUksQ0FBQyxFQUFFLENBQUMsYUFBYSxFQUFFLElBQUksRUFBRTtZQUMvQixpQ0FBaUM7WUFDakMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxFQUFFLEdBQUcsQ0FBQyxDQUFDO1NBQ2pEO2FBQU07WUFDTCwyRkFBMkY7WUFDM0YsSUFBSSxDQUFDLFFBQVEsR0FBRyxFQUFDLEdBQUcsR0FBRyxFQUFFLEVBQUUsRUFBRSxJQUFJLENBQUMsRUFBRSxFQUFDLENBQUM7U0FDdkM7SUFDSCxDQUFDO0lBQ0Qsb0ZBQW9GO0lBQ3BGLElBQVcsT0FBTztRQUNoQixPQUFPLElBQUksQ0FBQyxFQUFFLENBQUMsYUFBYSxJQUFJLElBQUksQ0FBQyxRQUFRLElBQUksRUFBQyxFQUFFLEVBQUUsSUFBSSxDQUFDLEVBQUUsRUFBQyxDQUFDO0lBQ2pFLENBQUM7SUFJRCwyRUFBMkU7SUFDM0UsSUFBVyxFQUFFLEtBQThCLE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDO0lBRWxGLHNEQUFzRDtJQUMvQyxZQUFZO1FBQ2pCLE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQztJQUN2QixDQUFDO0lBTU0sV0FBVztRQUNoQixPQUFPLElBQUksQ0FBQyxHQUFHLENBQUM7UUFDaEIsT0FBTyxJQUFJLENBQUMsRUFBRSxDQUFDLGFBQWEsQ0FBQztJQUMvQixDQUFDOzttSEEzQ1Usc0JBQXNCO3VHQUF0QixzQkFBc0IseUtBR0QsZ0JBQWdCLDJDQWpCdEM7Ozs7Ozs7O1dBUUQ7MkZBTUUsc0JBQXNCO2tCQWhCbEMsU0FBUzsrQkFDRSxnQkFBZ0IsWUFDaEI7Ozs7Ozs7O1dBUUQ7aUdBUytELFNBQVM7c0JBQWhGLFNBQVM7dUJBQUMsV0FBVyxFQUFFLEVBQUUsSUFBSSxFQUFFLGdCQUFnQixFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUM7Z0JBUzNDLE9BQU87c0JBQTFCLEtBQUsiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIGdyaWRzdGFjay1pdGVtLmNvbXBvbmVudC50cyA4LjIuMFxuICogQ29weXJpZ2h0IChjKSAyMDIyIEFsYWluIER1bWVzbnkgLSBzZWUgR3JpZFN0YWNrIHJvb3QgbGljZW5zZVxuICovXG5cbmltcG9ydCB7IENvbXBvbmVudCwgRWxlbWVudFJlZiwgSW5wdXQsIFZpZXdDaGlsZCwgVmlld0NvbnRhaW5lclJlZiwgT25EZXN0cm95LCBDb21wb25lbnRSZWYgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEdyaWRJdGVtSFRNTEVsZW1lbnQsIEdyaWRTdGFja05vZGUgfSBmcm9tICdncmlkc3RhY2snO1xuaW1wb3J0IHsgQmFzZVdpZGdldCB9IGZyb20gJy4vYmFzZS13aWRnZXQnO1xuXG4vKiogc3RvcmUgZWxlbWVudCB0byBOZyBDbGFzcyBwb2ludGVyIGJhY2sgKi9cbmV4cG9ydCBpbnRlcmZhY2UgR3JpZEl0ZW1Db21wSFRNTEVsZW1lbnQgZXh0ZW5kcyBHcmlkSXRlbUhUTUxFbGVtZW50IHtcbiAgX2dyaWRJdGVtQ29tcD86IEdyaWRzdGFja0l0ZW1Db21wb25lbnQ7XG59XG5cbi8qKlxuICogSFRNTCBDb21wb25lbnQgV3JhcHBlciBmb3IgZ3JpZHN0YWNrIGl0ZW1zLCBpbiBjb21iaW5hdGlvbiB3aXRoIEdyaWRzdGFja0NvbXBvbmVudCBmb3IgcGFyZW50IGdyaWRcbiAqL1xuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnZ3JpZHN0YWNrLWl0ZW0nLFxuICB0ZW1wbGF0ZTogYFxuICAgIDxkaXYgY2xhc3M9XCJncmlkLXN0YWNrLWl0ZW0tY29udGVudFwiPlxuICAgICAgPCEtLSB3aGVyZSBkeW5hbWljIGl0ZW1zIGdvIGJhc2VkIG9uIGNvbXBvbmVudCB0eXBlcywgb3Igc3ViLWdyaWRzLCBldGMuLi4pIC0tPlxuICAgICAgPG5nLXRlbXBsYXRlICNjb250YWluZXI+PC9uZy10ZW1wbGF0ZT5cbiAgICAgIDwhLS0gYW55IHN0YXRpYyAoZGVmaW5lZCBpbiBkb20pIGNvbnRlbnQgZ29lcyBoZXJlIC0tPlxuICAgICAgPG5nLWNvbnRlbnQ+PC9uZy1jb250ZW50PlxuICAgICAgPCEtLSBmYWxsYmFjayBIVE1MIGNvbnRlbnQgZnJvbSBHcmlkU3RhY2tXaWRnZXQgY29udGVudCBmaWVsZCBpZiB1c2VkIGluc3RlYWQgLS0+XG4gICAgICB7e29wdGlvbnMuY29udGVudH19XG4gICAgPC9kaXY+YCxcbiAgc3R5bGVzOiBbYFxuICAgIDpob3N0IHsgZGlzcGxheTogYmxvY2s7IH1cbiAgYF0sXG4gIC8vIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLCAvLyBJRkYgeW91IHdhbnQgdG8gb3B0aW1pemUgYW5kIGNvbnRyb2wgd2hlbiBDaGFuZ2VEZXRlY3Rpb24gbmVlZHMgdG8gaGFwcGVuLi4uXG59KVxuZXhwb3J0IGNsYXNzIEdyaWRzdGFja0l0ZW1Db21wb25lbnQgaW1wbGVtZW50cyBPbkRlc3Ryb3kge1xuXG4gIC8qKiBjb250YWluZXIgdG8gYXBwZW5kIGl0ZW1zIGR5bmFtaWNhbGx5ICovXG4gIEBWaWV3Q2hpbGQoJ2NvbnRhaW5lcicsIHsgcmVhZDogVmlld0NvbnRhaW5lclJlZiwgc3RhdGljOiB0cnVlfSkgcHVibGljIGNvbnRhaW5lcj86IFZpZXdDb250YWluZXJSZWY7XG5cbiAgLyoqIENvbXBvbmVudFJlZiBvZiBvdXJzZWxmIC0gdXNlZCBieSBkeW5hbWljIG9iamVjdCB0byBjb3JyZWN0bHkgZ2V0IHJlbW92ZWQgKi9cbiAgcHVibGljIHJlZjogQ29tcG9uZW50UmVmPEdyaWRzdGFja0l0ZW1Db21wb25lbnQ+IHwgdW5kZWZpbmVkO1xuXG4gIC8qKiBjaGlsZCBjb21wb25lbnQgc28gd2UgY2FuIHNhdmUvcmVzdG9yZSBhZGRpdGlvbmFsIGRhdGEgdG8gYmUgc2F2ZWQgYWxvbmcgKi9cbiAgcHVibGljIGNoaWxkV2lkZ2V0OiBCYXNlV2lkZ2V0IHwgdW5kZWZpbmVkO1xuXG4gIC8qKiBsaXN0IG9mIG9wdGlvbnMgZm9yIGNyZWF0aW5nL3VwZGF0aW5nIHRoaXMgaXRlbSAqL1xuICBASW5wdXQoKSBwdWJsaWMgc2V0IG9wdGlvbnModmFsOiBHcmlkU3RhY2tOb2RlKSB7XG4gICAgaWYgKHRoaXMuZWwuZ3JpZHN0YWNrTm9kZT8uZ3JpZCkge1xuICAgICAgLy8gYWxyZWFkeSBidWlsdCwgZG8gYW4gdXBkYXRlLi4uXG4gICAgICB0aGlzLmVsLmdyaWRzdGFja05vZGUuZ3JpZC51cGRhdGUodGhpcy5lbCwgdmFsKTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gc3RvcmUgb3VyIGN1c3RvbSBlbGVtZW50IGluIG9wdGlvbnMgc28gd2UgY2FuIHVwZGF0ZSBpdCBhbmQgbm90IHJlLWNyZWF0ZSBhIGdlbmVyaWMgZGl2IVxuICAgICAgdGhpcy5fb3B0aW9ucyA9IHsuLi52YWwsIGVsOiB0aGlzLmVsfTtcbiAgICB9XG4gIH1cbiAgLyoqIHJldHVybiB0aGUgbGF0ZXN0IGdyaWQgb3B0aW9ucyAoZnJvbSBHUyBvbmNlIGJ1aWx0LCBvdGhlcndpc2UgaW5pdGlhbCB2YWx1ZXMpICovXG4gIHB1YmxpYyBnZXQgb3B0aW9ucygpOiBHcmlkU3RhY2tOb2RlIHtcbiAgICByZXR1cm4gdGhpcy5lbC5ncmlkc3RhY2tOb2RlIHx8IHRoaXMuX29wdGlvbnMgfHwge2VsOiB0aGlzLmVsfTtcbiAgfVxuXG4gIHByaXZhdGUgX29wdGlvbnM/OiBHcmlkU3RhY2tOb2RlO1xuXG4gIC8qKiByZXR1cm4gdGhlIG5hdGl2ZSBlbGVtZW50IHRoYXQgY29udGFpbnMgZ3JpZCBzcGVjaWZpYyBmaWVsZHMgYXMgd2VsbCAqL1xuICBwdWJsaWMgZ2V0IGVsKCk6IEdyaWRJdGVtQ29tcEhUTUxFbGVtZW50IHsgcmV0dXJuIHRoaXMuZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50OyB9XG5cbiAgLyoqIGNsZWFycyB0aGUgaW5pdGlhbCBvcHRpb25zIG5vdyB0aGF0IHdlJ3ZlIGJ1aWx0ICovXG4gIHB1YmxpYyBjbGVhck9wdGlvbnMoKSB7XG4gICAgZGVsZXRlIHRoaXMuX29wdGlvbnM7XG4gIH1cblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJlYWRvbmx5IGVsZW1lbnRSZWY6IEVsZW1lbnRSZWY8R3JpZEl0ZW1IVE1MRWxlbWVudD4pIHtcbiAgICB0aGlzLmVsLl9ncmlkSXRlbUNvbXAgPSB0aGlzO1xuICB9XG5cbiAgcHVibGljIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgIGRlbGV0ZSB0aGlzLnJlZjtcbiAgICBkZWxldGUgdGhpcy5lbC5fZ3JpZEl0ZW1Db21wO1xuICB9XG59XG4iXX0=
|