@dintero/discounts-web-sdk 0.2.3 → 0.2.5

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 (38) hide show
  1. package/.github/workflows/build.yml +14 -10
  2. package/.github/workflows/release.yml +6 -6
  3. package/README.md +2 -2
  4. package/dist/declarations/src/index.d.ts +1 -1
  5. package/dist/dintero-discounts-web-sdk.cjs.d.ts +1 -0
  6. package/dist/dintero-discounts-web-sdk.cjs.d.ts.map +1 -0
  7. package/dist/dintero-discounts-web-sdk.cjs.dev.js +29 -101
  8. package/dist/dintero-discounts-web-sdk.cjs.prod.js +29 -101
  9. package/dist/dintero-discounts-web-sdk.esm.js +29 -101
  10. package/dist/dintero-discounts-web-sdk.umd.min.js +1 -1
  11. package/dist/dintero-discounts-web-sdk.umd.min.js.map +1 -1
  12. package/package.json +12 -18
  13. package/coverage/Chrome Headless 110.0.5478.0 (Linux x86_64)/html/base.css +0 -224
  14. package/coverage/Chrome Headless 110.0.5478.0 (Linux x86_64)/html/block-navigation.js +0 -87
  15. package/coverage/Chrome Headless 110.0.5478.0 (Linux x86_64)/html/discounts.ts.html +0 -1093
  16. package/coverage/Chrome Headless 110.0.5478.0 (Linux x86_64)/html/dom.ts.html +0 -310
  17. package/coverage/Chrome Headless 110.0.5478.0 (Linux x86_64)/html/error.ts.html +0 -244
  18. package/coverage/Chrome Headless 110.0.5478.0 (Linux x86_64)/html/favicon.png +0 -0
  19. package/coverage/Chrome Headless 110.0.5478.0 (Linux x86_64)/html/fetch.ts.html +0 -385
  20. package/coverage/Chrome Headless 110.0.5478.0 (Linux x86_64)/html/formatters.ts.html +0 -223
  21. package/coverage/Chrome Headless 110.0.5478.0 (Linux x86_64)/html/index.html +0 -236
  22. package/coverage/Chrome Headless 110.0.5478.0 (Linux x86_64)/html/index.ts.html +0 -436
  23. package/coverage/Chrome Headless 110.0.5478.0 (Linux x86_64)/html/loading.ts.html +0 -214
  24. package/coverage/Chrome Headless 110.0.5478.0 (Linux x86_64)/html/normalize.ts.html +0 -136
  25. package/coverage/Chrome Headless 110.0.5478.0 (Linux x86_64)/html/prettify.css +0 -1
  26. package/coverage/Chrome Headless 110.0.5478.0 (Linux x86_64)/html/prettify.js +0 -2
  27. package/coverage/Chrome Headless 110.0.5478.0 (Linux x86_64)/html/sort-arrow-sprite.png +0 -0
  28. package/coverage/Chrome Headless 110.0.5478.0 (Linux x86_64)/html/sorter.js +0 -196
  29. package/coverage/Chrome Headless 110.0.5478.0 (Linux x86_64)/html/translations.ts.html +0 -199
  30. package/dist/declarations/src/discounts.d.ts +0 -7
  31. package/dist/declarations/src/dom.d.ts +0 -15
  32. package/dist/declarations/src/error.d.ts +0 -2
  33. package/dist/declarations/src/fetch.d.ts +0 -2
  34. package/dist/declarations/src/formatters.d.ts +0 -7
  35. package/dist/declarations/src/loading.d.ts +0 -2
  36. package/dist/declarations/src/normalize.d.ts +0 -1
  37. package/dist/declarations/src/translations.d.ts +0 -25
  38. package/karma.conf.js +0 -33
@@ -11,13 +11,17 @@ jobs:
11
11
  runs-on: ubuntu-latest
12
12
 
13
13
  steps:
