@smartimpact-it/modern-video-embed 2.0.5 → 2.0.7

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 (65) hide show
  1. package/README.md +322 -71
  2. package/dist/components/BaseVideoEmbed.d.ts +91 -0
  3. package/dist/components/BaseVideoEmbed.d.ts.map +1 -0
  4. package/dist/components/BaseVideoEmbed.js +275 -0
  5. package/dist/components/BaseVideoEmbed.js.map +1 -0
  6. package/dist/components/VideoEmbed.d.ts +68 -0
  7. package/dist/components/VideoEmbed.d.ts.map +1 -0
  8. package/dist/components/VideoEmbed.js +786 -0
  9. package/dist/components/VideoEmbed.js.map +1 -0
  10. package/dist/components/VimeoEmbed.d.ts +26 -36
  11. package/dist/components/VimeoEmbed.d.ts.map +1 -1
  12. package/dist/components/VimeoEmbed.js +231 -326
  13. package/dist/components/VimeoEmbed.js.map +1 -1
  14. package/dist/components/VimeoEmbed.min.js +1 -1
  15. package/dist/components/YouTubeEmbed.d.ts +108 -42
  16. package/dist/components/YouTubeEmbed.d.ts.map +1 -1
  17. package/dist/components/YouTubeEmbed.js +361 -375
  18. package/dist/components/YouTubeEmbed.js.map +1 -1
  19. package/dist/components/YouTubeEmbed.min.js +1 -1
  20. package/dist/css/components.css +285 -68
  21. package/dist/css/components.css.map +1 -1
  22. package/dist/css/components.min.css +1 -1
  23. package/dist/css/main.css +285 -68
  24. package/dist/css/main.css.map +1 -1
  25. package/dist/css/main.min.css +1 -1
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +1 -0
  29. package/dist/index.js.map +1 -1
  30. package/dist/index.min.js +1 -1
  31. package/dist/types/index.d.ts +1 -0
  32. package/dist/types/index.d.ts.map +1 -1
  33. package/dist/video-only.d.ts +7 -0
  34. package/dist/video-only.d.ts.map +1 -0
  35. package/dist/video-only.js +8 -0
  36. package/dist/video-only.js.map +1 -0
  37. package/dist/vimeo-only.d.ts +2 -2
  38. package/dist/vimeo-only.d.ts.map +1 -1
  39. package/dist/vimeo-only.js +2 -2
  40. package/dist/vimeo-only.js.map +1 -1
  41. package/dist/vimeo-only.min.js +1 -1
  42. package/dist/youtube-only.d.ts +2 -2
  43. package/dist/youtube-only.d.ts.map +1 -1
  44. package/dist/youtube-only.js +2 -2
  45. package/dist/youtube-only.js.map +1 -1
  46. package/dist/youtube-only.min.js +1 -1
  47. package/package.json +6 -5
  48. package/src/components/BaseVideoEmbed.ts +335 -0
  49. package/src/components/VideoEmbed.ts +870 -0
  50. package/src/components/VideoEmbed.ts.backup +1051 -0
  51. package/src/components/VimeoEmbed.ts +258 -395
  52. package/src/components/YouTubeEmbed.ts +378 -432
  53. package/src/index.ts +1 -0
  54. package/src/styles/_embed-base.scss +275 -0
  55. package/src/styles/_shared-functions.scss +56 -0
  56. package/src/styles/components.scss +4 -3
  57. package/src/styles/main.scss +7 -5
  58. package/src/styles/video-embed.scss +55 -0
  59. package/src/styles/vimeo-embed.scss +8 -248
  60. package/src/styles/youtube-embed.scss +8 -254
  61. package/src/types/index.ts +1 -0
  62. package/src/types/video-embed.d.ts +90 -0
  63. package/src/video-only.ts +9 -0
  64. package/src/vimeo-only.ts +2 -2
  65. package/src/youtube-only.ts +2 -2
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  // Main entry point for Video Embed Components
2
2
  import "./components/YouTubeEmbed.js";
3
3
  import "./components/VimeoEmbed.js";
