hyperspeed 0.1.0 → 0.2.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.
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
+ }