14
- - uses: actions/checkout@v2
15
- - uses: actions/setup-node@v2
16
- with:
17
- node-version: 'lts/*'
18
- cache: npm
19
- - name: npm install, build and test
20
- run: |
21
- npm install
22
- npm run test
23
- npm run build
14
+ - name: Checkout
15
+ uses: actions/checkout@v3
16
+
17
+ - name: Setup Node.js
18
+ uses: actions/setup-node@v3
19
+ with:
20
+ node-version: 'lts/*'
21
+ cache: yarn
22
+
23
+ - name: yarn install, build and test
24
+ run: |
25
+ yarn install
26
+ yarn run test
27
+ yarn run build
@@ -18,22 +18,22 @@ jobs:
18
18
 
19
19
  steps:
20
20
  - name: Checkout
21
- uses: actions/checkout@v2
21
+ uses: actions/checkout@v3
22
22
  with:
23
23
  fetch-depth: 0
24
24
 
25
25
  - name: Setup Node.js
26
- uses: actions/setup-node@v2
26
+ uses: actions/setup-node@v3
27
27
  with:
28
28
  node-version: 'lts/*'
29
29
 
30
- - name: Install dependencies
30
+ - name: yarn install and test
31
31
  run: |
32
- npm install
33
- npm run test
32
+ yarn install
33
+ yarn run test
34
34
 
35
35
  - name: Release
36
36
  env:
37
37
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
38
38
  NPM_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
39
- run: npm run semantic-release
39
+ run: yarn run semantic-release
package/README.md CHANGED
@@ -169,8 +169,8 @@ All major modern browsers above version `N - 1`, where `N` is the most recent ve
169
169
  ## Building from source
170
170
 
171
171
  ```bash
172
- npm install
173
- npm run build
172
+ yarn install
173
+ yarn run build
174
174
  ```
175
175
 
176
176
  ### Creating a new release
@@ -1,2 +1,2 @@
1
- import { Configuration, Embed } from "./types";
1
+ import { Configuration, Embed } from "./types.js";
2
2
  export declare const embed: (configuration: Configuration) => Promise<Embed>;
@@ -1 +1,2 @@
1
1
  export * from "./declarations/src/index";
2
+ //# sourceMappingURL=dintero-discounts-web-sdk.cjs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dintero-discounts-web-sdk.cjs.d.ts","sourceRoot":"","sources":["./declarations/src/index.d.ts"],"names":[],"mappings":"AAAA"}
@@ -4,55 +4,43 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  const createElement = options => {
6
6
  const elem = document.createElement(options.tag);
7
-
8
7
  if (options.attributes) {
9
8
  addAttributes(elem, options.attributes);
10
9
  }
11
-
12
10
  if (options.handlers) {
13
11
  addEventListeners(elem, options.handlers);
14
12
  }
15
-
16
13
  if (options.styles) {
17
14
  addStyles(elem, options.styles, options.theme);
18
15
  }
19
-
20
16
  if (options.innerHTML) {
21
17
  elem.innerHTML = options.innerHTML;
22
18
  }
23
-
24
19
  return elem;
25
20
  };
26
-
27
21
  const addClass = (elem, className) => {
28
22
  const names = elem.className.split(" ");
29
-
30
23
  if (!names.includes(className)) {
31
24
  elem.className = elem.className + " " + className;
32
25
  }
33
26
  };
34
-
35
27
  const hash = input => {
36
28
  let hash = 0;
37
-
38
29
  for (var i = 0; i < input.length; i++) {
39
30
  var char = input.charCodeAt(i);
40
31
  hash = (hash << 5) - hash + char;
41
32
  hash = hash & hash;
42
33
  }
43
-
44
34
  return hash.toString(36);
45
35
  };