4
+ import "./components/VideoEmbed.js";
@@ -0,0 +1,275 @@
1
+ /**
2
+ * Base styles and mixins for all video embed components
3
+ * Provides reusable CSS patterns to reduce duplication
4
+ */
5
+
6
+ @use "shared-functions" as *;
7
+
8
+ // CSS Variables (common across all components)
9
+ // All variables are prefixed with --si-embed- to prevent conflicts
10
+ @mixin embed-css-variables {
11
+ --si-embed-video-aspect-ratio: 56.25%; // Default 16:9
12
+ --si-embed-aspect-width: 16; // For dynamic aspect ratio calculations
13
+ --si-embed-aspect-height: 9; // For dynamic aspect ratio calculations
14
+ --si-embed-poster-object-fit: cover;
15
+ --si-embed-video-object-fit: contain;
16
+ --si-embed-control-button-size: 70px;
17
+ --si-embed-control-button-color: #ffffff;
18
+ --si-embed-overlay-background-color: rgba(0, 0, 0, 0.5);
19
+ }
20
+
21
+ // Base container styles
22
+ @mixin embed-container-base {
23
+ display: block;
24
+ position: relative;
25
+ width: 100%;
26
+ padding-top: var(--si-embed-video-aspect-ratio);
27
+ overflow: hidden;
28
+ background-color: #000;
29
+ }
30
+
31
+ // Media element positioning (iframe or video)
32
+ @mixin embed-media-base {
33
+ position: absolute;
34
+ top: 0;
35
+ left: 0;
36
+ width: 100%;
37
+ height: 100%;
38
+ border: none;
39
+ object-fit: var(--si-embed-video-object-fit);
40
+ }
41
+
42
+ // Poster image styles
43
+ @mixin embed-poster {
44
+ .video-poster,
45
+ .youtube-poster,
46
+ .vimeo-poster {
47
+ position: absolute;
48
+ top: 0;
49
+ left: 0;
50
+ width: 100%;
51
+ height: 100%;
52
+ object-fit: var(--si-embed-poster-object-fit);
53
+ display: block;
54
+ cursor: pointer;
55
+ }
56
+ }
57
+
58
+ // Custom control button overlay
59
+ @mixin embed-button-overlay {
60
+ .button-overlay {
61
+ background: var(--si-embed-overlay-background-color);
62
+ cursor: pointer;
63
+ display: flex;
64
+ align-items: center;
65
+ justify-content: center;
66
+ position: absolute;
67
+ left: 0;
68
+ top: 0;
69
+ right: 0;
70
+ bottom: 0;
71
+ height: 100%;
72
+ width: 100%;
73
+ transition: opacity 0.3s ease;
74
+ z-index: 10;
75
+ }
76
+
77
+ &.is-playing .button-overlay {
78
+ opacity: 0;
79
+ pointer-events: none;
80
+ }
81
+
82
+ &.is-playing:hover .button-overlay {
83
+ opacity: 1;
84
+ pointer-events: auto;
85
+ }
86
+
87
+ .button-overlay .button {
88
+ cursor: pointer;
89
+ display: flex;
90
+ align-items: center;
91
+ justify-content: center;
92
+ height: var(--si-embed-control-button-size);
93
+ width: var(--si-embed-control-button-size);
94
+ background-image: inline-svg(
95
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#ffffff" d="M8,5.14V19.14L19,12.14L8,5.14Z" /></svg>'
96
+ );
97
+ background-size: cover;
98
+ background-repeat: no-repeat;
99
+ background-position: center;
100
+ }
101
+
102
+ &.is-playing .button-overlay .button {
103
+ background-image: inline-svg(
104
+ '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#ffffff" d="M14,19H18V5H14M6,19H10V5H6V19Z" /></svg>'
105
+ );
106
+ }
107
+ }
108
+
109
+ // Background mode styles (for background video effect)
110
+ @mixin embed-background-mode($media-type: "iframe") {
111
+ &.is-background {
112
+ padding-top: 0;
113
+ position: absolute;
114
+ height: 100%;
115
+ width: 100%;
116
+ top: 0;
117
+ left: 0;
118
+ z-index: 0;
119
+ overflow: hidden;
120
+ background: #000;
121
+
122
+ @if $media-type == "iframe" {
123
+ iframe {
124
+ position: absolute;
125
+ top: 50%;
126
+ left: 50%;
127
+ transform: translate(-50%, -50%);
128
+ // Use aspect-ratio with min-dimensions to ensure coverage
129
+ aspect-ratio: var(--si-embed-aspect-width) /
130
+ var(--si-embed-aspect-height);
131
+ // Start with auto sizing based on aspect ratio
132
+ width: auto;
133
+ height: auto;
134
+ // Ensure iframe covers entire container
135
+ min-width: 100%;
136
+ min-height: 100%;
137
+ pointer-events: none;
138
+ }
139
+ } @else if $media-type == "video" {
140
+ video {
141
+ position: absolute;
142
+ top: 50%;
143
+ left: 50%;
144
+ transform: translate(-50%, -50%);
145
+ // Use CSS custom properties for dynamic aspect ratio
146
+ width: 100%;
147
+ height: calc(
148
+ (100% * var(--si-embed-aspect-height)) / var(--si-embed-aspect-width)
149
+ );
150
+ min-width: 100%;
151
+ min-height: 100%;
152
+ object-fit: cover;
153
+ pointer-events: none;
154
+
155
+ // Hide native media controls in background mode
156
+ &::-webkit-media-controls {
157
+ display: none !important;
158
+ }
159
+ &::-webkit-media-controls-enclosure {
160
+ display: none !important;
161
+ }
162
+ }
163
+ }
164
+
165
+ .button-overlay {
166
+ display: none;
167
+ }
168
+ }
169
+
170
+ // When background controls are enabled
171
+ &.has-background-controls {
172
+ .button-overlay {
173
+ display: flex;
174
+ background: none;
175
+ justify-content: flex-end;
176
+ align-items: flex-end;
177
+ padding: 1rem;
178
+
179
+ .button {
180
+ --si-embed-control-button-size: 40px;
181
+ opacity: 0.7;
182
+ transition: opacity 0.2s ease;
183
+ &:hover {
184
+ opacity: 1;
185
+ }
186
+ }
187
+ }
188
+
189
+ &.is-playing .button-overlay {
190
+ opacity: 0;
191
+ &:hover {
192
+ opacity: 1;
193
+ }
194
+ }
195
+ }
196
+ }
197
+
198
+ // Error message display
199
+ @mixin embed-error-message(
200
+ $error-class,
201
+ $accent-color: #ff4444,
202
+ $hover-color: #cc0000
203
+ ) {
204
+ .#{$error-class} {
205
+ position: absolute;
206
+ top: 0;
207
+ left: 0;
208
+ width: 100%;
209
+ height: 100%;
210
+ display: flex;
211
+ align-items: center;
212
+ justify-content: center;
213
+ background: rgba(0, 0, 0, 0.9);
214
+ color: white;
215
+ z-index: 1000;
216
+
217
+ .error-content {
218
+ text-align: center;
219
+ padding: 2rem;
220
+ max-width: 400px;
221
+
222
+ svg {
223
+ color: $accent-color;
224
+ margin-bottom: 1rem;
225
+ }
226
+
227
+ .error-message {
228
+ margin: 1rem 0;
229
+ font-size: 1rem;
230
+ line-height: 1.5;
231
+ }
232
+
233
+ .retry-button {
234
+ margin-top: 1rem;
235
+ padding: 0.75rem 1.5rem;
236
+ background: $accent-color;
237
+ color: white;
238
+ border: none;
239
+ border-radius: 4px;
240
+ cursor: pointer;
241
+ font-size: 1rem;
242
+ font-weight: 600;
243
+ transition: background 0.2s ease;
244
+
245
+ &:hover {
246
+ background: $hover-color;
247
+ }
248
+
249
+ &:focus {
250
+ outline: 2px solid white;
251
+ outline-offset: 2px;
252
+ }
253
+ }
254
+ }
255
+ }
256
+ }
257
+
258
+ // Complete base mixin that includes all common styles
259
+ @mixin embed-base-styles(
260
+ $component-name,
261
+ $media-type: "iframe",
262
+ $accent-color: #ff4444,
263
+ $hover-color: #cc0000
264
+ ) {
265
+ @include embed-css-variables;
266
+ @include embed-container-base;
267
+ @include embed-poster;
268
+ @include embed-button-overlay;
269
+ @include embed-background-mode($media-type);
270
+ @include embed-error-message(
271
+ "#{$component-name}-error-message",
272
+ $accent-color,
273
+ $hover-color
274
+ );
275
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Shared SCSS functions for SVG encoding
3
+ * Used by all video embed components
4
+ */
5
+
6
+ // No imports needed - this is a base module
7
+
8
+ @function svg-str-replace($string, $search, $replace: "") {
9
+ $index: str-index($string, $search);
10
+ @if $index {
11
+ @return str-slice($string, 1, $index - 1) + $replace +
12
+ svg-str-replace(
13
+ str-slice($string, $index + str-length($search)),
14
+ $search,
15
+ $replace
16
+ );
17
+ }
18
+ @return $string;
19
+ }
20
+
21
+ @function svg-url-encode($string) {
22
+ $map: (
23
+ "%": "%25",
24
+ "<": "%3C",
25
+ ">": "%3E",
26
+ " ": "%20",
27
+ "!": "%21",
28
+ "*": "%2A",
29
+ "'": "%27",
30
+ '"': "%22",
31
+ "(": "%28",
32
+ ")": "%29",
33
+ ";": "%3B",
34
+ ":": "%3A",
35
+ "@": "%40",
36
+ "&": "%26",
37
+ "=": "%3D",
38
+ "+": "%2B",
39
+ "$": "%24",
40
+ ",": "%2C",
41
+ "/": "%2F",
42
+ "?": "%3F",
43
+ "#": "%23",
44
+ "[": "%5B",
45
+ "]": "%5D",
46
+ );
47
+ $new: $string;
48
+ @each $search, $replace in $map {
49
+ $new: #{svg-str-replace($new, $search, $replace)};
50
+ }
51
+ @return $new;
52
+ }
53
+
54
+ @function inline-svg($string) {
55
+ @return url("data:image/svg+xml;utf8,#{svg-url-encode($string)}");
56
+ }
@@ -2,6 +2,7 @@
2
2
  // This file contains only the essential styles needed for the video embed components
3
3
  // Use this when integrating into your website
4
4
 
5
- // Import our custom video embed styles
6
- @import "./youtube-embed.scss";
7
- @import "./vimeo-embed.scss";
5
+ // Use our custom video embed styles
6
+ @use "./youtube-embed.scss";
7
+ @use "./vimeo-embed.scss";
8
+ @use "./video-embed.scss";
@@ -2,9 +2,11 @@
2
2
  // This file includes Bootstrap and component styles for demos and examples
3
3
  // For production integration, use components.scss instead
4
4
 
5
- // Import Bootstrap SCSS (only needed for demos)
6
- @import "../../node_modules/bootstrap/scss/bootstrap";
5
+ // Use Bootstrap SCSS (only needed for demos)
6
+ // Using 'as *' to maintain global namespace compatibility
7
+ @use "../../node_modules/bootstrap/scss/bootstrap" as *;
7
8
 
8
- // Import our custom video embed styles
9
- @import "./youtube-embed.scss";
10
- @import "./vimeo-embed.scss";
9
+ // Use our custom video embed styles
10
+ @use "./youtube-embed.scss";
11
+ @use "./vimeo-embed.scss";
12
+ @use "./video-embed.scss";
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Video Embed Component Styles
3
+ * Uses shared base styles with video-specific overrides
4
+ */
5
+ @use "embed-base" as *;
6
+
7
+ video-embed {
8
+ // Import all common embed styles
9
+ @include embed-base-styles("video", "video", #ff4444, #cc0000);
10
+
11
+ // Video-specific styles only
12
+ video {
13
+ @include embed-media-base;
14
+
15
+ // Hide native controls when controls attribute is not set
16
+ &::-webkit-media-controls {
17
+ display: none !important;
18
+ }
19
+ &::-webkit-media-controls-enclosure {
20
+ display: none !important;
21
+ }
22
+ }
23
+
24
+ // Show native controls only when explicitly enabled
25
+ &[controls] video {
26
+ &::-webkit-media-controls {
27
+ display: flex !important;
28
+ }
29
+ &::-webkit-media-controls-enclosure {
30
+ display: flex !important;
31
+ }
32
+ }
33
+
34
+ // Poster image specific to video (uses background-image pattern)
35
+ .video-poster {
36
+ background-size: cover;
37
+ background-position: center;
38
+ background-repeat: no-repeat;
39
+ }
40
+
41
+ // Fullscreen mode (video-specific)
42
+ &.is-fullscreen {
43
+ padding-top: 0;
44
+ position: fixed;
45
+ top: 0;
46
+ left: 0;
47
+ width: 100vw;
48
+ height: 100vh;
49
+ z-index: 9999;
50
+
51
+ video {
52
+ object-fit: contain;
53
+ }
54
+ }
55
+ }
@@ -1,255 +1,15 @@
1
1
  /**
2
- * SVG encoding functions (shared with YouTube embed)
3
- */
4
- @function svg-str-replace($string, $search, $replace: "") {
5
- $index: str-index($string, $search);
6
- @if $index {
7
- @return str-slice($string, 1, $index - 1) + $replace +
8
- svg-str-replace(
9
- str-slice($string, $index + str-length($search)),
10
- $search,
11
- $replace
12
- );
13
- }
14
- @return $string;
15
- }
16
-
17
- @function svg-url-encode($string) {
18
- $map: (
19
- "%": "%25",
20
- "<": "%3C",
21
- ">": "%3E",
22
- " ": "%20",
23
- "!": "%21",
24
- "*": "%2A",
25
- "'": "%27",
26
- '"': "%22",
27
- "(": "%28",
28
- ")": "%29",
29
- ";": "%3B",
30
- ":": "%3A",
31
- "@": "%40",
32
- "&": "%26",
33
- "=": "%3D",
34
- "+": "%2B",
35
- "$": "%24",
36
- ",": "%2C",
37
- "/": "%2F",
38
- "?": "%3F",
39
- "#": "%23",
40
- "[": "%5B",
41
- "]": "%5D",
42
- );
43
- $new: $string;
44
- @each $search, $replace in $map {
45
- $new: #{svg-str-replace($new, $search, $replace)};
46
- }
47
- @return $new;
48
- }
49
-
50
- @function inline-svg($string) {
51
- @return url("data:image/svg+xml;utf8,#{svg-url-encode($string)}");
52
- }
2
+ * Vimeo Embed Component Styles
3
+ * Uses shared base styles with Vimeo-specific branding
4
+ */
5
+ @use "embed-base" as *;
53
6
 
54
7
  vimeo-embed {
55
- // CSS Variables for customization
56
- --video-aspect-ratio: 56.25%; // Default 16:9
57
- --poster-object-fit: cover;
58
- --video-object-fit: contain;
59
- --control-button-size: 70px;
60
- --control-button-color: #ffffff;
61
- --overlay-background-color: rgba(0, 0, 0, 0.5);
62
-
63
- display: block;
64
- position: relative;
65
- width: 100%;
66
- padding-top: var(--video-aspect-ratio);
67
- overflow: hidden;
68
- background-color: #000;
8
+ // Import all common embed styles with Vimeo blue accent
9
+ @include embed-base-styles("vimeo", "iframe", #00adef, #0088bf);
69
10
 
11
+ // iframe-specific styles
70
12
  iframe {
71
- position: absolute;
72
- top: 0;
73
- left: 0;
74
- width: 100%;
75
- height: 100%;
76
- border: none;
77
- object-fit: var(--video-object-fit);
78
- }
79
-
80
- // Poster image
81
- .vimeo-poster,
82
- .video-poster {
83
- position: absolute;
84
- top: 0;
85
- left: 0;
86
- width: 100%;
87
- height: 100%;
88
- object-fit: var(--poster-object-fit);
89
- display: block;
90
- cursor: pointer;
91
- }
92
-
93
- // Custom control overlay
94
- .button-overlay {
95
- background: var(--overlay-background-color);
96
- cursor: pointer;
97
- display: flex;
98
- align-items: center;
99
- justify-content: center;
100
- position: absolute;
101
- left: 0;
102
- top: 0;
103
- right: 0;
104
- bottom: 0;
105
- height: 100%;
106
- width: 100%;
107
- transition: opacity 0.3s ease;
108
- z-index: 10;
109
- }
110
-
111
- &.is-playing .button-overlay {
112
- opacity: 0;
113
- }
114
-
115
- &.is-playing:hover .button-overlay {
116
- opacity: 1;
117
- }
118
-
119
- .button-overlay .button {
120
- cursor: pointer;
121
- display: flex;
122
- align-items: center;
123
- justify-content: center;
124
- height: var(--control-button-size);
125
- width: var(--control-button-size);
126
- background-image: inline-svg(
127
- '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#ffffff" d="M8,5.14V19.14L19,12.14L8,5.14Z" /></svg>'
128
- );
129
- background-size: cover;
130
- background-repeat: no-repeat;
131
- background-position: center;
132
- }
133
-
134
- &.is-playing .button-overlay .button {
135
- background-image: inline-svg(
136
- '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#ffffff" d="M14,19H18V5H14M6,19H10V5H6V19Z" /></svg>'
137
- );
138
- }
139
-
140
- // Background mode styles
141
- &.is-background {
142
- padding-top: 0;
143
- position: absolute;
144
- height: 100%;
145
- width: 100%;
146
- top: 0;
147
- left: 0;
148
- z-index: 0;
149
- overflow: hidden;
150
- background: #000;
151
-
152
- iframe {
153
- position: absolute;
154
- top: 50%;
155
- left: 50%;
156
- transform: translate(-50%, -50%);
157
-
158
- min-width: 100%;
159
- min-height: 100%;
160
- width: auto;
161
- height: auto;
162
-
163
- @supports (aspect-ratio: 16 / 9) {
164
- aspect-ratio: 16 / 9;
165
- }
166
-
167
- pointer-events: none;
168
- }
169
-
170
- .button-overlay {
171
- display: none;
172
- }
173
- }
174
-
175
- // When background controls are enabled
176
- &.has-background-controls {
177
- .button-overlay {
178
- display: flex;
179
- background: none;
180
- justify-content: flex-end;
181
- align-items: flex-end;
182
- padding: 1rem;
183
-
184
- .button {
185
- --control-button-size: 40px;
186
- opacity: 0.7;
187
- transition: opacity 0.2s ease;
188
- &:hover {
189
- opacity: 1;
190
- }
191
- }
192
- }
193
-
194
- &.is-playing .button-overlay {
195
- opacity: 0;
196
- &:hover {
197
- opacity: 1;
198
- }
199
- }
200
- }
201
-
202
- // Error message styling
203
- .vimeo-error-message {
204
- position: absolute;
205
- top: 0;
206
- left: 0;
207
- width: 100%;
208
- height: 100%;
209
- display: flex;
210
- align-items: center;
211
- justify-content: center;
212
- background: rgba(0, 0, 0, 0.9);
213
- color: white;
214
- z-index: 1000;
215
-
216
- .error-content {
217
- text-align: center;
218
- padding: 2rem;
219
- max-width: 400px;
220
-
221
- svg {
222
- color: #00adef;
223
- margin-bottom: 1rem;
224
- }
225
-
226
- .error-message {
227
- margin: 1rem 0;
228
- font-size: 1rem;
229
- line-height: 1.5;
230
- }
231
-
232
- .retry-button {
233
- margin-top: 1rem;
234
- padding: 0.75rem 1.5rem;
235
- background: #00adef;
236
- color: white;
237
- border: none;
238
- border-radius: 4px;
239
- cursor: pointer;
240
- font-size: 1rem;
241
- font-weight: 600;
242
- transition: background 0.2s ease;
243
-
244
- &:hover {
245
- background: #0088bf;
246
- }
247
-
248
- &:focus {
249
- outline: 2px solid white;
250
- outline-offset: 2px;
251
- }
252
- }
253
- }
13
+ @include embed-media-base;
254
14
  }
255
15
  }