@fmidev/smartmet-alert-client 4.2.7 → 4.7.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.
Files changed (127) hide show
  1. package/.eslintignore +2 -14
  2. package/.github/workflows/test.yaml +26 -0
  3. package/.nvmrc +1 -0
  4. package/dist/index.dark.html +1 -1
  5. package/dist/index.en.html +1 -1
  6. package/dist/index.fi.html +1 -1
  7. package/dist/index.html +5 -0
  8. package/dist/index.js +105 -135
  9. package/dist/index.mjs +112 -135
  10. package/dist/index.sv.html +1 -1
  11. package/dist/locale-en-DCEKDw5G.js +8 -0
  12. package/dist/locale-fi-DPiOM1rB.js +8 -0
  13. package/dist/locale-sv-B0FlbgEF.js +8 -0
  14. package/dist/vendor-Cfkkvdz7.js +21 -0
  15. package/dist/vue/index.mjs +15245 -0
  16. package/dist/vue/style.css +1 -0
  17. package/dist/xml-parser-BiNO9kc-.js +13 -0
  18. package/package.json +61 -25
  19. package/public/index.dark.html +1 -1
  20. package/public/index.en.html +1 -1
  21. package/public/index.fi.html +1 -1
  22. package/public/index.sv.html +1 -1
  23. package/src/AlertClientVue.vue +170 -0
  24. package/src/App.vue +58 -176
  25. package/src/assets/img/ui/arrow-down.svg +4 -14
  26. package/src/assets/img/ui/arrow-up.svg +4 -14
  27. package/src/assets/img/ui/clear.svg +7 -21
  28. package/src/assets/img/ui/close.svg +4 -15
  29. package/src/assets/img/ui/toggle-selected.svg +12 -0
  30. package/src/assets/img/ui/toggle-unselected.svg +12 -0
  31. package/src/assets/img/warning/cold-weather.svg +3 -6
  32. package/src/assets/img/warning/flood-level-3.svg +4 -7
  33. package/src/assets/img/warning/forest-fire-weather.svg +2 -6
  34. package/src/assets/img/warning/grass-fire-weather.svg +2 -6
  35. package/src/assets/img/warning/hot-weather.svg +3 -6
  36. package/src/assets/img/warning/pedestrian-safety.svg +3 -7
  37. package/src/assets/img/warning/rain.svg +2 -7
  38. package/src/assets/img/warning/sea-icing.svg +2 -6
  39. package/src/assets/img/warning/sea-thunder-storm.svg +2 -5
  40. package/src/assets/img/warning/sea-water-height-high-water.svg +3 -8
  41. package/src/assets/img/warning/sea-water-height-shallow-water.svg +3 -7
  42. package/src/assets/img/warning/sea-wave-height.svg +4 -7
  43. package/src/assets/img/warning/sea-wind-legend.svg +2 -5
  44. package/src/assets/img/warning/sea-wind.svg +2 -5
  45. package/src/assets/img/warning/several.svg +2 -5
  46. package/src/assets/img/warning/thunder-storm.svg +2 -5
  47. package/src/assets/img/warning/traffic-weather.svg +2 -6
  48. package/src/assets/img/warning/uv-note.svg +2 -6
  49. package/src/assets/img/warning/wind.svg +2 -5
  50. package/src/components/AlertClient.vue +42 -20
  51. package/src/components/CollapsiblePanel.vue +284 -0
  52. package/src/components/DayLarge.vue +40 -32
  53. package/src/components/DaySmall.vue +17 -7
  54. package/src/components/Days.vue +77 -52
  55. package/src/components/DescriptionWarning.vue +26 -8
  56. package/src/components/GrayScaleToggle.vue +42 -35
  57. package/src/components/Legend.vue +36 -240
  58. package/src/components/MapLarge.vue +55 -50
  59. package/src/components/MapSmall.vue +48 -29
  60. package/src/components/PopupRow.vue +6 -3
  61. package/src/components/Region.vue +162 -53
  62. package/src/components/RegionWarning.vue +33 -11
  63. package/src/components/Regions.vue +59 -29
  64. package/src/components/Warning.vue +53 -49
  65. package/src/components/Warnings.vue +54 -16
  66. package/src/locales/en.json +21 -4
  67. package/src/locales/fi.json +23 -6
  68. package/src/locales/sv.json +20 -3
  69. package/src/main.js +1 -0
  70. package/src/mixins/alertClientCore.js +210 -0
  71. package/src/mixins/config.js +263 -257
  72. package/src/mixins/utils.js +46 -15
  73. package/src/plugins/index.js +1 -1
  74. package/src/scss/_utilities.scss +193 -0
  75. package/src/scss/backgrounds.scss +2 -0
  76. package/src/scss/colors.scss +5 -3
  77. package/src/scss/constants.scss +3 -2
  78. package/src/scss/themes/dark-gray.scss +3 -3
  79. package/src/scss/themes/dark.scss +1 -1
  80. package/src/scss/themes/light-gray.scss +5 -5
  81. package/src/scss/themes/light.scss +3 -3
  82. package/src/scss/warningImages.scss +9 -5
  83. package/src/vue.js +41 -0
  84. package/svgo.config.js +45 -0
  85. package/tests/README.md +430 -0
  86. package/tests/fixtures/mockWarningData.js +135 -0
  87. package/tests/integration/warning-flow.spec.js +452 -0
  88. package/tests/setup.js +41 -0
  89. package/tests/unit/components/AlertClient.spec.js +734 -0
  90. package/tests/unit/components/DayLarge.spec.js +281 -0
  91. package/tests/unit/components/DaySmall.spec.js +278 -0
  92. package/tests/unit/components/Days.spec.js +565 -0
  93. package/tests/unit/components/DescriptionWarning.spec.js +432 -0
  94. package/tests/unit/components/GrayScaleToggle.spec.js +311 -0
  95. package/tests/unit/components/Legend.spec.js +223 -0
  96. package/tests/unit/components/MapLarge.spec.js +276 -0
  97. package/tests/unit/components/MapSmall.spec.js +226 -0
  98. package/tests/unit/components/PopupRow.spec.js +261 -0
  99. package/tests/unit/components/Region.spec.js +430 -0
  100. package/tests/unit/components/RegionWarning.snapshot.spec.js +73 -0
  101. package/tests/unit/components/RegionWarning.spec.js +408 -0
  102. package/tests/unit/components/Regions.spec.js +335 -0
  103. package/tests/unit/components/Warning.snapshot.spec.js +107 -0
  104. package/tests/unit/components/Warning.spec.js +472 -0
  105. package/tests/unit/components/Warnings.spec.js +329 -0
  106. package/tests/unit/components/__snapshots__/RegionWarning.snapshot.spec.js.snap +21 -0
  107. package/tests/unit/components/__snapshots__/Warning.snapshot.spec.js.snap +199 -0
  108. package/tests/unit/mixins/config.spec.js +269 -0
  109. package/tests/unit/mixins/i18n.spec.js +115 -0
  110. package/tests/unit/mixins/keycodes.spec.js +37 -0
  111. package/tests/unit/mixins/utils.spec.js +624 -0
  112. package/vite.config.js +96 -26
  113. package/vitest.config.js +40 -0
  114. package/dist/index.mjs.map +0 -1
  115. package/dist/index.relative.html +0 -19
  116. package/dist/index.start.html +0 -20
  117. package/playwright.config.ts +0 -18
  118. package/public/index.relative.html +0 -19
  119. package/public/index.start.html +0 -20
  120. package/src/assets/img/ui/toggle-selected-blue.svg +0 -4
  121. package/src/assets/img/ui/toggle-selected-dark.svg +0 -4
  122. package/src/assets/img/ui/toggle-selected-light.svg +0 -4
  123. package/src/assets/img/ui/toggle-unselected-dark.svg +0 -4
  124. package/src/assets/img/ui/toggle-unselected-light.svg +0 -4
  125. package/src/mixins/panzoom.js +0 -900
  126. package/test/snapshot.test.ts +0 -126
  127. package/vitest.config.ts +0 -6