46
-
47
36
  const addStyles = (elem, styles, theme) => {
48
37
  // "styled components" light
49
38
  styles.forEach(cssFn => {
50
39
  // get class name from hash
51
40
  const className = 'dintero-deals-' + hash(cssFn('dintero-deals', theme));
52
- addClass(elem, className); // add style tag to DOM if not exists
53
-
41
+ addClass(elem, className);
42
+ // add style tag to DOM if not exists
54
43
  const hit = document.querySelector(`[data-css-hash=${className}]`);
55
-
56
44
  if (!hit) {
57
45
  const style = document.createElement('style');
58
46
  style.innerHTML = cssFn(className, theme);
@@ -61,13 +49,11 @@ const addStyles = (elem, styles, theme) => {
61
49
  }
62
50
  });
63
51
  };
64
-
65
52
  const addAttributes = (elem, attributes) => {
66
53
  Object.keys(attributes).forEach(key => {
67
54
  elem.setAttribute(key, attributes[key]);
68
55
  });
69
56
  };
70
-
71
57
  const addEventListeners = (elem, handlers) => {
72
58
  Object.keys(handlers).forEach(key => {
73
59
  elem.addEventListener(key, handlers[key]);
@@ -97,13 +83,11 @@ const no = {
97
83
  const translations = {
98
84
  no
99
85
  };
100
- const findValuesRegex = /(\{\{)[^}]*(\}\})/g; // i18n light
101
-
86
+ const findValuesRegex = /(\{\{)[^}]*(\}\})/g;
87
+ // i18n light
102
88
  const t = (translateString, values) => {
103
89
  const matches = translateString.match(findValuesRegex) || [];
104
-
105
90
  const _values = values || {};
106
-
107
91
  return matches.reduce((interpolated, match) => {
108
92
  const key = match.replace('{{', '').replace('}}', '');
109
93
  const value = _values[key] || '';
@@ -118,41 +102,32 @@ const monetaryString = (amount, configuration, options) => {
118
102
  const beforeDot = amountString.slice(0, dotIndex) || '0';
119
103
  const afterDot = amountString.slice(dotIndex);
120
104
  const exponentAmount = afterDot == "00" && !opt.decimal ? beforeDot : beforeDot + '.' + afterDot;
121
-
122
105
  if (opt.variant === 'short' && afterDot === '00') {
123
106
  return beforeDot + ',-';
124
107
  }
125
-
126
108
  if (!opt.currency) {
127
109
  return exponentAmount;
128
110
  }
129
-
130
111
  if (configuration.currency.position === 'prefix') {
131
112
  return configuration.currency.value + ' ' + exponentAmount;
132
113
  }
133
-
134
114
  return exponentAmount + ' ' + configuration.currency.value;
135
115
  };
136
-
137
116
  const padZero = value => {
138
117
  if (value < 10) {
139
118
  return `0${value}`;
140
119
  }
141
-
142
120
  return value.toString();
143
121
  };
144
-
145
122
  const dateString = (isoString, configuration) => {
146
123
  try {
147
124
  const date = new Date(isoString);
148
-
149
125
  if (configuration.language === 'no') {
150
126
  const dd = padZero(date.getDate());
151
127
  const mm = padZero(date.getMonth() + 1);
152
128
  const yyyy = date.getFullYear();
153
129
  return [dd, mm, yyyy].join('.');
154
130
  }
155
-
156
131
  return new Intl.DateTimeFormat().format(date);
157
132
  } catch (e) {
158
133
  return isoString.substr(0, 10);
@@ -178,7 +153,6 @@ const normalize = className => `
178
153
  `;
179
154
 
180
155
  const findWebshopLink = discount => discount.links && discount.links.find(x => x.rel && x.rel === 'webshop');
181
-
182
156
  const discountStyle = (className, theme) => `
183
157
  @keyframes appear {
184
158
  from {
@@ -221,7 +195,6 @@ const discountStyle = (className, theme) => `
221
195
  }
222
196
  }
223
197
  `;
224
-
225
198
  const imageWrapperStyles = className => `
226
199
  .${className} {
227
200
  display: flex;
@@ -230,7 +203,6 @@ const imageWrapperStyles = className => `
230
203
  height: 300px;
231
204
  width: 100%,
232
205
  }`;
233
-
234
206
  const imageStyle = className => `
235
207
  .${className} {
236
208
  max-width: 100%;
@@ -238,7 +210,6 @@ const imageStyle = className => `
238
210
  text-align: center;
239
211
  display: inline-block;
240
212
  }`;
241
-
242
213
  const ribbonTopStyle = (className, theme) => `
243
214
  .${className} {
244
215
  position: absolute;
@@ -266,7 +237,6 @@ const ribbonTopStyle = (className, theme) => `
266
237
  opacity: 0.5;
267
238
  }
268
239
  `;
269
-
270
240
  const ribbonBottomStyle = (className, theme) => `
271
241
  .${className} {
272
242
  position: absolute;
@@ -294,7 +264,6 @@ const ribbonBottomStyle = (className, theme) => `
294
264
  opacity: 0.5;
295
265
  }
296
266
  `;
297
-
298
267
  const titleStyle = className => `
299
268
  .${className} {
300
269
  margin-bottom: 0;
@@ -303,12 +272,10 @@ const titleStyle = className => `
303
272
  font-size: 1.3em;
304
273
  font-weight: 700;
305
274
  }`;
306
-
307
275
  const subtitleStyle = className => `
308
276
  .${className} {
309
277
  margin-top: 0;
310
278
  }`;
311
-
312
279
  const limitationsWrapperStyle = className => `
313
280
  .${className} {
314
281
  font-size: 0.75em;
@@ -317,11 +284,9 @@ const limitationsWrapperStyle = className => `
317
284
  display: block;
318
285
  }
319
286
  `;
320
-
321
287
  const getTopRibbonText = discount => {
322
288
  return discount && discount.metadata && discount.metadata.label || undefined;
323
289
  };
324
-
325
290
  const createTopRibbon = (discount, configuration) => {
326
291
  const text = getTopRibbonText(discount);
327
292
  return text && createElement({
@@ -331,10 +296,8 @@ const createTopRibbon = (discount, configuration) => {
331
296
  styles: [ribbonTopStyle]
332
297
  });
333
298
  };
334
-
335
299
  const getBottomRibbonText = (discount, configuration) => {
336
300
  const tStrings = translations[configuration.language];
337
-
338
301
  if (discount.reward.type === "discount_item_new_price") {
339
302
  return monetaryString(discount.reward.value, configuration, {
340
303
  variant: "short"
@@ -363,10 +326,8 @@ const getBottomRibbonText = (discount, configuration) => {
363
326
  } else if (discount.reward.type === "discount_item_percent") {
364
327
  return discount.reward.value + "%";
365
328
  }
366
-
367
329
  return "";
368
330
  };
369
-
370
331
  const createBottomRibbon = (discount, configuration) => {
371
332
  const text = getBottomRibbonText(discount, configuration);
372
333
  return text && createElement({
@@ -376,7 +337,6 @@ const createBottomRibbon = (discount, configuration) => {
376
337
  styles: [ribbonBottomStyle]
377
338
  });
378
339
  };
379
-
380
340
  const createImage = discount => {
381
341
  const imageWrapper = createElement({
382
342
  tag: "div",
@@ -392,14 +352,11 @@ const createImage = discount => {
392
352
  },
393
353
  styles: [normalize, imageStyle]
394
354
  });
395
-
396
355
  if (image) {
397
356
  imageWrapper.appendChild(image);
398
357
  }
399
-
400
358
  return imageWrapper;
401
359
  };
402
-
403
360
  const createLimitations = (discount, configuration) => {
404
361
  const tStrings = translations[configuration.language];
405
362
  const wrapper = createElement({
@@ -434,13 +391,8 @@ const createLimitations = (discount, configuration) => {
434
391
  });
435
392
  const children = [quantity, repeat, start, end].filter(child => child);
436
393
  children.forEach(child => wrapper.appendChild(child));
437
- console.log({
438
- discount,
439
- wrapper
440
- });
441
394
  return wrapper;
442
395
  };
443
-
444
396
  const createDiscount = (discount, configuration) => {
445
397
  const discountElem = createElement({
446
398
  tag: findWebshopLink(discount) ? "a" : "div",
@@ -464,8 +416,8 @@ const createDiscount = (discount, configuration) => {
464
416
  tag: "p",
465
417
  innerHTML: discount.description
466
418
  });
467
- const limitations = createLimitations(discount, configuration); // add children to discount wrapper
468
-
419
+ const limitations = createLimitations(discount, configuration);
420
+ // add children to discount wrapper
469
421
  const children = [ribbonTop, ribbonBottom, image, title, subtitle, description, limitations].filter(child => child);
470
422
  children.forEach(child => discountElem.appendChild(child));
471
423
  return discountElem;
@@ -473,7 +425,8 @@ const createDiscount = (discount, configuration) => {
473
425
 
474
426
  const createHeaders = (keyValues, configuration) => {
475
427
  const headers = new Headers();
476
- Object.entries({ ...keyValues,
428
+ Object.entries({
429
+ ...keyValues,
477
430
  "Dintero-System-Name": "deals-web-sdk",
478
431
  "Dintero-System-Version": configuration.version
479
432
  }).forEach(([key, value]) => {
@@ -481,12 +434,10 @@ const createHeaders = (keyValues, configuration) => {
481
434
  });
482
435
  return headers;
483
436
  };
484
-
485
437
  const fetchAccessToken = configuration => {
486
438
  if (!configuration.api) {
487
439
  throw new Error("Authentication configuration missing");
488
440
  }
489
-
490
441
  const basicAuthCredentials = window.btoa(`${configuration.api.key}:${configuration.api.secret}`);
491
442
  const headers = createHeaders({
492
443
  Authorization: `Basic ${basicAuthCredentials}`,
@@ -504,17 +455,14 @@ const fetchAccessToken = configuration => {
504
455
  if (response.status === 200) {
505
456
  return response.json();
506
457
  }
507
-
508
458
  throw new Error("Authentication failed");
509
459
  });
510
460
  };
511
-
512
461
  const fetchDiscounts = configuration => {
513
462
  return fetchAccessToken(configuration).then(tokenResponse => {
514
463
  const headers = createHeaders({
515
464
  Authorization: `${tokenResponse.token_type} ${tokenResponse.access_token}`
516
465
  }, configuration);
517
-
518
466
  if (configuration.api.discountId) {
519
467
  return window.fetch(`${configuration.api.url}/v1/accounts/${configuration.api.account}/discounts/public/rules/${configuration.api.discountId}`, {
520
468
  headers
@@ -522,18 +470,15 @@ const fetchDiscounts = configuration => {
522
470
  if (response.status === 200) {
523
471
  return response.json().then(discount => [discount]);
524
472
  }
525
-
526
473
  throw new Error("Authentication failed");
527
474
  });
528
475
  }
529
-
530
476
  return window.fetch(`${configuration.api.url}/v1/accounts/${configuration.api.account}/discounts/public/rules?limit=${configuration.api.limit}`, {
531
477
  headers
532
478
  }).then(response => {
533
479
  if (response.status === 200) {
534
480
  return response.json();
535
481
  }
536
-
537
482
  throw new Error("Authentication failed");
538
483
  });
539
484
  });
@@ -572,7 +517,6 @@ const errorStyle = (className, theme) => `
572
517
  transform-origin: top center;
573
518
  }
574
519
  `;
575
-
576
520
  const createError = configuration => {
577
521
  const tString = translations[configuration.language];
578
522
  const errorElem = createElement({
@@ -613,7 +557,6 @@ const loadingStyle = (className, theme) => `
613
557
  }
614
558
  }
615
559
  `;
616
-
617
560
  const createLoading = configuration => {
618
561
  const errorElem = createElement({
619
562
  tag: "div",
@@ -625,7 +568,7 @@ const createLoading = configuration => {
625
568
 
626
569
  var pkg = {
627
570
  name: "@dintero/discounts-web-sdk",
628
- version: "0.2.3",
571
+ version: "0.2.5",
629
572
  description: "Dintero Discounts SDK for web frontends",
630
573
  main: "dist/dintero-discounts-web-sdk.cjs.js",
631
574
  module: "dist/dintero-discounts-web-sdk.esm.js",
@@ -636,10 +579,10 @@ var pkg = {
636
579
  umdName: "discounts"
637
580
  },
638
581
  scripts: {
639
- test: "karma start",
582
+ test: "vitest --dom",
640
583
  watch: "preconstruct watch",
641
584
  build: "preconstruct build",
642
- prepublishOnly: "npm run build",
585
+ prepublishOnly: "yarn build",
643
586
  "semantic-release": "semantic-release"
644
587
  },
645
588
  "private": false,
@@ -654,27 +597,21 @@ var pkg = {
654
597
  url: "https://github.com/Dintero/Dintero.Discounts.Web.SDK/issues"
655
598
  },
656
599
  devDependencies: {
657
- "@babel/preset-env": "^7.14.1",
658
- "@babel/preset-typescript": "^7.13.0",
659
- "@preconstruct/cli": "^2.1.0",
660
- "@semantic-release/git": "^10.0.1",
661
- chai: "^4.2.0",
662
- karma: "^6.3.16",
663
- "karma-chai": "^0.1.0",
664
- "karma-chrome-launcher": "^3.1.0",
665
- "karma-mocha": "^2.0.1",
666
- "karma-typescript": "^5.0.3",
667
- mocha: "^8.1.1",
668
- prettier: "^2.7.0",
669
- puppeteer: "^19.0.0",
670
- "semantic-release": "^19.0.3",
671
- typescript: "^4.2.4"
600
+ "@babel/core": "7.13.0",
601
+ "@babel/preset-typescript": "7.13.0",
602
+ "@preconstruct/cli": "2.8.1",
603
+ "@semantic-release/git": "10.0.1",
604
+ "happy-dom": "12.9.0",
605
+ prettier: "3.0.3",
606
+ "semantic-release": "22.0.5",
607
+ typescript: "5.2.2",
608
+ vitest: "0.34.6"
672
609
  }
673
610
  };
674
611
 
675
612
  const defaultConfig = {
676
613
  language: 'no',
677
- version: pkg.version,
614
+ version: pkg?.version || "SNAPSHOT",
678
615
  linkTarget: '_self',
679
616
  currency: {
680
617
  value: 'Kr',
@@ -697,22 +634,24 @@ const defaultConfig = {
697
634
  limit: 50
698
635
  }
699
636
  };
700
-
701
637
  const mergeConfig = (a, b) => {
702
- return { ...a,
638
+ return {
639
+ ...a,
703
640
  ...b,
704
- currency: { ...a.currency,
641
+ currency: {
642
+ ...a.currency,
705
643
  ...b.currency
706
644
  },
707
- theme: { ...a.theme,
645
+ theme: {
646
+ ...a.theme,
708
647
  ...b.theme
709
648
  },
710
- api: { ...a.api,
649
+ api: {
650
+ ...a.api,
711
651
  ...b.api
712
652
  }
713
653
  };
714
654
  };
715
-
716
655
  const wrapperStyles = (className, theme) => `
717
656
  .${className} {
718
657
  display: flex;
@@ -725,33 +664,25 @@ const wrapperStyles = (className, theme) => `
725
664
  font-size: ${theme.fontSize};
726
665
  color: ${theme.color};
727
666
  }`;
728
-
729
667
  const embed = async configuration => {
730
668
  const _configuration = mergeConfig(defaultConfig, configuration);
731
-
732
669
  if (!_configuration.container || !_configuration.container.appendChild) {
733
670
  console.error("Invalid configuration");
734
671
  throw new Error("Invalid configuration");
735
672
  }
736
-
737
673
  if (_configuration.discounts) {
738
674
  return renderDeals(_configuration, _configuration.discounts);
739
675
  } else {
740
676
  const loader = createLoading(_configuration);
741
-
742
677
  try {
743
678
  configuration.container.appendChild(loader);
744
679
  const discounts = await fetchDiscounts(_configuration);
745
-
746
680
  _configuration.container.removeChild(loader);
747
-
748
681
  return renderDeals(_configuration, discounts);
749
682
  } catch (error) {
750
683
  configuration.container.removeChild(loader);
751
684
  const errorMessage = createError(_configuration);
752
-
753
685
  _configuration.container.appendChild(errorMessage);
754
-
755
686
  return {
756
687
  destroy: () => {
757
688
  configuration.container.removeChild(errorMessage);
@@ -760,7 +691,6 @@ const embed = async configuration => {
760
691
  }
761
692
  }
762
693
  };
763
-
764
694
  const renderDeals = (configuration, discounts) => {
765
695
  const wrapper = createElement({
766
696
  tag: "div",
@@ -770,12 +700,10 @@ const renderDeals = (configuration, discounts) => {
770
700
  discounts.forEach(discount => {
771
701
  const elem = createDiscount(discount, configuration);
772
702
  const webShopLink = findWebshopLink(discount);
773
-
774
703
  if (webShopLink) {
775
704
  elem.setAttribute('target', configuration?.linkTarget || '_self');
776
705
  elem.setAttribute('href', webShopLink.href);
777
706
  }
778
-
779
707
  wrapper.appendChild(elem);
780
708
  });
781
709
  configuration.container.appendChild(wrapper);