hyperspeed 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f45c0446779ae1fd9f3ac53ba634bf46215d58011619b38e841e6e85dfe9e6e6
4
- data.tar.gz: 1552907e6d1669c79dec47ca1e400bafaa5c7f84e1ce1b5be740a4b459b6a8d5
3
+ metadata.gz: 187d4560cebc7a585cabcbef91fc6e719c7c8975e1f2d0bf83c1895c35968806
4
+ data.tar.gz: 4fabc7ff43179d231d5fb784161f692d29ec5bb4cae64266136c9874df7f601d
5
5
  SHA512:
6
- metadata.gz: f9a845e89568e244b92d4705539d79fefb1fd1d2ef910257c9c1f56e88252e4573e1a7037f99e814e21f60f2f1a1720a1322181e09aa3c801bc1b0a7e259b9dc
7
- data.tar.gz: 2a091758dac68b305ae8f114d360dc9ac7dd153239a83fe62b9a9bd0c87066192f370cac67b6bf2f80040e6d97599220ea466fe79ef1b738088b4db9b4f5605d
6
+ metadata.gz: 9d8d8e0885f33f9843c8f6ae44be89439f65526f7906d7a5d5995ed78b1aeeebf080e0ad7f64d15ba7d22a9c5ce64dbc1f5050e835c878bd114da5aa9825d677
7
+ data.tar.gz: 90e4cbdda1dd895f02f5ab93bd419439892196528675d340e93c1af403efc25e49ea36179d6ad0f76d28135d26ded43caecca9889a4c97ba57e37f83411c4270
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Hyperspeed
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/hyperspeed`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ ## For writing Hypertext at speed in Ruby
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ Unlike other Ruby interfaces for generating HTML, `hyperspeed` works with a simple Hash abstract syntax tree until the very last moment, allowing you to write helpers and objects that transform and compose your HTML partials. `hyperspeed` also provides a terse and lightweight Ruby DSL for writing your HTML (whether partials, components, or fragments).
6
6
 
7
7
  ## Installation
8
8
 
@@ -22,7 +22,62 @@ Or install it yourself as:
22
22
 
23
23
  ## Usage
24
24
 
25
- TODO: Write usage instructions here
25
+ If you've ever been frustrated by ERB or HAML's inability to easily compose or transform your partials/components, then `hyperspeed` is likely for you. Or, if you've simply wanted to write your markup in pure Ruby with the simplest possible methods, `hyperspeed` is here to help.
26
+
27
+ The `Hyperspeed` module provides only two methods: `define` and `render`. You may pass a block to either, in which you write HTML as if it were pure Ruby:
28
+
29
+ ```ruby
30
+ Hyperspeed.render do
31
+ form([
32
+ input({ type: 'text' }),
33
+ button({ type: 'submit' }, 'Greet'),
34
+ output
35
+ ])
36
+ end
37
+
38
+ # => "<form><input type=\"text\"></input><button type=\"submit\">Greet</button><output></output></form>"
39
+ ```
40
+
41
+ The `Hyperspeed.define` method accepts a block and will return a Hash AST representing your markup:
42
+
43
+ ```ruby
44
+ Hyperspeed.define do
45
+ form([
46
+ input({ type: 'text' }),
47
+ button({ type: 'submit' }, 'Greet'),
48
+ output
49
+ ])
50
+ end
51
+
52
+ # => {
53
+ # type: :ELEMENT,
54
+ # tag: :form,
55
+ # children: [
56
+ # {
57
+ # type: :ELEMENT,
58
+ # tag: :input,
59
+ # properties: { type: "text" }
60
+ # },
61
+ # {
62
+ # type: :ELEMENT,
63
+ # tag: :button,
64
+ # properties: { type: "submit" },
65
+ # children: [
66
+ # {
67
+ # type: :TEXT,
68
+ # value: "Greet"
69
+ # }
70
+ # ]
71
+ # },
72
+ # {
73
+ # type: :ELEMENT,
74
+ # tag: :output
75
+ # }
76
+ # ]
77
+ # }
78
+ ```
79
+
80
+ The `Hyperspeed.render` method accepts either a block or such a Hash. If it receives a block, it will delegate that block to `Hyperspeed.define`, receive the AST Hash back, and then render that AST Hash to a string. If it receives an AST Hash directly, it will simply return your markup as a string.
26
81
 
27
82
  ## Development
28
83
 
@@ -1,8 +1,132 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'hyperspeed/version'
3
+ require_relative './hyperspeed/version'
4
4
 
5
+ # rubocop:disable Style/CommentedKeyword
5
6
  module Hyperspeed
6
7
  class Error < StandardError; end
7
- # Your code goes here...
8
+
9
+ def self.define(&block)
10
+ definition_proxy = DefinitionProxy.new
11
+ definition_proxy.evaluate(&block)
12
+ end
13
+
14
+ # rubocop:disable Metrics/AbcSize
15
+ def self.render(ast = nil) # &block
16
+ ast ||= define(&Proc.new)
17
+
18
+ if ast[:type] == :ELEMENT
19
+ tag_name = ast[:tag].to_s.downcase
20
+ # ensure the string we are reducing isn't frozen, so that we can modify it
21
+ content = ast[:children]&.reduce(+'') { |memo, child| memo << render(child) }
22
+
23
+ if ast[:properties]
24
+ properties = dasherize_nested_hash_keys(ast[:properties])
25
+ .map { |k, v| %(#{k}="#{meld(v)}") }
26
+ .join(' ')
27
+ %(<#{tag_name} #{properties}>#{content}</#{tag_name}>)
28
+ else
29
+ %(<#{tag_name}>#{content}</#{tag_name}>)
30
+ end
31
+ elsif ast[:type] == :TEXT
32
+ ast[:value].to_s
33
+ else
34
+ fail 'Root `type` must be either ELEMENT or TEXT!'
35
+ end
36
+ end
37
+ # rubocop:enable Metrics/AbcSize
38
+
39
+ class DefinitionProxy
40
+ def evaluate(&block)
41
+ @self_before_instance_eval = eval('self', block.binding, __FILE__, __LINE__)
42
+ instance_eval(&block)
43
+ end
44
+
45
+ # rubocop:disable Style/MethodMissingSuper
46
+ def method_missing(tag_or_method, *args)
47
+ if @self_before_instance_eval.respond_to?(tag_or_method, _include_private_methods = true)
48
+ @self_before_instance_eval.send(tag_or_method, *args)
49
+ else
50
+ build_ast(tag_or_method, *args)
51
+ end
52
+ end
53
+ # rubocop:enable Style/MethodMissingSuper
54
+
55
+ def respond_to_missing?(_tag_or_method, *_args)
56
+ true
57
+ end
58
+
59
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize
60
+ def build_ast(tag, properties_or_children_or_text = nil, children_or_text = nil)
61
+ definition = {
62
+ type: :ELEMENT,
63
+ tag: tag
64
+ }
65
+ if properties_or_children_or_text && children_or_text
66
+ if children_or_text.is_a?(Array)
67
+ unless properties_or_children_or_text.is_a?(Hash)
68
+ fail Error, 'when `children` are defined as second argument, first argument must be a `properties` Hash'
69
+ end
70
+
71
+ definition.merge(properties: properties_or_children_or_text,
72
+ children: children_or_text)
73
+ elsif children_or_text.is_a?(String)
74
+ unless properties_or_children_or_text.is_a?(Hash)
75
+ fail Error, 'when `text` is defined as second argument, first argument must be a `properties` Hash'
76
+ end
77
+
78
+ definition.merge(properties: properties_or_children_or_text,
79
+ children: [{
80
+ type: :TEXT,
81
+ value: children_or_text
82
+ }])
83
+ else
84
+ # rubocop:disable Layout/LineLength
85
+ fail Error, 'when first argument is a `properties` Hash, second argument must be either `Array` of `children` or a `String`'
86
+ # rubocop:enable Layout/LineLength
87
+ end
88
+ elsif properties_or_children_or_text.is_a?(Hash)
89
+ definition.merge(properties: properties_or_children_or_text)
90
+ elsif properties_or_children_or_text.is_a?(Array)
91
+ definition.merge(children: properties_or_children_or_text)
92
+ elsif properties_or_children_or_text.is_a?(String)
93
+ definition.merge(children: [{
94
+ type: :TEXT,
95
+ value: properties_or_children_or_text
96
+ }])
97
+ elsif properties_or_children_or_text.nil?
98
+ definition
99
+ else
100
+ fail Error, 'first argument must be a `properties` Hash, `children` Array, text `String`, or empty'
101
+ end
102
+ end
103
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize
104
+ end
105
+ end
106
+
107
+ def dasherize_nested_hash_keys(hash)
108
+ separator = '-'
109
+ hash.each_with_object({}) do |(key, value), output|
110
+ if value.is_a? Hash
111
+ dasherize_nested_hash_keys(value).each do |subkey, subvalue|
112
+ flat_key = [key, subkey].join(separator)
113
+ output[flat_key] = subvalue
114
+ end
115
+ elsif key.is_a? Array
116
+ flat_key = key.join(separator)
117
+ output[flat_key] = value
118
+ else
119
+ output[key] = value
120
+ end
121
+ end
122
+ end
123
+
124
+ def meld(*args)
125
+ args.flatten
126
+ .compact
127
+ .map(&:to_s)
128
+ .map(&:strip)
129
+ .uniq
130
+ .join(' ')
8
131
  end
132
+ # rubocop:enable Style/CommentedKeyword
@@ -0,0 +1,51 @@
1
+ [
2
+ "aria-activedescendant",
3
+ "aria-atomic",
4
+ "aria-autocomplete",
5
+ "aria-busy",
6
+ "aria-checked",
7
+ "aria-colcount",
8
+ "aria-colindex",
9
+ "aria-colspan",
10
+ "aria-controls",
11
+ "aria-current",
12
+ "aria-describedby",
13
+ "aria-details",
14
+ "aria-disabled",
15
+ "aria-dropeffect",
16
+ "aria-errormessage",
17
+ "aria-expanded",
18
+ "aria-flowto",
19
+ "aria-grabbed",
20
+ "aria-haspopup",
21
+ "aria-hidden",
22
+ "aria-invalid",
23
+ "aria-keyshortcuts",
24
+ "aria-label",
25
+ "aria-labelledby",
26
+ "aria-level",
27
+ "aria-live",
28
+ "aria-modal",
29
+ "aria-multiline",
30
+ "aria-multiselectable",
31
+ "aria-orientation",
32
+ "aria-owns",
33
+ "aria-placeholder",
34
+ "aria-posinset",
35
+ "aria-pressed",
36
+ "aria-readonly",
37
+ "aria-relevant",
38
+ "aria-required",
39
+ "aria-roledescription",
40
+ "aria-rowcount",
41
+ "aria-rowindex",
42
+ "aria-rowspan",
43
+ "aria-selected",
44
+ "aria-setsize",
45
+ "aria-sort",
46
+ "aria-valuemax",
47
+ "aria-valuemin",
48
+ "aria-valuenow",
49
+ "aria-valuetext",
50
+ "role"
51
+ ]
@@ -0,0 +1,600 @@
1
+ {
2
+ "*": [
3
+ "accesskey",
4
+ "autocapitalize",
5
+ "autofocus",
6
+ "class",
7
+ "contenteditable",
8
+ "dir",
9
+ "draggable",
10
+ "enterkeyhint",
11
+ "hidden",
12
+ "id",
13
+ "inputmode",
14
+ "is",
15
+ "itemid",
16
+ "itemprop",
17
+ "itemref",
18
+ "itemscope",
19
+ "itemtype",
20
+ "lang",
21
+ "nonce",
22
+ "slot",
23
+ "spellcheck",
24
+ "style",
25
+ "tabindex",
26
+ "title",
27
+ "translate"
28
+ ],
29
+ "a": [
30
+ "accesskey",
31
+ "charset",
32
+ "coords",
33
+ "download",
34
+ "href",
35
+ "hreflang",
36
+ "name",
37
+ "ping",
38
+ "referrerpolicy",
39
+ "rel",
40
+ "rev",
41
+ "shape",
42
+ "tabindex",
43
+ "target",
44
+ "type"
45
+ ],
46
+ "abbr": [
47
+ "title"
48
+ ],
49
+ "applet": [
50
+ "align",
51
+ "alt",
52
+ "archive",
53
+ "code",
54
+ "codebase",
55
+ "height",
56
+ "hspace",
57
+ "name",
58
+ "object",
59
+ "vspace",
60
+ "width"
61
+ ],
62
+ "area": [
63
+ "accesskey",
64
+ "alt",
65
+ "coords",
66
+ "download",
67
+ "href",
68
+ "hreflang",
69
+ "nohref",
70
+ "ping",
71
+ "referrerpolicy",
72
+ "rel",
73
+ "shape",
74
+ "tabindex",
75
+ "target",
76
+ "type"
77
+ ],
78
+ "audio": [
79
+ "autoplay",
80
+ "controls",
81
+ "crossorigin",
82
+ "loop",
83
+ "muted",
84
+ "preload",
85
+ "src"
86
+ ],
87
+ "base": [
88
+ "href",
89
+ "target"
90
+ ],
91
+ "basefont": [
92
+ "color",
93
+ "face",
94
+ "size"
95
+ ],
96
+ "bdo": [
97
+ "dir"
98
+ ],
99
+ "blockquote": [
100
+ "cite"
101
+ ],
102
+ "body": [
103
+ "alink",
104
+ "background",
105
+ "bgcolor",
106
+ "link",
107
+ "text",
108
+ "vlink"
109
+ ],
110
+ "br": [
111
+ "clear"
112
+ ],
113
+ "button": [
114
+ "accesskey",
115
+ "autofocus",
116
+ "disabled",
117
+ "form",
118
+ "formaction",
119
+ "formenctype",
120
+ "formmethod",
121
+ "formnovalidate",
122
+ "formtarget",
123
+ "name",
124
+ "tabindex",
125
+ "type",
126
+ "value"
127
+ ],
128
+ "canvas": [
129
+ "height",
130
+ "width"
131
+ ],
132
+ "caption": [
133
+ "align"
134
+ ],
135
+ "col": [
136
+ "align",
137
+ "char",
138
+ "charoff",
139
+ "span",
140
+ "valign",
141
+ "width"
142
+ ],
143
+ "colgroup": [
144
+ "align",
145
+ "char",
146
+ "charoff",
147
+ "span",
148
+ "valign",
149
+ "width"
150
+ ],
151
+ "data": [
152
+ "value"
153
+ ],
154
+ "del": [
155
+ "cite",
156
+ "datetime"
157
+ ],
158
+ "details": [
159
+ "open"
160
+ ],
161
+ "dfn": [
162
+ "title"
163
+ ],
164
+ "dialog": [
165
+ "open"
166
+ ],
167
+ "dir": [
168
+ "compact"
169
+ ],
170
+ "div": [
171
+ "align"
172
+ ],
173
+ "dl": [
174
+ "compact"
175
+ ],
176
+ "embed": [
177
+ "height",
178
+ "src",
179
+ "type",
180
+ "width"
181
+ ],
182
+ "fieldset": [
183
+ "disabled",
184
+ "form",
185
+ "name"
186
+ ],
187
+ "font": [
188
+ "color",
189
+ "face",
190
+ "size"
191
+ ],
192
+ "form": [
193
+ "accept",
194
+ "accept-charset",
195
+ "action",
196
+ "autocomplete",
197
+ "enctype",
198
+ "method",
199
+ "name",
200
+ "novalidate",
201
+ "target"
202
+ ],
203
+ "frame": [
204
+ "frameborder",
205
+ "longdesc",
206
+ "marginheight",
207
+ "marginwidth",
208
+ "name",
209
+ "noresize",
210
+ "scrolling",
211
+ "src"
212
+ ],
213
+ "frameset": [
214
+ "cols",
215
+ "rows"
216
+ ],
217
+ "h1": [
218
+ "align"
219
+ ],
220
+ "h2": [
221
+ "align"
222
+ ],
223
+ "h3": [
224
+ "align"
225
+ ],
226
+ "h4": [
227
+ "align"
228
+ ],
229
+ "h5": [
230
+ "align"
231
+ ],
232
+ "h6": [
233
+ "align"
234
+ ],
235
+ "head": [
236
+ "profile"
237
+ ],
238
+ "hr": [
239
+ "align",
240
+ "noshade",
241
+ "size",
242
+ "width"
243
+ ],
244
+ "html": [
245
+ "manifest",
246
+ "version"
247
+ ],
248
+ "iframe": [
249
+ "align",
250
+ "allow",
251
+ "allowfullscreen",
252
+ "allowpaymentrequest",
253
+ "allowusermedia",
254
+ "frameborder",
255
+ "height",
256
+ "longdesc",
257
+ "marginheight",
258
+ "marginwidth",
259
+ "name",
260
+ "referrerpolicy",
261
+ "sandbox",
262
+ "scrolling",
263
+ "src",
264
+ "srcdoc",
265
+ "width"
266
+ ],
267
+ "img": [
268
+ "align",
269
+ "alt",
270
+ "border",
271
+ "crossorigin",
272
+ "decoding",
273
+ "height",
274
+ "hspace",
275
+ "ismap",
276
+ "longdesc",
277
+ "name",
278
+ "referrerpolicy",
279
+ "sizes",
280
+ "src",
281
+ "srcset",
282
+ "usemap",
283
+ "vspace",
284
+ "width"
285
+ ],
286
+ "input": [
287
+ "accept",
288
+ "accesskey",
289
+ "align",
290
+ "alt",
291
+ "autocomplete",
292
+ "autofocus",
293
+ "checked",
294
+ "dirname",
295
+ "disabled",
296
+ "form",
297
+ "formaction",
298
+ "formenctype",
299
+ "formmethod",
300
+ "formnovalidate",
301
+ "formtarget",
302
+ "height",
303
+ "ismap",
304
+ "list",
305
+ "max",
306
+ "maxlength",
307
+ "min",
308
+ "minlength",
309
+ "multiple",
310
+ "name",
311
+ "pattern",
312
+ "placeholder",
313
+ "readonly",
314
+ "required",
315
+ "size",
316
+ "src",
317
+ "step",
318
+ "tabindex",
319
+ "title",
320
+ "type",
321
+ "usemap",
322
+ "value",
323
+ "width"
324
+ ],
325
+ "ins": [
326
+ "cite",
327
+ "datetime"
328
+ ],
329
+ "isindex": [
330
+ "prompt"
331
+ ],
332
+ "label": [
333
+ "accesskey",
334
+ "for",
335
+ "form"
336
+ ],
337
+ "legend": [
338
+ "accesskey",
339
+ "align"
340
+ ],
341
+ "li": [
342
+ "type",
343
+ "value"
344
+ ],
345
+ "link": [
346
+ "as",
347
+ "charset",
348
+ "color",
349
+ "crossorigin",
350
+ "href",
351
+ "hreflang",
352
+ "imagesizes",
353
+ "imagesrcset",
354
+ "integrity",
355
+ "media",
356
+ "nonce",
357
+ "referrerpolicy",
358
+ "rel",
359
+ "rev",
360
+ "sizes",
361
+ "target",
362
+ "title",
363
+ "type"
364
+ ],
365
+ "map": [
366
+ "name"
367
+ ],
368
+ "menu": [
369
+ "compact"
370
+ ],
371
+ "meta": [
372
+ "charset",
373
+ "content",
374
+ "http-equiv",
375
+ "name",
376
+ "scheme"
377
+ ],
378
+ "meter": [
379
+ "high",
380
+ "low",
381
+ "max",
382
+ "min",
383
+ "optimum",
384
+ "value"
385
+ ],
386
+ "object": [
387
+ "align",
388
+ "archive",
389
+ "border",
390
+ "classid",
391
+ "codebase",
392
+ "codetype",
393
+ "data",
394
+ "declare",
395
+ "form",
396
+ "height",
397
+ "hspace",
398
+ "name",
399
+ "standby",
400
+ "tabindex",
401
+ "type",
402
+ "typemustmatch",
403
+ "usemap",
404
+ "vspace",
405
+ "width"
406
+ ],
407
+ "ol": [
408
+ "compact",
409
+ "reversed",
410
+ "start",
411
+ "type"
412
+ ],
413
+ "optgroup": [
414
+ "disabled",
415
+ "label"
416
+ ],
417
+ "option": [
418
+ "disabled",
419
+ "label",
420
+ "selected",
421
+ "value"
422
+ ],
423
+ "output": [
424
+ "for",
425
+ "form",
426
+ "name"
427
+ ],
428
+ "p": [
429
+ "align"
430
+ ],
431
+ "param": [
432
+ "name",
433
+ "type",
434
+ "value",
435
+ "valuetype"
436
+ ],
437
+ "pre": [
438
+ "width"
439
+ ],
440
+ "progress": [
441
+ "max",
442
+ "value"
443
+ ],
444
+ "q": [
445
+ "cite"
446
+ ],
447
+ "script": [
448
+ "async",
449
+ "charset",
450
+ "crossorigin",
451
+ "defer",
452
+ "integrity",
453
+ "language",
454
+ "nomodule",
455
+ "nonce",
456
+ "referrerpolicy",
457
+ "src",
458
+ "type"
459
+ ],
460
+ "select": [
461
+ "autocomplete",
462
+ "autofocus",
463
+ "disabled",
464
+ "form",
465
+ "multiple",
466
+ "name",
467
+ "required",
468
+ "size",
469
+ "tabindex"
470
+ ],
471
+ "slot": [
472
+ "name"
473
+ ],
474
+ "source": [
475
+ "media",
476
+ "sizes",
477
+ "src",
478
+ "srcset",
479
+ "type"
480
+ ],
481
+ "style": [
482
+ "media",
483
+ "nonce",
484
+ "title",
485
+ "type"
486
+ ],
487
+ "table": [
488
+ "align",
489
+ "bgcolor",
490
+ "border",
491
+ "cellpadding",
492
+ "cellspacing",
493
+ "frame",
494
+ "rules",
495
+ "summary",
496
+ "width"
497
+ ],
498
+ "tbody": [
499
+ "align",
500
+ "char",
501
+ "charoff",
502
+ "valign"
503
+ ],
504
+ "td": [
505
+ "abbr",
506
+ "align",
507
+ "axis",
508
+ "bgcolor",
509
+ "char",
510
+ "charoff",
511
+ "colspan",
512
+ "headers",
513
+ "height",
514
+ "nowrap",
515
+ "rowspan",
516
+ "scope",
517
+ "valign",
518
+ "width"
519
+ ],
520
+ "textarea": [
521
+ "accesskey",
522
+ "autocomplete",
523
+ "autofocus",
524
+ "cols",
525
+ "dirname",
526
+ "disabled",
527
+ "form",
528
+ "maxlength",
529
+ "minlength",
530
+ "name",
531
+ "placeholder",
532
+ "readonly",
533
+ "required",
534
+ "rows",
535
+ "tabindex",
536
+ "wrap"
537
+ ],
538
+ "tfoot": [
539
+ "align",
540
+ "char",
541
+ "charoff",
542
+ "valign"
543
+ ],
544
+ "th": [
545
+ "abbr",
546
+ "align",
547
+ "axis",
548
+ "bgcolor",
549
+ "char",
550
+ "charoff",
551
+ "colspan",
552
+ "headers",
553
+ "height",
554
+ "nowrap",
555
+ "rowspan",
556
+ "scope",
557
+ "valign",
558
+ "width"
559
+ ],
560
+ "thead": [
561
+ "align",
562
+ "char",
563
+ "charoff",
564
+ "valign"
565
+ ],
566
+ "time": [
567
+ "datetime"
568
+ ],
569
+ "tr": [
570
+ "align",
571
+ "bgcolor",
572
+ "char",
573
+ "charoff",
574
+ "valign"
575
+ ],
576
+ "track": [
577
+ "default",
578
+ "kind",
579
+ "label",
580
+ "src",
581
+ "srclang"
582
+ ],
583
+ "ul": [
584
+ "compact",
585
+ "type"
586
+ ],
587
+ "video": [
588
+ "autoplay",
589
+ "controls",
590
+ "crossorigin",
591
+ "height",
592
+ "loop",
593
+ "muted",
594
+ "playsinline",
595
+ "poster",
596
+ "preload",
597
+ "src",
598
+ "width"
599
+ ]
600
+ }