@@ -0,0 +1,565 @@
1
+ import { describe, it, expect, afterEach, vi } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import Days from '@/components/Days.vue'
4
+
5
+ const mockDaysInput = [
6
+ {
7
+ weekdayName: 'thursday',
8
+ day: 31,
9
+ month: 10,
10
+ year: 2025,
11
+ severity: 3,
12
+ updatedDate: '31.10.2025',
13
+ updatedTime: '14:00',
14
+ },
15
+ {
16
+ weekdayName: 'friday',
17
+ day: 1,
18
+ month: 11,
19
+ year: 2025,
20
+ severity: 2,
21
+ updatedDate: '31.10.2025',
22
+ updatedTime: '14:00',
23
+ },
24
+ {
25
+ weekdayName: 'saturday',
26
+ day: 2,
27
+ month: 11,
28
+ year: 2025,
29
+ severity: 0,
30
+ updatedDate: '31.10.2025',
31
+ updatedTime: '14:00',
32
+ },
33
+ {
34
+ weekdayName: 'sunday',
35
+ day: 3,
36
+ month: 11,
37
+ year: 2025,
38
+ severity: 0,
39
+ updatedDate: '31.10.2025',
40
+ updatedTime: '14:00',
41
+ },
42
+ {
43
+ weekdayName: 'monday',
44
+ day: 4,
45
+ month: 11,
46
+ year: 2025,
47
+ severity: 0,
48
+ updatedDate: '31.10.2025',
49
+ updatedTime: '14:00',
50
+ },
51
+ ]
52
+
53
+ const mockRegions = [
54
+ { land: [], sea: [] },
55
+ { land: [], sea: [] },
56
+ { land: [], sea: [] },
57
+ { land: [], sea: [] },
58
+ { land: [], sea: [] },
59
+ ]
60
+
61
+ describe('Days.vue', () => {
62
+ let wrapper
63
+
64
+ afterEach(() => {
65
+ if (wrapper) {
66
+ wrapper.unmount()
67
+ }
68
+ })
69
+
70
+ describe('Component mounting', () => {
71
+ it('should mount with required props', () => {
72
+ wrapper = mount(Days, {
73
+ props: {
74
+ input: mockDaysInput,
75
+ regions: mockRegions,
76
+ geometryId: 2021,
77
+ language: 'fi',
78
+ },
79
+ global: {
80
+ stubs: {
81
+ 'b-tabs': false,
82
+ 'b-tab': false,
83
+ },
84
+ },
85
+ })
86
+
87
+ expect(wrapper.exists()).toBe(true)
88
+ })
89
+
90
+ it('should initialize with selectedDay prop', () => {
91
+ wrapper = mount(Days, {
92
+ props: {
93
+ input: mockDaysInput,
94
+ regions: mockRegions,
95
+ geometryId: 2021,
96
+ selectedDay: 2,
97
+ language: 'fi',
98
+ },
99
+ })
100
+
101
+ expect(wrapper.vm.day).toBe(2)
102
+ })
103
+
104
+ it('should have default selectedDay of 0', () => {
105
+ wrapper = mount(Days, {
106
+ props: {
107
+ input: mockDaysInput,
108
+ regions: mockRegions,
109
+ geometryId: 2021,
110
+ language: 'fi',
111
+ },
112
+ })
113
+
114
+ expect(wrapper.vm.day).toBe(0)
115
+ })
116
+ })
117
+
118
+ describe('Props validation', () => {
119
+ it('should accept valid selectedDay values (0-4)', () => {
120
+ const validDays = [0, 1, 2, 3, 4]
121
+
122
+ validDays.forEach((day) => {
123
+ wrapper = mount(Days, {
124
+ props: {
125
+ input: mockDaysInput,
126
+ regions: mockRegions,
127
+ geometryId: 2021,
128
+ selectedDay: day,
129
+ language: 'fi',
130
+ },
131
+ })
132
+
133
+ expect(wrapper.vm.selectedDay).toBe(day)
134
+ })
135
+ })
136
+
137
+ it('should accept staticDays boolean prop', () => {
138
+ wrapper = mount(Days, {
139
+ props: {
140
+ input: mockDaysInput,
141
+ regions: mockRegions,
142
+ geometryId: 2021,
143
+ staticDays: false,
144
+ language: 'fi',
145
+ },
146
+ })
147
+
148
+ expect(wrapper.vm.staticDays).toBe(false)
149
+ })
150
+
151
+ it('should accept timeOffset number prop', () => {
152
+ wrapper = mount(Days, {
153
+ props: {
154
+ input: mockDaysInput,
155
+ regions: mockRegions,
156
+ geometryId: 2021,
157
+ timeOffset: 3600000,
158
+ language: 'fi',
159
+ },
160
+ })
161
+
162
+ expect(wrapper.vm.timeOffset).toBe(3600000)
163
+ })
164
+
165
+ it('should accept loading prop', () => {
166
+ wrapper = mount(Days, {
167
+ props: {
168
+ input: mockDaysInput,
169
+ regions: mockRegions,
170
+ geometryId: 2021,
171
+ loading: false,
172
+ language: 'fi',
173
+ },
174
+ })
175
+
176
+ expect(wrapper.vm.loading).toBe(false)
177
+ })
178
+ })
179
+
180
+ describe('Computed properties', () => {
181
+ it('should compute numberOfDays as 5', () => {
182
+ wrapper = mount(Days, {
183
+ props: {
184
+ input: mockDaysInput,
185
+ regions: mockRegions,
186
+ geometryId: 2021,
187
+ language: 'fi',
188
+ },
189
+ })
190
+
191
+ expect(wrapper.vm.numberOfDays).toBe(5)
192
+ })
193
+ })
194
+
195
+ describe('Day selection', () => {
196
+ it('should emit daySelected event when day changes', async () => {
197
+ wrapper = mount(Days, {
198
+ props: {
199
+ input: mockDaysInput,
200
+ regions: mockRegions,
201
+ geometryId: 2021,
202
+ language: 'fi',
203
+ },
204
+ })
205
+
206
+ wrapper.vm.day = 2
207
+
208
+ await wrapper.vm.$nextTick()
209
+
210
+ expect(wrapper.emitted('daySelected')).toBeTruthy()
211
+ expect(wrapper.emitted('daySelected')[0]).toEqual([2])
212
+ })
213
+
214
+ it('should call onDaySelected when day changes', async () => {
215
+ wrapper = mount(Days, {
216
+ props: {
217
+ input: mockDaysInput,
218
+ regions: mockRegions,
219
+ geometryId: 2021,
220
+ language: 'fi',
221
+ },
222
+ })
223
+
224
+ const onDaySelectedSpy = vi.spyOn(wrapper.vm, 'onDaySelected')
225
+
226
+ wrapper.vm.day = 3
227
+
228
+ await wrapper.vm.$nextTick()
229
+
230
+ expect(onDaySelectedSpy).toHaveBeenCalledWith(3)
231
+ })
232
+ })
233
+
234
+ describe('Edge cases', () => {
235
+ it('should handle empty input array', () => {
236
+ wrapper = mount(Days, {
237
+ props: {
238
+ input: [],
239
+ regions: [],
240
+ geometryId: 2021,
241
+ language: 'fi',
242
+ },
243
+ })
244
+
245
+ expect(wrapper.exists()).toBe(true)
246
+ // With empty input, numberOfDays should be 0
247
+ expect(wrapper.vm.input.length).toBe(0)
248
+ })
249
+
250
+ it('should handle mismatched regions and days', () => {
251
+ wrapper = mount(Days, {
252
+ props: {
253
+ input: mockDaysInput,
254
+ regions: [{ land: [], sea: [] }], // Only 1 region but 5 days
255
+ geometryId: 2021,
256
+ language: 'fi',
257
+ },
258
+ })
259
+
260
+ expect(wrapper.exists()).toBe(true)
261
+ })
262
+
263
+ it('should handle boundary selected day values', () => {
264
+ // Test with last valid day (4)
265
+ wrapper = mount(Days, {
266
+ props: {
267
+ input: mockDaysInput,
268
+ regions: mockRegions,
269
+ selectedDay: 4,
270
+ geometryId: 2021,
271
+ language: 'fi',
272
+ },
273
+ })
274
+
275
+ expect(wrapper.exists()).toBe(true)
276
+ expect(wrapper.vm.day).toBe(4)
277
+ })
278
+
279
+ it('should handle first day selection', () => {
280
+ wrapper = mount(Days, {
281
+ props: {
282
+ input: mockDaysInput,
283
+ regions: mockRegions,
284
+ selectedDay: 0,
285
+ geometryId: 2021,
286
+ language: 'fi',
287
+ },
288
+ })
289
+
290
+ expect(wrapper.exists()).toBe(true)
291
+ expect(wrapper.vm.day).toBe(0)
292
+ })
293
+
294
+ it('should handle malformed day data', () => {
295
+ const malformedData = [
296
+ {
297
+ // Missing required properties
298
+ severity: 1,
299
+ },
300
+ {
301
+ weekdayName: 'friday',
302
+ // Missing other properties
303
+ },
304
+ ]
305
+
306
+ wrapper = mount(Days, {
307
+ props: {
308
+ input: malformedData,
309
+ regions: mockRegions,
310
+ geometryId: 2021,
311
+ language: 'fi',
312
+ },
313
+ })
314
+
315
+ expect(wrapper.exists()).toBe(true)
316
+ })
317
+ })
318
+
319
+ describe('Additional keyboard navigation', () => {
320
+ it('should have switchDay method', () => {
321
+ wrapper = mount(Days, {
322
+ props: {
323
+ input: mockDaysInput,
324
+ regions: mockRegions,
325
+ geometryId: 2021,
326
+ language: 'fi',
327
+ },
328
+ })
329
+
330
+ expect(typeof wrapper.vm.switchDay).toBe('function')
331
+ })
332
+
333
+ it('should navigate left with arrow key', async () => {
334
+ wrapper = mount(Days, {
335
+ props: {
336
+ input: mockDaysInput,
337
+ regions: mockRegions,
338
+ geometryId: 2021,
339
+ selectedDay: 2,
340
+ language: 'fi',
341
+ },
342
+ })
343
+
344
+ // Mock querySelector to avoid DOM errors
345
+ wrapper.vm.$el.querySelector = vi.fn(() => ({
346
+ focus: vi.fn(),
347
+ }))
348
+
349
+ const event = {
350
+ keyCode: 37, // LEFT
351
+ preventDefault: vi.fn(),
352
+ }
353
+
354
+ wrapper.vm.switchDay(event)
355
+
356
+ expect(wrapper.vm.day).toBe(1)
357
+ expect(event.preventDefault).toHaveBeenCalled()
358
+ })
359
+
360
+ it('should navigate right with arrow key', async () => {
361
+ wrapper = mount(Days, {
362
+ props: {
363
+ input: mockDaysInput,
364
+ regions: mockRegions,
365
+ geometryId: 2021,
366
+ selectedDay: 2,
367
+ language: 'fi',
368
+ },
369
+ })
370
+
371
+ // Mock querySelector to avoid DOM errors
372
+ wrapper.vm.$el.querySelector = vi.fn(() => ({
373
+ focus: vi.fn(),
374
+ }))
375
+
376
+ const event = {
377
+ keyCode: 39, // RIGHT
378
+ preventDefault: vi.fn(),
379
+ }
380
+
381
+ wrapper.vm.switchDay(event)
382
+
383
+ expect(wrapper.vm.day).toBe(3)
384
+ expect(event.preventDefault).toHaveBeenCalled()
385
+ })
386
+
387
+ it('should navigate to first day with Home key', async () => {
388
+ wrapper = mount(Days, {
389
+ props: {
390
+ input: mockDaysInput,
391
+ regions: mockRegions,
392
+ geometryId: 2021,
393
+ selectedDay: 3,
394
+ language: 'fi',
395
+ },
396
+ })
397
+
398
+ // Mock querySelector to avoid DOM errors
399
+ wrapper.vm.$el.querySelector = vi.fn(() => ({
400
+ focus: vi.fn(),
401
+ }))
402
+
403
+ const event = {
404
+ keyCode: 36, // HOME
405
+ preventDefault: vi.fn(),
406
+ }
407
+
408
+ wrapper.vm.switchDay(event)
409
+
410
+ expect(wrapper.vm.day).toBe(0)
411
+ expect(event.preventDefault).toHaveBeenCalled()
412
+ })
413
+
414
+ it('should navigate to last day with End key', async () => {
415
+ wrapper = mount(Days, {
416
+ props: {
417
+ input: mockDaysInput,
418
+ regions: mockRegions,
419
+ geometryId: 2021,
420
+ selectedDay: 1,
421
+ language: 'fi',
422
+ },
423
+ })
424
+
425
+ // Mock querySelector to avoid DOM errors
426
+ wrapper.vm.$el.querySelector = vi.fn(() => ({
427
+ focus: vi.fn(),
428
+ }))
429
+
430
+ const event = {
431
+ keyCode: 35, // END
432
+ preventDefault: vi.fn(),
433
+ }
434
+
435
+ wrapper.vm.switchDay(event)
436
+
437
+ expect(wrapper.vm.day).toBe(4)
438
+ expect(event.preventDefault).toHaveBeenCalled()
439
+ })
440
+
441
+ it('should not go below day 0 when navigating left', () => {
442
+ wrapper = mount(Days, {
443
+ props: {
444
+ input: mockDaysInput,
445
+ regions: mockRegions,
446
+ geometryId: 2021,
447
+ selectedDay: 0,
448
+ language: 'fi',
449
+ },
450
+ })
451
+
452
+ // Mock querySelector to avoid DOM errors
453
+ wrapper.vm.$el.querySelector = vi.fn(() => ({
454
+ focus: vi.fn(),
455
+ }))
456
+
457
+ const event = {
458
+ keyCode: 37, // LEFT
459
+ preventDefault: vi.fn(),
460
+ }
461
+
462
+ wrapper.vm.switchDay(event)
463
+
464
+ expect(wrapper.vm.day).toBe(0)
465
+ })
466
+
467
+ it('should not go above day 4 when navigating right', () => {
468
+ wrapper = mount(Days, {
469
+ props: {
470
+ input: mockDaysInput,
471
+ regions: mockRegions,
472
+ geometryId: 2021,
473
+ selectedDay: 4,
474
+ language: 'fi',
475
+ },
476
+ })
477
+
478
+ // Mock querySelector to avoid DOM errors
479
+ wrapper.vm.$el.querySelector = vi.fn(() => ({
480
+ focus: vi.fn(),
481
+ }))
482
+
483
+ const event = {
484
+ keyCode: 39, // RIGHT
485
+ preventDefault: vi.fn(),
486
+ }
487
+
488
+ wrapper.vm.switchDay(event)
489
+
490
+ expect(wrapper.vm.day).toBe(4)
491
+ })
492
+ })
493
+
494
+ describe('Event handling', () => {
495
+ it('should emit loaded event when child emits', () => {
496
+ wrapper = mount(Days, {
497
+ props: {
498
+ input: mockDaysInput,
499
+ regions: mockRegions,
500
+ geometryId: 2021,
501
+ language: 'fi',
502
+ },
503
+ })
504
+
505
+ wrapper.vm.onLoaded(true)
506
+
507
+ expect(wrapper.emitted('loaded')).toBeTruthy()
508
+ expect(wrapper.emitted('loaded')[0]).toEqual([true])
509
+ })
510
+
511
+ it('should not emit loaded event when child emits false', () => {
512
+ wrapper = mount(Days, {
513
+ props: {
514
+ input: mockDaysInput,
515
+ regions: mockRegions,
516
+ geometryId: 2021,
517
+ language: 'fi',
518
+ },
519
+ })
520
+
521
+ wrapper.vm.onLoaded(false)
522
+
523
+ expect(wrapper.emitted('loaded')).toBeFalsy()
524
+ })
525
+ })
526
+
527
+ describe('Theme support', () => {
528
+ it('should apply theme class', () => {
529
+ wrapper = mount(Days, {
530
+ props: {
531
+ input: mockDaysInput,
532
+ regions: mockRegions,
533
+ geometryId: 2021,
534
+ theme: 'dark-theme',
535
+ language: 'fi',
536
+ },
537
+ })
538
+
539
+ expect(wrapper.find('.date-selector').classes()).toContain('dark-theme')
540
+ })
541
+
542
+ it('should support all theme variants', () => {
543
+ const themes = [
544
+ 'light-theme',
545
+ 'dark-theme',
546
+ 'light-gray-theme',
547
+ 'dark-gray-theme',
548
+ ]
549
+
550
+ themes.forEach((theme) => {
551
+ wrapper = mount(Days, {
552
+ props: {
553
+ input: mockDaysInput,
554
+ regions: mockRegions,
555
+ geometryId: 2021,
556
+ theme,
557
+ language: 'fi',
558
+ },
559
+ })
560
+
561
+ expect(wrapper.find('.date-selector').classes()).toContain(theme)
562
+ })
563
+ })
564
+ })
565
+ })