@notabene/javascript-sdk 1.34.0 → 2.0.0-RC

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.
Files changed (44) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +500 -259
  3. package/dist/{es/index.js → js/notabene.js} +1 -1
  4. package/dist/notabene.cjs +1 -0
  5. package/dist/notabene.d.ts +1565 -0
  6. package/dist/notabene.js +343 -0
  7. package/dist/tsdoc-metadata.json +11 -0
  8. package/package.json +50 -64
  9. package/src/__tests__/notabene.test.ts +270 -0
  10. package/src/components/EmbeddedComponent.ts +237 -0
  11. package/src/components/__tests__/EmbeddedComponent.test.ts +530 -0
  12. package/src/ivms/types.ts +232 -152
  13. package/src/locales.ts +47 -0
  14. package/src/notabene.ts +292 -288
  15. package/src/types.ts +1068 -78
  16. package/src/utils/MessageEventManager.ts +115 -0
  17. package/src/utils/__tests__/MessageEventManager.test.ts +119 -0
  18. package/src/utils/__tests__/urls.test.ts +112 -0
  19. package/src/utils/arbitraries.ts +244 -0
  20. package/src/utils/caip.ts +16 -0
  21. package/src/utils/urls.ts +40 -0
  22. package/.editorconfig +0 -10
  23. package/.envrc.template +0 -1
  24. package/.eslintignore +0 -3
  25. package/.eslintrc.js +0 -13
  26. package/.gitlab-ci.yml +0 -74
  27. package/.husky/commit-msg +0 -4
  28. package/.husky/pre-commit +0 -4
  29. package/.husky/pre-push +0 -4
  30. package/.nvmrc +0 -1
  31. package/.prettierrc +0 -4
  32. package/.releaserc.json +0 -16
  33. package/.tool-versions +0 -3
  34. package/.vscode/extensions.json +0 -7
  35. package/.vscode/settings.json +0 -11
  36. package/CODEOWNERS +0 -1
  37. package/dist/lib/index.js +0 -376
  38. package/jest.config.js +0 -5
  39. package/public/index.html +0 -93
  40. package/src/lib/index.ts +0 -2
  41. package/src/module/index.ts +0 -10
  42. package/src/zoidComponentProps.ts +0 -81
  43. package/tsconfig.json +0 -18
  44. /package/{public/js/.gitkeep → src/arbitraries.ts} +0 -0
