@ngneat/helipopper 9.2.0 → 10.0.0-alpha.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/README.md +52 -55
- package/esm2022/lib/coercion.mjs +10 -0
- package/esm2022/lib/intersection-observer.mjs +9 -0
- package/esm2022/lib/providers.mjs +2 -2
- package/esm2022/lib/tippy.directive.mjs +249 -269
- package/esm2022/lib/tippy.factory.mjs +43 -0
- package/esm2022/lib/tippy.service.mjs +23 -21
- package/esm2022/lib/tippy.types.mjs +2 -7
- package/esm2022/lib/utils.mjs +3 -2
- package/fesm2022/ngneat-helipopper.mjs +325 -294
- package/fesm2022/ngneat-helipopper.mjs.map +1 -1
- package/lib/coercion.d.ts +9 -0
- package/lib/intersection-observer.d.ts +1 -0
- package/lib/providers.d.ts +1 -1
- package/lib/tippy.directive.d.ts +43 -51
- package/lib/tippy.factory.d.ts +16 -0
- package/lib/tippy.service.d.ts +3 -1
- package/lib/tippy.types.d.ts +15 -8
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -38,8 +38,12 @@ If you're using v1 and don't want to migrate, you can find it [here](https://git
|
|
|
38
38
|
|
|
39
39
|
### Installation
|
|
40
40
|
|
|
41
|
-
```
|
|
42
|
-
npm
|
|
41
|
+
```sh
|
|
42
|
+
$ npm i @ngneat/helipopper
|
|
43
|
+
# Or if you're using yarn
|
|
44
|
+
$ yarn add @ngneat/helipopper
|
|
45
|
+
# Or if you're using pnpm
|
|
46
|
+
$ pnpm i @ngneat/helipopper
|
|
43
47
|
```
|
|
44
48
|
|
|
45
49
|
Configure it as shown below:
|
|
@@ -50,22 +54,33 @@ import { provideTippyConfig, tooltipVariation, popperVariation } from '@ngneat/h
|
|
|
50
54
|
bootstrapApplication(AppComponent, {
|
|
51
55
|
providers: [
|
|
52
56
|
provideTippyConfig({
|
|
57
|
+
loader: () => import('tippy.js'),
|
|
53
58
|
defaultVariation: 'tooltip',
|
|
54
59
|
variations: {
|
|
55
60
|
tooltip: tooltipVariation,
|
|
56
61
|
popper: popperVariation,
|
|
57
|
-
}
|
|
58
|
-
})
|
|
59
|
-
]
|
|
60
|
-
})
|
|
62
|
+
},
|
|
63
|
+
}),
|
|
64
|
+
],
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Please note that the `loader` property is required, as it specifies how Tippy is loaded - either synchronously or asynchronously. When dynamic import is used, the library will load only when the first Tippy directive is rendered. If we want it to load synchronously, we use the following:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import tippy from 'tippy.js';
|
|
72
|
+
|
|
73
|
+
provideTippyConfig({
|
|
74
|
+
loader: () => tippy,
|
|
75
|
+
});
|
|
61
76
|
```
|
|
62
77
|
|
|
63
78
|
Add the styles you want to `styles.scss`:
|
|
64
79
|
|
|
65
80
|
```scss
|
|
66
|
-
@import '
|
|
67
|
-
@import '
|
|
68
|
-
@import '
|
|
81
|
+
@import 'tippy.js/dist/tippy.css';
|
|
82
|
+
@import 'tippy.js/themes/light.css';
|
|
83
|
+
@import 'tippy.js/animations/scale.css';
|
|
69
84
|
```
|
|
70
85
|
|
|
71
86
|
You have the freedom to [customize](https://atomiks.github.io/tippyjs/v6/themes/) it if you need to.
|
|
@@ -73,9 +88,7 @@ You have the freedom to [customize](https://atomiks.github.io/tippyjs/v6/themes/
|
|
|
73
88
|
Import the standalone `TippyDirective` and use it in your templates:
|
|
74
89
|
|
|
75
90
|
```html
|
|
76
|
-
<button tp="Helpful Message">
|
|
77
|
-
I have a tooltip
|
|
78
|
-
</button>
|
|
91
|
+
<button tp="Helpful Message">I have a tooltip</button>
|
|
79
92
|
```
|
|
80
93
|
|
|
81
94
|
The library exposes default variations for `tooltip` and `popper`. You can use them, extend them, or pass your own
|
|
@@ -88,16 +101,14 @@ export const tooltipVariation = {
|
|
|
88
101
|
arrow: false,
|
|
89
102
|
animation: 'scale',
|
|
90
103
|
trigger: 'mouseenter',
|
|
91
|
-
offset: [0, 5]
|
|
104
|
+
offset: [0, 5],
|
|
92
105
|
};
|
|
93
106
|
```
|
|
94
107
|
|
|
95
108
|
### Use `TemplateRef` as content
|
|
96
109
|
|
|
97
110
|
```html
|
|
98
|
-
<button [tp]="tpl" tpVariation="popper">
|
|
99
|
-
Click Me
|
|
100
|
-
</button>
|
|
111
|
+
<button [tp]="tpl" tpVariation="popper">Click Me</button>
|
|
101
112
|
|
|
102
113
|
<ng-template #tpl let-hide>
|
|
103
114
|
<h6>Popover title</h6>
|
|
@@ -117,9 +128,7 @@ class MyComponent {
|
|
|
117
128
|
```
|
|
118
129
|
|
|
119
130
|
```html
|
|
120
|
-
<button [tp]="MyComponent">
|
|
121
|
-
Click Me
|
|
122
|
-
</button>
|
|
131
|
+
<button [tp]="MyComponent">Click Me</button>
|
|
123
132
|
```
|
|
124
133
|
|
|
125
134
|
### Text Overflow
|
|
@@ -128,9 +137,7 @@ You can pass the `onlyTextOverflow` input to show the tooltip only when the host
|
|
|
128
137
|
|
|
129
138
|
```html
|
|
130
139
|
<div style="max-width: 100px;" class="overflow-hidden flex">
|
|
131
|
-
<p class="ellipsis" [tp]="text" tpPlacement="right" [tpOnlyTextOverflow]="true">
|
|
132
|
-
{{ text }}
|
|
133
|
-
</p>
|
|
140
|
+
<p class="ellipsis" [tp]="text" tpPlacement="right" [tpOnlyTextOverflow]="true">{{ text }}</p>
|
|
134
141
|
</div>
|
|
135
142
|
```
|
|
136
143
|
|
|
@@ -140,7 +147,14 @@ You might have cases where the host has a static width and the content is dynami
|
|
|
140
147
|
|
|
141
148
|
```html
|
|
142
149
|
<div style="max-width: 100px;" class="overflow-hidden flex">
|
|
143
|
-
<p
|
|
150
|
+
<p
|
|
151
|
+
style="width: 100px"
|
|
152
|
+
class="ellipsis"
|
|
153
|
+
[tp]="dynamicText"
|
|
154
|
+
tpPlacement="right"
|
|
155
|
+
[tpOnlyTextOverflow]="true"
|
|
156
|
+
tpStaticWidthHost
|
|
157
|
+
>
|
|
144
158
|
{{ dynamicText }}
|
|
145
159
|
</p>
|
|
146
160
|
</div>
|
|
@@ -153,34 +167,25 @@ Note: when using `tpStaticWidthHost` you can't use `tpUseTextContent`, you need
|
|
|
153
167
|
You can instruct tippy to use the element textContent as the tooltip content:
|
|
154
168
|
|
|
155
169
|
```html
|
|
156
|
-
<p tp tpUseTextContent>
|
|
157
|
-
{{ text }}
|
|
158
|
-
</p>
|
|
170
|
+
<p tp tpUseTextContent>{{ text }}</p>
|
|
159
171
|
```
|
|
160
172
|
|
|
161
|
-
|
|
162
173
|
### Lazy
|
|
163
174
|
|
|
164
175
|
You can pass the `tpIsLazy` input when you want to defer the creation of tippy only when the element is in the view:
|
|
165
176
|
|
|
166
177
|
```html
|
|
167
|
-
<div *ngFor="let item of items"
|
|
168
|
-
[tp]="item.label"
|
|
169
|
-
[tpIsLazy]="true">{{ item.label }}
|
|
170
|
-
</div>
|
|
178
|
+
<div *ngFor="let item of items" [tp]="item.label" [tpIsLazy]="true">{{ item.label }}</div>
|
|
171
179
|
```
|
|
172
180
|
|
|
173
181
|
Note that it's using [`IntersectionObserver`](https://caniuse.com/intersectionobserver) api.
|
|
174
182
|
|
|
175
183
|
### Context Menu
|
|
184
|
+
|
|
176
185
|
First, define the `contextMenu` variation:
|
|
186
|
+
|
|
177
187
|
```ts
|
|
178
|
-
import {
|
|
179
|
-
popperVariation,
|
|
180
|
-
tooltipVariation,
|
|
181
|
-
provideTippyConfig,
|
|
182
|
-
withContextMenuVariation
|
|
183
|
-
} from '@ngneat/helipopper';
|
|
188
|
+
import { popperVariation, tooltipVariation, provideTippyConfig, withContextMenuVariation } from '@ngneat/helipopper';
|
|
184
189
|
|
|
185
190
|
bootstrapApplication(AppComponent, {
|
|
186
191
|
providers: [
|
|
@@ -190,10 +195,10 @@ bootstrapApplication(AppComponent, {
|
|
|
190
195
|
tooltip: tooltipVariation,
|
|
191
196
|
popper: popperVariation,
|
|
192
197
|
contextMenu: withContextMenuVariation(popperVariation),
|
|
193
|
-
}
|
|
194
|
-
})
|
|
195
|
-
]
|
|
196
|
-
})
|
|
198
|
+
},
|
|
199
|
+
}),
|
|
200
|
+
],
|
|
201
|
+
});
|
|
197
202
|
```
|
|
198
203
|
|
|
199
204
|
Now you can use it in your template:
|
|
@@ -207,21 +212,14 @@ Now you can use it in your template:
|
|
|
207
212
|
</ng-template>
|
|
208
213
|
|
|
209
214
|
<ul>
|
|
210
|
-
<li *ngFor="let item of list"
|
|
211
|
-
[tp]="contextMenu"
|
|
212
|
-
[tpData]="item"
|
|
213
|
-
tpVariation="contextMenu">
|
|
214
|
-
{{ item.label }}
|
|
215
|
-
</li>
|
|
215
|
+
<li *ngFor="let item of list" [tp]="contextMenu" [tpData]="item" tpVariation="contextMenu">{{ item.label }}</li>
|
|
216
216
|
</ul>
|
|
217
217
|
```
|
|
218
218
|
|
|
219
219
|
### Manual Trigger
|
|
220
220
|
|
|
221
221
|
```html
|
|
222
|
-
<div tp="Helpful Message" tpTrigger="manual" #tooltip="tippy">
|
|
223
|
-
Click Open to see me
|
|
224
|
-
</div>
|
|
222
|
+
<div tp="Helpful Message" tpTrigger="manual" #tooltip="tippy">Click Open to see me</div>
|
|
225
223
|
|
|
226
224
|
<button (click)="tooltip.show()">Open</button>
|
|
227
225
|
<button (click)="tooltip.hide()">Close</button>
|
|
@@ -232,9 +230,7 @@ Now you can use it in your template:
|
|
|
232
230
|
Use isVisible to trigger show and hide. Set trigger to manual.
|
|
233
231
|
|
|
234
232
|
```html
|
|
235
|
-
<div tp="Helpful Message" tpTrigger="manual" [tpIsVisible]="visibility">
|
|
236
|
-
Click Open to see me
|
|
237
|
-
</div>
|
|
233
|
+
<div tp="Helpful Message" tpTrigger="manual" [tpIsVisible]="visibility">Click Open to see me</div>
|
|
238
234
|
|
|
239
235
|
<button (click)="visibility = true">Open</button>
|
|
240
236
|
<button (click)="visibility = false">Close</button>
|
|
@@ -286,7 +282,8 @@ tpVisible = new EventEmitter<boolean>();
|
|
|
286
282
|
```
|
|
287
283
|
|
|
288
284
|
### Global Config
|
|
289
|
-
|
|
285
|
+
|
|
286
|
+
- You can pass any `tippy` option at global config level.
|
|
290
287
|
- `beforeRender` - Hook that'll be called before rendering the tooltip content ( applies only for string )
|
|
291
288
|
|
|
292
289
|
### Create `tippy` Programmatically
|
|
@@ -300,7 +297,7 @@ class Component {
|
|
|
300
297
|
private tippyService = inject(TippyService);
|
|
301
298
|
|
|
302
299
|
show() {
|
|
303
|
-
if(!this.tippy) {
|
|
300
|
+
if (!this.tippy) {
|
|
304
301
|
this.tippy = this.tippyService.create(this.inputName, 'this field is required');
|
|
305
302
|
}
|
|
306
303
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { booleanAttribute as originalBooleanAttribute } from '@angular/core';
|
|
2
|
+
/**
|
|
3
|
+
* Transforms a value (typically a string) to a boolean.
|
|
4
|
+
* Intended to be used as a transform function of an input.
|
|
5
|
+
*
|
|
6
|
+
* @see https://material.angular.io/cdk/coercion/overview
|
|
7
|
+
*/
|
|
8
|
+
const coerceBooleanAttribute = originalBooleanAttribute;
|
|
9
|
+
export { coerceBooleanAttribute };
|
|
10
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29lcmNpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9uZ25lYXQvaGVsaXBvcHBlci9zcmMvbGliL2NvZXJjaW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxnQkFBZ0IsSUFBSSx3QkFBd0IsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUk3RTs7Ozs7R0FLRztBQUNILE1BQU0sc0JBQXNCLEdBQXFDLHdCQUF3QixDQUFDO0FBRTFGLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgYm9vbGVhbkF0dHJpYnV0ZSBhcyBvcmlnaW5hbEJvb2xlYW5BdHRyaWJ1dGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcblxudHlwZSBCb29sZWFuSW5wdXQgPSBib29sZWFuIHwgYCR7Ym9vbGVhbn1gIHwgJycgfCBudWxsIHwgdW5kZWZpbmVkO1xuXG4vKipcbiAqIFRyYW5zZm9ybXMgYSB2YWx1ZSAodHlwaWNhbGx5IGEgc3RyaW5nKSB0byBhIGJvb2xlYW4uXG4gKiBJbnRlbmRlZCB0byBiZSB1c2VkIGFzIGEgdHJhbnNmb3JtIGZ1bmN0aW9uIG9mIGFuIGlucHV0LlxuICpcbiAqIEBzZWUgaHR0cHM6Ly9tYXRlcmlhbC5hbmd1bGFyLmlvL2Nkay9jb2VyY2lvbi9vdmVydmlld1xuICovXG5jb25zdCBjb2VyY2VCb29sZWFuQXR0cmlidXRlOiAodmFsdWU6IEJvb2xlYW5JbnB1dCkgPT4gYm9vbGVhbiA9IG9yaWdpbmFsQm9vbGVhbkF0dHJpYnV0ZTtcblxuZXhwb3J0IHsgY29lcmNlQm9vbGVhbkF0dHJpYnV0ZSB9O1xuIl19
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Let's retrieve the native `IntersectionObserver` implementation hidden by
|
|
2
|
+
// `__zone_symbol__IntersectionObserver`. This would be the unpatched version of
|
|
3
|
+
// the observer present in zone.js environments.
|
|
4
|
+
// Otherwise, if the user is using zoneless change detection (and zone.js is not included),
|
|
5
|
+
// we fall back to the native implementation. Accessing the native implementation
|
|
6
|
+
// allows us to remove `runOutsideAngular` calls and reduce indentation,
|
|
7
|
+
// making the code a bit more readable.
|
|
8
|
+
export const IntersectionObserver = globalThis['__zone_symbol__IntersectionObserver'] || globalThis.IntersectionObserver;
|
|
9
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJzZWN0aW9uLW9ic2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvbmduZWF0L2hlbGlwb3BwZXIvc3JjL2xpYi9pbnRlcnNlY3Rpb24tb2JzZXJ2ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsNEVBQTRFO0FBQzVFLGdGQUFnRjtBQUNoRixnREFBZ0Q7QUFDaEQsMkZBQTJGO0FBQzNGLGlGQUFpRjtBQUNqRix3RUFBd0U7QUFDeEUsdUNBQXVDO0FBQ3ZDLE1BQU0sQ0FBQyxNQUFNLG9CQUFvQixHQUMvQixVQUFVLENBQUMscUNBQXFDLENBQUMsSUFBSSxVQUFVLENBQUMsb0JBQW9CLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBMZXQncyByZXRyaWV2ZSB0aGUgbmF0aXZlIGBJbnRlcnNlY3Rpb25PYnNlcnZlcmAgaW1wbGVtZW50YXRpb24gaGlkZGVuIGJ5XG4vLyBgX196b25lX3N5bWJvbF9fSW50ZXJzZWN0aW9uT2JzZXJ2ZXJgLiBUaGlzIHdvdWxkIGJlIHRoZSB1bnBhdGNoZWQgdmVyc2lvbiBvZlxuLy8gdGhlIG9ic2VydmVyIHByZXNlbnQgaW4gem9uZS5qcyBlbnZpcm9ubWVudHMuXG4vLyBPdGhlcndpc2UsIGlmIHRoZSB1c2VyIGlzIHVzaW5nIHpvbmVsZXNzIGNoYW5nZSBkZXRlY3Rpb24gKGFuZCB6b25lLmpzIGlzIG5vdCBpbmNsdWRlZCksXG4vLyB3ZSBmYWxsIGJhY2sgdG8gdGhlIG5hdGl2ZSBpbXBsZW1lbnRhdGlvbi4gQWNjZXNzaW5nIHRoZSBuYXRpdmUgaW1wbGVtZW50YXRpb25cbi8vIGFsbG93cyB1cyB0byByZW1vdmUgYHJ1bk91dHNpZGVBbmd1bGFyYCBjYWxscyBhbmQgcmVkdWNlIGluZGVudGF0aW9uLFxuLy8gbWFraW5nIHRoZSBjb2RlIGEgYml0IG1vcmUgcmVhZGFibGUuXG5leHBvcnQgY29uc3QgSW50ZXJzZWN0aW9uT2JzZXJ2ZXI6IHR5cGVvZiBnbG9iYWxUaGlzLkludGVyc2VjdGlvbk9ic2VydmVyID1cbiAgZ2xvYmFsVGhpc1snX196b25lX3N5bWJvbF9fSW50ZXJzZWN0aW9uT2JzZXJ2ZXInXSB8fCBnbG9iYWxUaGlzLkludGVyc2VjdGlvbk9ic2VydmVyO1xuIl19
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { inject, makeEnvironmentProviders } from '@angular/core';
|
|
2
2
|
import { TIPPY_CONFIG, TIPPY_REF } from './tippy.types';
|
|
3
|
-
export function provideTippyConfig(config
|
|
3
|
+
export function provideTippyConfig(config) {
|
|
4
4
|
return makeEnvironmentProviders([{ provide: TIPPY_CONFIG, useValue: config }]);
|
|
5
5
|
}
|
|
6
6
|
export function injectTippyRef() {
|
|
@@ -10,4 +10,4 @@ export function injectTippyRef() {
|
|
|
10
10
|
}
|
|
11
11
|
throw new Error('tp is not provided in the current context or on one of its ancestors');
|
|
12
12
|
}
|
|
13
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
13
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvdmlkZXJzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvbmduZWF0L2hlbGlwb3BwZXIvc3JjL2xpYi9wcm92aWRlcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE1BQU0sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUNqRSxPQUFPLEVBQUUsWUFBWSxFQUFFLFNBQVMsRUFBOEIsTUFBTSxlQUFlLENBQUM7QUFFcEYsTUFBTSxVQUFVLGtCQUFrQixDQUFDLE1BQW1CO0lBQ3BELE9BQU8sd0JBQXdCLENBQUMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxZQUFZLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUMsQ0FBQztBQUNqRixDQUFDO0FBRUQsTUFBTSxVQUFVLGNBQWM7SUFDNUIsTUFBTSxRQUFRLEdBQUcsTUFBTSxDQUFDLFNBQVMsRUFBRSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBRXZELElBQUksUUFBUSxFQUFFLENBQUM7UUFDYixPQUFPLFFBQVEsQ0FBQztJQUNsQixDQUFDO0lBRUQsTUFBTSxJQUFJLEtBQUssQ0FBQyxzRUFBc0UsQ0FBQyxDQUFDO0FBQzFGLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBpbmplY3QsIG1ha2VFbnZpcm9ubWVudFByb3ZpZGVycyB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgVElQUFlfQ09ORklHLCBUSVBQWV9SRUYsIFRpcHB5Q29uZmlnLCBUaXBweUluc3RhbmNlIH0gZnJvbSAnLi90aXBweS50eXBlcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBwcm92aWRlVGlwcHlDb25maWcoY29uZmlnOiBUaXBweUNvbmZpZykge1xuICByZXR1cm4gbWFrZUVudmlyb25tZW50UHJvdmlkZXJzKFt7IHByb3ZpZGU6IFRJUFBZX0NPTkZJRywgdXNlVmFsdWU6IGNvbmZpZyB9XSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpbmplY3RUaXBweVJlZigpOiBUaXBweUluc3RhbmNlIHtcbiAgY29uc3QgaW5zdGFuY2UgPSBpbmplY3QoVElQUFlfUkVGLCB7IG9wdGlvbmFsOiB0cnVlIH0pO1xuXG4gIGlmIChpbnN0YW5jZSkge1xuICAgIHJldHVybiBpbnN0YW5jZTtcbiAgfVxuXG4gIHRocm93IG5ldyBFcnJvcigndHAgaXMgbm90IHByb3ZpZGVkIGluIHRoZSBjdXJyZW50IGNvbnRleHQgb3Igb24gb25lIG9mIGl0cyBhbmNlc3RvcnMnKTtcbn1cbiJdfQ==
|