package/.gitlab-ci.yml DELETED
@@ -1,74 +0,0 @@
1
- image: registry.gitlab.com/notabene/devops/build-node-16-image:latest
2
-
3
- variables:
4
- NPM_TOKEN: ${CI_JOB_TOKEN}
5
-
6
- stages:
7
- - install
8
- - lint
9
- - test
10
- - build
11
- - release
12
-
13
- install:
14
- stage: install
15
- artifacts:
16
- paths:
17
- - .yarn
18
- - .pnp.cjs
19
- - .pnp.loader.mjs
20
- script:
21
- - yarn
22
-
23
- lint:
24
- stage: lint
25
- needs:
26
- - install
27
- script:
28
- - yarn lint
29
-
30
- test:
31
- stage: test
32
- needs:
33
- - install
34
- script:
35
- - yarn test
36
-
37
- build:
38
- stage: build
39
- needs:
40
- - install
41
- script:
42
- - yarn build
43
- artifacts:
44
- paths:
45
- - dist
46
-
47
- publish-gitlab:
48
- stage: release
49
- needs:
50
- - install
51
- - build
52
- variables:
53
- GITLAB_USER_NAME: 'gitlab-ci'
54
- GITLAB_USER_EMAIL: 'gitlab-ci@example.com'
55
- before_script:
56
- - git config --global user.email "$GITLAB_USER_EMAIL"
57
- - git config --global user.name "$GITLAB_USER_NAME"
58
- - |
59
- {
60
- echo "@${CI_PROJECT_ROOT_NAMESPACE}:registry=${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/npm/"
61
- echo "${CI_API_V4_URL#https?}/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=\${CI_JOB_TOKEN}"
62
- } | tee -a .npmrc
63
- script:
64
- - yarn semantic-release
65
- only:
66
- - master
67
-
68
- publish-npm:
69
- stage: release
70
- when: manual
71
- script:
72
- - yarn npm publish
73
- only:
74
- - tags
package/.husky/commit-msg DELETED
@@ -1,4 +0,0 @@
1
- #!/bin/sh
2
- . "$(dirname "$0")/_/husky.sh"
3
-
4
- yarn commitlint -e "${1}"
package/.husky/pre-commit DELETED
@@ -1,4 +0,0 @@
1
- #!/bin/sh
2
- . "$(dirname "$0")/_/husky.sh"
3
-
4
- yarn lint-staged
package/.husky/pre-push DELETED
@@ -1,4 +0,0 @@
1
- #!/bin/sh
2
- . "$(dirname "$0")/_/husky.sh"
3
-
4
- yarn test
package/.nvmrc DELETED
@@ -1 +0,0 @@
1
- v16
package/.prettierrc DELETED
@@ -1,4 +0,0 @@
1
- {
2
- "semi": true,
3
- "singleQuote": true
4
- }
package/.releaserc.json DELETED
@@ -1,16 +0,0 @@
1
- {
2
- "branches": ["master"],
3
- "plugins": [
4
- "@semantic-release/commit-analyzer",
5
- "@semantic-release/release-notes-generator",
6
- "@semantic-release/gitlab",
7
- "@semantic-release/npm",
8
- [
9
- "@semantic-release/git",
10
- {
11
- "assets": ["package.json"],
12
- "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
13
- }
14
- ]
15
- ]
16
- }
package/.tool-versions DELETED
@@ -1,3 +0,0 @@
1
- nodejs 16.16.0
2
- yarn 1.22.19
3
- direnv 2.32.1
@@ -1,7 +0,0 @@
1
- {
2
- "recommendations": [
3
- "arcanis.vscode-zipfs",
4
- "dbaeumer.vscode-eslint",
5
- "esbenp.prettier-vscode"
6
- ]
7
- }
@@ -1,11 +0,0 @@
1
- {
2
- "search.exclude": {
3
- "**/.yarn": true,
4
- "**/.pnp.*": true
5
- },
6
- "eslint.nodePath": ".yarn/sdks",
7
- "prettier.prettierPath": ".yarn/sdks/prettier/index.js",
8
- "typescript.tsdk": ".yarn/sdks/typescript/lib",
9
- "typescript.enablePromptUseWorkspaceTsdk": true,
10
- "editor.defaultFormatter": "esbenp.prettier-vscode"
11
- }
package/CODEOWNERS DELETED
@@ -1 +0,0 @@
1
- * @ajunge @brunosimplicio @dw1284 @richardcrosby @david1250 @marieh_mai @timothy4915912 @graham9961840
package/dist/lib/index.js DELETED
@@ -1,376 +0,0 @@
1
- var $4mxld$jsxpragmatic = require("jsx-pragmatic");
2
- var $4mxld$zoid = require("zoid");
3
-
4
- function $parcel$export(e, n, v, s) {
5
- Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
6
- }
7
- function $parcel$exportWildcard(dest, source) {
8
- Object.keys(source).forEach(function(key) {
9
- if (key === 'default' || key === '__esModule' || dest.hasOwnProperty(key)) {
10
- return;
11
- }
12
-
13
- Object.defineProperty(dest, key, {
14
- enumerable: true,
15
- get: function get() {
16
- return source[key];
17
- }
18
- });
19
- });
20
-
21
- return dest;
22
- }
23
- function $parcel$interopDefault(a) {
24
- return a && a.__esModule ? a.default : a;
25
- }
26
-
27
- $parcel$export(module.exports, "Notabene", () => $fef5d623a19b12b5$export$2e2bcd8739ae039);
28
-
29
-
30
- var $48505191f6b4c91c$exports = {};
31
-
32
- $parcel$export($48505191f6b4c91c$exports, "WidgetType", () => $48505191f6b4c91c$export$95cdd842edb044c5);
33
- let $48505191f6b4c91c$export$95cdd842edb044c5;
34
- (function($48505191f6b4c91c$export$95cdd842edb044c5) {
35
- $48505191f6b4c91c$export$95cdd842edb044c5["WITHDRAWAL"] = "WITHDRAWAL";
36
- $48505191f6b4c91c$export$95cdd842edb044c5["POST_DEPOSIT"] = "POST_DEPOSIT";
37
- })($48505191f6b4c91c$export$95cdd842edb044c5 || ($48505191f6b4c91c$export$95cdd842edb044c5 = {}));
38
-
39
-
40
- const $fd25ee12f453fb93$var$generalProps = {
41
- vaspDID: {
42
- type: 'string',
43
- required: true
44
- },
45
- transaction: {
46
- type: 'object',
47
- required: false
48
- },
49
- error: {
50
- type: 'object',
51
- required: false
52
- },
53
- onError: {
54
- type: 'function',
55
- required: true
56
- },
57
- storeTransaction: {
58
- type: 'function',
59
- required: true
60
- },
61
- theme: {
62
- type: 'object',
63
- required: false
64
- },
65
- dictionary: {
66
- type: 'object',
67
- required: false
68
- },
69
- transactionTypeAllowed: {
70
- type: 'string',
71
- required: false
72
- },
73
- nonCustodialDeclarationType: {
74
- type: 'string',
75
- required: false
76
- },
77
- beneficiaryDetails: {
78
- type: 'array',
79
- required: false
80
- },
81
- fieldsProps: {
82
- type: 'object',
83
- required: false
84
- },
85
- fallbacks: {
86
- type: 'array',
87
- required: false
88
- },
89
- optInFeatures: {
90
- type: 'array',
91
- required: false
92
- }
93
- };
94
- const $fd25ee12f453fb93$export$1aafbfc7f973e450 = {
95
- ...$fd25ee12f453fb93$var$generalProps,
96
- onRequireAdditionalInformation: {
97
- type: 'function',
98
- required: true
99
- },
100
- onValidStateChange: {
101
- type: 'function',
102
- required: true
103
- }
104
- };
105
- const $fd25ee12f453fb93$export$73ab9d0d47d71b0c = {
106
- ...$fd25ee12f453fb93$var$generalProps,
107
- close: {
108
- type: 'function'
109
- },
110
- popupOpen: {
111
- type: 'boolean',
112
- required: true
113
- },
114
- setPopupOpen: {
115
- type: 'function',
116
- required: true
117
- }
118
- };
119
-
120
-
121
- class $fef5d623a19b12b5$export$2e2bcd8739ae039 {
122
- constructor(config){
123
- this.config = config;
124
- this.config.nonCustodialDeclarationType = config.nonCustodialDeclarationType || 'SIGNATURE';
125
- this.config.transactionTypeAllowed = config.transactionTypeAllowed || 'ALL';
126
- this.widgetRendered = false;
127
- this.shouldBeDestroyed = false;
128
- this.popupRendered = false;
129
- this.config.fallbacks = this.config.fallbacks || [
130
- {
131
- flow: 'WALLET_NOT_SUPPORTED',
132
- action: 'DECLARATION'
133
- },
134
- ];
135
- }
136
- /**
137
- * Renders a new widget
138
- * @param type - the type of widget to render
139
- */ async renderWidget(type = $48505191f6b4c91c$export$95cdd842edb044c5.WITHDRAWAL) {
140
- this._renderWidget(type);
141
- }
142
- async _renderWidget(type) {
143
- if (this.widgetRendered) {
144
- console.error('Notabene: Widget already rendered, please destroy it first');
145
- this.config.onError(new Error('Widget already rendered, please destroy it first'));
146
- return;
147
- }
148
- this.widget = await this.buildWidget();
149
- this.popup = await this.buildPopup();
150
- this.widget.updateProps({
151
- transaction: this.tx,
152
- type: type
153
- });
154
- this.popup.updateProps({
155
- transaction: this.tx,
156
- type: type
157
- });
158
- this.popup.hide();
159
- await this.widget.render(this.config.container);
160
- await this.renderPopup();
161
- }
162
- async renderPopup() {
163
- if (!this.popup) return;
164
- if (!this.popupRendered) {
165
- await this.popup.render(this.config.container);
166
- this.popupRendered = true;
167
- }
168
- }
169
- async destroyWidget() {
170
- if (!this.widgetRendered || !this.popupRendered) {
171
- this.shouldBeDestroyed = true;
172
- return;
173
- }
174
- await $4mxld$zoid.destroy();
175
- this.shouldBeDestroyed = false;
176
- this.widgetRendered = false;
177
- this.popupRendered = false;
178
- }
179
- setTransaction(tx) {
180
- this.tx = tx;
181
- if (this.widgetRendered) {
182
- this.widget.updateProps({
183
- transaction: tx
184
- });
185
- this.popup.updateProps({
186
- transaction: tx
187
- });
188
- }
189
- }
190
- cleanUpDestroyedComponents() {
191
- if (!this.shouldBeDestroyed) return;
192
- this.destroyWidget();
193
- }
194
- getWidgetUrl() {
195
- const url = new URL(this.config.widget);
196
- url.hash = `authToken=${this.config.authToken}`;
197
- return url.toString();
198
- }
199
- getAdditionalInfoUrl() {
200
- const url = new URL(this.config.widget);
201
- url.search = 'popup=true';
202
- url.hash = `authToken=${this.config.authToken}`;
203
- return url.toString();
204
- }
205
- handleError(err) {
206
- this.error = err;
207
- if (this.widget && this.popup) {
208
- this.widget.updateProps({
209
- error: err
210
- });
211
- this.popup.updateProps({
212
- error: err
213
- });
214
- }
215
- if (!err || !this.config.onError) return;
216
- this.config.onError(err);
217
- }
218
- buildWidget() {
219
- if (!this.config.authToken) {
220
- console.error('Notabene: authToken is required');
221
- return;
222
- }
223
- const builtWidget = ($parcel$interopDefault($4mxld$zoid)).create({
224
- tag: 'widget',
225
- url: this.getWidgetUrl(),
226
- dimensions: {
227
- width: '100%',
228
- height: '280px'
229
- },
230
- attributes: {
231
- iframe: {
232
- allow: 'usb *;bluetooth *'
233
- }
234
- },
235
- props: $fd25ee12f453fb93$export$1aafbfc7f973e450
236
- })({
237
- vaspDID: this.config.vaspDID,
238
- transaction: {},
239
- theme: this.config.theme,
240
- dictionary: this.config.dictionary,
241
- transactionTypeAllowed: this.config.transactionTypeAllowed,
242
- nonCustodialDeclarationType: this.config.nonCustodialDeclarationType,
243
- beneficiaryDetails: this.config.beneficiaryDetails,
244
- fieldsProps: this.config.fieldsProps,
245
- fallbacks: this.config.fallbacks,
246
- optInFeatures: this.config.optInFeatures,
247
- onRequireAdditionalInformation: ()=>{
248
- this.popup.updateProps({
249
- popupOpen: true
250
- });
251
- this.popup.show();
252
- },
253
- onValidStateChange: (isValid)=>{
254
- this.config.onValidStateChange(isValid);
255
- },
256
- error: this.error,
257
- onError: (err)=>{
258
- this.handleError(err);
259
- console.debug('Notabene: Error handler called');
260
- },
261
- storeTransaction: (tx)=>{
262
- this.setTransaction(tx);
263
- }
264
- });
265
- builtWidget.event.on(($parcel$interopDefault($4mxld$zoid)).EVENT.RENDERED, ()=>{
266
- this.widgetRendered = true;
267
- this.cleanUpDestroyedComponents();
268
- });
269
- return builtWidget;
270
- }
271
- buildPopup() {
272
- if (!this.config.authToken) {
273
- console.error('Notabene: authToken is required');
274
- return;
275
- }
276
- const builtPopup = ($parcel$interopDefault($4mxld$zoid)).create({
277
- tag: 'popup',
278
- url: this.getAdditionalInfoUrl(),
279
- children: ()=>{
280
- return {
281
- Transaction: this.tx
282
- };
283
- },
284
- dimensions: {
285
- width: '100%',
286
- height: '100%'
287
- },
288
- attributes: {
289
- iframe: {
290
- allow: 'usb *;bluetooth *'
291
- }
292
- },
293
- props: $fd25ee12f453fb93$export$73ab9d0d47d71b0c,
294
- containerTemplate: ({ uid: uid , doc: doc , frame: frame , prerenderFrame: prerenderFrame })=>{
295
- return $4mxld$jsxpragmatic.node('div', {
296
- id: uid,
297
- class: 'nb-container'
298
- }, $4mxld$jsxpragmatic.node('style', null, `
299
- #${uid}.container {
300
- border: 5px solid red;
301
- }
302
- .nb-overlay {
303
- position: fixed;
304
- width: 100vw;
305
- height: 100vh;
306
- top: 0;
307
- left: 0;
308
- display: flex;
309
- align-items: center;
310
- justify-content: center;
311
- background-color: rgba(0,0,0,0.5);
312
- }
313
- .nb-popup {
314
- width: 100%;
315
- height: 100%;
316
- }
317
- .nb-preloader {
318
- display: none;
319
- }
320
- `), $4mxld$jsxpragmatic.node('div', {
321
- class: 'nb-overlay'
322
- }, $4mxld$jsxpragmatic.node('node', {
323
- el: frame,
324
- class: 'nb-popup'
325
- }), $4mxld$jsxpragmatic.node('node', {
326
- el: prerenderFrame,
327
- class: 'nb-preloader'
328
- }))).render($4mxld$jsxpragmatic.dom({
329
- doc: doc
330
- }));
331
- }
332
- })({
333
- vaspDID: this.config.vaspDID,
334
- transaction: this.tx,
335
- beneficiaryDetails: this.config.beneficiaryDetails,
336
- fieldsProps: this.config.fieldsProps,
337
- storeTransaction: (tx)=>{
338
- this.setTransaction(tx);
339
- console.debug('Notabene: Transaction stored');
340
- },
341
- close: ()=>{
342
- this.popup.close();
343
- console.debug('Notabene: Popup closing');
344
- },
345
- setPopupOpen: (isOpen)=>{
346
- this.popup.updateProps({
347
- popupOpen: isOpen
348
- });
349
- },
350
- error: this.error,
351
- onError: (err)=>{
352
- this.handleError(err);
353
- console.debug('Notabene: Error handler called');
354
- },
355
- popupOpen: false,
356
- theme: this.config.theme,
357
- dictionary: this.config.dictionary,
358
- transactionTypeAllowed: this.config.transactionTypeAllowed,
359
- nonCustodialDeclarationType: this.config.nonCustodialDeclarationType,
360
- fallbacks: this.config.fallbacks,
361
- optInFeatures: this.config.optInFeatures
362
- });
363
- builtPopup.event.on(($parcel$interopDefault($4mxld$zoid)).EVENT.RENDERED, ()=>{
364
- this.popupRendered = true;
365
- this.cleanUpDestroyedComponents();
366
- });
367
- return builtPopup;
368
- }
369
- }
370
- if (window) window.Notabene = $fef5d623a19b12b5$export$2e2bcd8739ae039;
371
-
372
-
373
-
374
- $parcel$exportWildcard(module.exports, $48505191f6b4c91c$exports);
375
-
376
-
package/jest.config.js DELETED
@@ -1,5 +0,0 @@
1
- /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
2
- module.exports = {
3
- preset: 'ts-jest',
4
- testEnvironment: 'node',
5
- };
package/public/index.html DELETED
@@ -1,93 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- ​
4
- <head>
5
- <meta charset="UTF-8" />
6
- <meta http-equiv="X-UA-Compatible" content="IE=edge" />
7
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
- <title>Notabene Widget Example</title>
9
- <script id="notabene" async src="http://localhost:3002/js/notabene.js"></script>
10
- <script>
11
- document.querySelector('#notabene').addEventListener('load', function () {
12
- const nb = new Notabene({
13
- widget: 'http://localhost:8080',
14
- container: '#container',
15
- authToken: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NkstUiJ9.eyJpYXQiOjE2NzI2ODY0ODMsImV4cCI6bnVsbCwidmMiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiLCJodHRwczovL2FwaS5ub3RhYmVuZS5pZC9zY2hlbWFzL3YxIl0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJBY2Nlc3NUb2tlbiJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJub25jZSI6ImFlZDY4YjllLWUxODEtNDRkMy1hOTdkLTYyODBjODA0NDZlMiIsInNjb3BlIjoiY3VzdG9tZXIifX0sInN1YiI6ImRpZDpldGhyOjB4YzZiMGRiYTRjOGYzNmQ3NjQxYjU4MjEwYzg1ZjI3Mjk1NDcyZmQxMyIsImlzcyI6ImRpZDpldGhyOjB4YzZiMGRiYTRjOGYzNmQ3NjQxYjU4MjEwYzg1ZjI3Mjk1NDcyZmQxMyJ9.-qdoEeJrRcYvtleGJhPDXD-crS0xDLjz3idUevEZKCevD64wjFugKaSP5ncenRSPVezHTNHdIRWlh7Cy6gX8jwE',
16
- onValidStateChange: (isValid) => {
17
- console.log(nb.tx);
18
- if(isValid){
19
- console.log('tx: %o',nb.tx)
20
- }
21
- },
22
- onError: (err) => {
23
- // If any errors are encountered, this function will be called.
24
- console.error(err.message);
25
- },
26
- vaspDID: 'did:ethr:0x8e73ea19a72e89f20339f5a9e4cda8e4b230455c',
27
- theme:{
28
- // primaryColor: '#fff',
29
- // primaryFontColor: '#fff',
30
- // secondaryFontColor: '#fff',
31
- // backgroundColor: 'black',
32
- // mode: 'dark',
33
- },
34
- // transactionTypeAllowed: 'VASP_2_VASP_ONLY',
35
- nonCustodialDeclarationType: 'DECLARATION',
36
- optInFeatures: ['REUSE_ADDRESS_OWNERSHIP_PROOF'],
37
-
38
- // @deprecated
39
- // beneficiaryDetails: ['beneficiaryNationalIdentification'],
40
-
41
- // fieldsProps: {
42
- // nationalIdentification: {
43
- // forceDisplay: true,
44
- // },
45
- // dateAndPlaceOfBirth: {
46
- // forceDisplay: true,
47
- // },
48
- // geographicAddress: {
49
- // forceDisplay: true,
50
- // optional: true
51
- // },
52
- // counterparty: {
53
- // optional: true
54
- // }
55
- // }
56
- fallbacks: [{flow: "WALLET_NOT_SUPPORTED", action: "REJECT"}]
57
- });
58
- nb.setTransaction({
59
- transactionAsset: 'BTC',
60
- // originatorAccountNumber: '0xdc0fF33a148031FasdfD1DE1e18e23456b25E1d8',
61
- beneficiaryAccountNumber: '0x8e73ea19a72e89f20339f5a9e4cda8e4b230455c',
62
- transactionAmount: '400000000000',
63
- });
64
-
65
- switch (window.location.hash.substr(1)) {
66
- case "post_deposit":
67
- nb.renderWidget('POST_DEPOSIT');
68
- break;
69
- case "withdrawal":
70
- default:
71
- nb.renderWidget('WITHDRAWAL');
72
- break;
73
- }
74
- });
75
- </script>
76
- </head>
77
- ​
78
- <body>
79
- <div id="container">
80
- </body>
81
- ​
82
- </html>
83
-
84
- <style>
85
- body{
86
- margin: 0;
87
- padding: 0;
88
- }
89
- #container{
90
- max-width: 100vw;
91
-
92
- }
93
- </style>
package/src/lib/index.ts DELETED
@@ -1,2 +0,0 @@
1
- export { default as Notabene } from '../notabene';
2
- export * from '../types';
@@ -1,10 +0,0 @@
1
- import Notabene from '../notabene';
2
- export * from '../types';
3
-
4
- declare global {
5
- interface Window {
6
- Notabene: typeof Notabene;
7
- }
8
- }
9
-
10
- if (window) window.Notabene = Notabene;