-
Notifications
You must be signed in to change notification settings - Fork 56
/
Copy pathAttributes.swift
1560 lines (1324 loc) · 47.9 KB
/
Attributes.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
import Foundation
public struct Attribute<Element>: Sendable {
public let key: String
public let value: String?
public init(_ key: String, _ value: String?) {
self.key = key
self.value = value
}
}
extension Attribute {
/// All HTML elements may have the `accesskey` content attribute set. The `accesskey` attribute's value is used by the user agent as a guide for creating a keyboard shortcut that activates or focuses the element.
///
/// - Parameter value: Used by the user agent as a guide for creating a keyboard shortcut that activates or focuses the element.
public static func accesskey(_ value: Character) -> Attribute {
return .init("accesskey", String(value))
}
/// When specified on HTML elements, the `class` attribute must have a value that is a set of space-separated tokens representing the various classes that the element belongs to.
///
/// - Parameter value: A set of space-separated tokens.
public static func `class`(_ value: String) -> Attribute {
return .init("class", value)
}
public enum Contenteditable: String, ExpressibleByBooleanLiteral {
case `true` = ""
case `false`
case inherit
public init(booleanLiteral value: Bool) {
self = value ? .true : .false
}
}
/// Make a document region editable.
///
/// - Parameter value: Should a document region be editable.
public static func contenteditable(_ value: Contenteditable) -> Attribute {
return .init("contenteditable", value == .inherit ? nil : value.rawValue)
}
/// Adds a `data`-prefixed attribute to the element.
///
/// - Parameters:
/// - name: The attribute suffix.
/// - value: The value.
public static func data(_ name: StaticString, _ value: String) -> Attribute {
return .init("data-\(name)", value)
}
public enum Direction: String {
case ltr
case rtl
case auto
}
public static func dir(_ value: Direction) -> Attribute {
return .init("dir", value.rawValue)
}
public enum Draggable: String, ExpressibleByBooleanLiteral {
/// The element is draggable.
case `true`
/// The element is not draggable.
case `false`
/// The default draggable behavior of the user agent.
case auto
public init(booleanLiteral value: Bool) {
self = value ? .true : .false
}
}
/// Whether or not an element is draggable.
///
/// - Parameter value: Whether or not an element is draggable.
public static func draggable(_ value: Draggable) -> Attribute {
return .init("draggable", value == .auto ? nil : value.rawValue)
}
/// Hide the element.
///
/// - Parameter value: Hide the element.
public static func hidden(_ value: Bool) -> Attribute {
return .init("hidden", value ? "" : nil)
}
/// When specified on HTML elements, the `id` attribute value must be unique amongst all the IDs in the element's tree and must contain at least one character. The value must not contain any space characters.
///
/// - Parameter value: A unique identifier.
public static func id(_ value: String) -> Attribute {
return .init("id", value)
}
public enum Language: String {
case aa
case ab
case ae
case af
case ak
case am
case an
case ar
case `as`
case av
case ay
case az
case ba
case be
case bg
case bh
case bi
case bm
case bn
case bo
case br
case bs
case ca
case ce
case ch
case co
case cr
case cs
case cu
case cv
case cy
case da
case de
case dv
case dz
case ee
case el
case en
case eo
case es
case et
case eu
case fa
case ff
case fi
case fj
case fo
case fr
case fy
case ga
case gd
case gl
case gn
case gu
case gv
case ha
case he
case hi
case ho
case hr
case ht
case hu
case hy
case hz
case ia
case id
case ie
case ig
case ii
case ik
case `in`
case io
case `is`
case it
case iu
case ja
case ji
case jv
case ka
case kg
case ki
case kj
case kk
case kl
case km
case kn
case ko
case kr
case ks
case ku
case kv
case kw
case ky
case la
case lb
case lg
case li
case ln
case lo
case lt
case lu
case lv
case mg
case mh
case mi
case mk
case ml
case mn
case mr
case ms
case mt
case my
case na
case nb
case nd
case ne
case nl
case nn
case no
case nr
case nv
case ny
case oc
case oj
case om
case or
case os
case pa
case pi
case pl
case ps
case pt
case qu
case rm
case rn
case ro
case ru
case rw
case sa
case sc
case sd
case se
case sg
case si
case sk
case sl
case sm
case sn
case so
case sq
case sr
case ss
case st
case su
case sv
case sw
case ta
case te
case tg
case th
case ti
case tk
case tl
case tn
case to
case tr
case ts
case tt
case tw
case ty
case ug
case uk
case ur
case uz
case ve
case vi
case vo
case wa
case wo
case xh
case yi
case yo
case za
case zu
case zh
case zhHans = "zh-Hans"
case zhHant = "zh-Hant"
case unknown = ""
}
/// The `lang` attribute specifies the primary language for the element's contents and for any of the element's attributes that contain text.
///
/// - Parameter value: A valid BCP 47 language tag, or the empty string. Setting the attribute to the empty string indicates that the primary language is unknown.
public static func lang(_ value: Language) -> Attribute {
return .init("lang", value.rawValue)
}
public static func spellcheck(_ value: Bool) -> Attribute {
return .init("spellcheck", String(value))
}
/// This is a style attribute as defined by the _CSS Style Attributes_ specification.
///
/// - Parameter value: A CSS style.
public static func style(safe value: StaticString) -> Attribute {
return style(unsafe: String(describing: value))
}
/// This is a style attribute as defined by the _CSS Style Attributes_ specification.
///
/// - Parameter value: A CSS style.
public static func style(unsafe value: String) -> Attribute {
return .init("style", String(describing: value))
}
/// The `tabindex` content attribute allows authors to indicate that an element is supposed to be focusable, whether it is supposed to be reachable using sequential focus navigation and, optionally, to suggest where in the sequential focus navigation order the element appears.
///
/// - Parameter value: The sequential focus navigation order the element appears.
public static func tabindex(_ value: Int) -> Attribute {
return .init("tabindex", String(value))
}
/// The `title` attribute represents advisory information for the element, such as would be appropriate for a tooltip.
///
/// - Parameter value: Advisory information.
public static func title(_ value: String) -> Attribute {
return .init("title", value)
}
public enum Translate: String, ExpressibleByBooleanLiteral {
case yes
case no
public init(booleanLiteral value: Bool) {
self = value ? .yes : .no
}
}
/// Whether or not an element's attribute values and the values of its text node children are to be translated when the page is localized.
///
/// - Parameter value: Whether or not an element's attribute values and the values of its text node children are to be translated when the page is localized.
public static func translate(_ value: Translate) -> Attribute {
return .init("translate", value.rawValue)
}
}
extension Attribute where Element == Tag.A {
/// Email address of a hyperlink.
///
/// - Parameters:
/// - address: One or more email addresses for the "to" field.
/// - cc: Zero or more email addresses for the "cc" field.
/// - bcc: Zero or more email addresses for the "bcc" field.
/// - subject: An optional email subject.
/// - body: An optional email body.
/// - Returns: A "mailto" URL for hyperlinks.
public static func mailto(
_ addresses: String...,
cc: [String] = [],
bcc: [String] = [],
subject: String = "",
body: String = ""
)
-> Attribute
{
return .mailto(addresses, cc: cc, bcc: bcc, subject: subject, body: body)
}
/// Email address of a hyperlink.
///
/// - Parameters:
/// - address: One or more email addresses for the "to" field.
/// - cc: Zero or more email addresses for the "cc" field.
/// - bcc: Zero or more email addresses for the "bcc" field.
/// - subject: An optional email subject.
/// - body: An optional email body.
/// - Returns: A "mailto" URL for hyperlinks.
public static func mailto(
_ addresses: [String],
cc: [String] = [],
bcc: [String] = [],
subject: String = "",
body: String = ""
)
-> Attribute
{
var urlComponents = URLComponents()
urlComponents.scheme = "mailto"
urlComponents.path = addresses.joined(separator: ",")
let queryItems = [
cc.isEmpty ? nil : URLQueryItem(name: "cc", value: cc.joined(separator: ",")),
bcc.isEmpty ? nil : URLQueryItem(name: "bcc", value: bcc.joined(separator: ",")),
subject.isEmpty ? nil : URLQueryItem(name: "subject", value: subject),
body.isEmpty ? nil : URLQueryItem(name: "body", value: body),
]
.compactMap { $0 }
urlComponents.queryItems = queryItems.isEmpty ? nil : queryItems
return href(urlComponents.string ?? "")
}
}
/// The kind of shape to be created in an image map.
public enum AreaShape: String {
case circle
// case `default`
case poly
case rect = ""
}
extension Attribute where Element == Tag.Area {
/// The kind of shape to be created in an image map.
///
/// - Parameter value: The kind of shape to be created in an image map.
public static func shape(_ value: AreaShape) -> Attribute {
return .init("shape", value == .rect ? nil : value.rawValue)
}
}
/// Type of button.
public enum ButtonType: String {
/// Does nothing.
case button
/// Resets the form.
case reset
/// Submits the form.
case submit
}
extension Attribute where Element == Tag.Button {
/// Type of button.
///
/// - Parameter value: Type of button.
public static func type(_ value: ButtonType) -> Attribute {
return .init("type", value.rawValue)
}
}
extension Attribute where Element == Tag.Details {
/// Whether the details are visible.
///
/// - Parameter value: Whether the details are visible.
public static func open(_ value: Bool) -> Attribute {
return .init("open", value ? "" : nil)
}
}
/// HTTP method to use for form submission.
public enum FormMethod: String {
/// Submitting the form is intended to close the dialog box in which the form finds itself, if any, and otherwise not submit.
case dialog = "dialog"
/// The HTTP GET method.
case get = ""
/// The HTTP POST method.
case post = "post"
}
extension Attribute where Element == Tag.Form {
/// URL to use for form submission.
///
/// - Parameter value: URL to use for form submission.
public static func action(_ value: String) -> Attribute {
return .init("action", value)
}
public struct Enctype: RawRepresentable, Sendable {
public let rawValue: String
public init(rawValue: String) {
self.rawValue = rawValue
}
public static let applicationXWwwFormUrlencoded = Enctype(
rawValue: "application/x-www-form-urlencoded")
public static let multipartFormData = Enctype(rawValue: "multipart/form-data")
public static let textPlain = Enctype(rawValue: "text/plain")
}
/// The type of form encoding.
///
/// - Parameter value: Enctype to use for form encoding.
public static func enctype(_ value: Enctype) -> Attribute {
return .init("enctype", value.rawValue)
}
/// HTTP method to use for form submission.
///
/// - Parameter value: HTTP method to use for form submission.
public static func method(_ value: FormMethod) -> Attribute {
let rawValue = value.rawValue
return .init("method", rawValue.isEmpty ? nil : rawValue)
}
/// Bypass form control validation for form submission.
///
/// - Parameter value: Bypass form control validation for form submission.
public static func novalidate(_ value: Bool) -> Attribute {
return .init("novalidate", value ? "" : nil)
}
}
public enum IframeSandbox: String {
/// Re-enable forms.
case allowForms = "allow-forms"
/// Re-enable the pointer lock API.
case allowPointerLock = "allow-pointer-lock"
/// Re-enable popups.
case allowPopups = "allow-popups"
/// Re-enable the presentation API.
case allowPresentation = "allow-presentation"
/// Causes the content to be treated as being from its real origin instead of forcing it into a unique origin.
case allowSameOrigin = "allow-same-origin"
/// Re-enable scripts.
case allowScripts = "allow-scripts"
/// Allows the content to navigate its top-level browsing context.
case allowTopNavigation = "allow-top-navigation"
}
extension Attribute where Element == Tag.Iframe {
/// Enables a set of extra restrictions on any content hosted by the `<iframe>`.
///
/// - Parameter value: Sandbox options.
public static func sandbox(_ value: [IframeSandbox]) -> Attribute {
return .init("sandbox", value.map { $0.rawValue }.joined(separator: " "))
}
/// Enables a set of extra restrictions on any content hosted by the `<iframe>`.
///
/// - Parameter value: Whether or not to sandbox the `<iframe>`.
public static func sandbox(_ value: Bool) -> Attribute {
return .init("sandbox", value ? "" : nil)
}
/// A document to render in the `iframe`.
///
/// - Parameter value: A document to render in the `iframe`.
public static func srcdoc(_ value: Node...) -> Attribute {
return .init("srcdoc", render(value))
}
}
public enum InputType: String {
case button
case checkbox
case color
case date
case datetimeLocal = "datetime-local"
case email
case file
case hidden
case image
case month
case number
case password
case radio
case range
case reset
case search
case submit
case tel
case text
case time
case url
case week
}
public struct Accept: RawRepresentable, Sendable {
public let rawValue: String
public init(rawValue: String) {
self.rawValue = rawValue
}
public static let audio = Accept(rawValue: "audio/*")
public static let image = Accept(rawValue: "image/*")
public static let video = Accept(rawValue: "video/*")
public static func file(_ type: String) -> Accept {
return .init(rawValue: type.hasPrefix(".") ? type : "." + type)
}
public static func mime(_ type: MediaType) -> Accept {
let mimeTypeOnly = type.description.split(separator: ";")[0]
return .init(rawValue: String(mimeTypeOnly))
}
}
extension Attribute where Element == Tag.Input {
/// Whether the command or control is checked.
///
/// - Parameter value: Whether the command or control is checked.
public static func checked(_ value: Bool) -> Attribute {
return .init("checked", value ? "" : nil)
}
/// Pattern to be matched by the form control's value.
///
/// - Parameter value: Pattern to be matched by the form control's value.
public static func pattern(_ value: String) -> Attribute {
return .init("pattern", value)
}
/// Granularity to be matched by the form control's value.
///
/// - Parameter value: Granularity to be matched by the form control's value.
public static func step(_ value: Int) -> Attribute {
return .init("step", String(value))
}
public static func type(_ value: InputType) -> Attribute {
return .init("type", value.rawValue)
}
/// Pattern or file types to be selected (only for type="file")
///
/// - Parameter value:
/// The file extension or media type the user can select,
/// such as ".pdf" or "image/*"
public static func accept(_ value: Accept...) -> Attribute {
return .init("accept", value.map { $0.rawValue }.joined(separator: ", "))
}
}
extension Attribute where Element == Tag.Meta {
/// Value of the `<meta>` element.
///
/// - Parameter value: Value of the element.
public static func content(_ value: String) -> Attribute {
return .init("content", value)
}
}
public enum ListType: String {
/// Decimal numbers: `1.`, `2.`, `3.`...`3999.`, `4000.`, `4001.`
case decimal = "1"
/// Lowercase Latin alphabet: `a.`, `b.`, `c.`...`ewu.`, `ewv.`, `eww.`
case lowerAlpha = "a"
/// Uppercase Latin alphabet: `A.`, `B.`, `C.`...`EWU.`, `EWV.`, `EWW.`
case upperAlpha = "A"
/// Lowercase Roman numerals: `1.`, `2.`, `3.`...`mmmcmxcix.`, `i̅v̅.`, `i̅v̅i.`
case lowerRoman = "i"
/// Uppercase Roman numerals: `I.`, `II.`, `III.`...`MMMCMXCIX.`, `I̅V̅.`, `I̅V̅I.`
case upperRoman = "I"
}
extension Attribute where Element == Tag.Ol {
/// Number the list backwards.
///
/// - Parameter value: Number the list backwards.
public static func reversed(_ value: Bool) -> Attribute {
return .init("reversed", value ? "" : nil)
}
/// Ordinal value of the first item.
///
/// - Parameter value: Ordinal value of the first item.
public static func start(_ value: Int) -> Attribute {
return .init("start", String(value))
}
/// Kind of list marker.
///
/// - Parameter value: Kind of list marker.
public static func type(_ value: ListType) -> Attribute {
return .init("type", value.rawValue)
}
}
extension Attribute where Element == Tag.Option {
/// Whether the option is selected by default.
///
/// - Parameter value: Whether the option is selected by default.
public static func selected(_ value: Bool) -> Attribute {
return .init("selected", value ? "" : nil)
}
}
extension Attribute where Element == Tag.Script {
/// Execute script in parallel.
///
/// - Parameter value: Execute script in parallel.
public static func async(_ value: Bool) -> Attribute {
return .init("async", value ? "" : nil)
}
/// Defer script execution.
///
/// - Parameter value: Defer script execution.
public static func `defer`(_ value: Bool) -> Attribute {
return .init("defer", value ? "" : nil)
}
/// Cryptographic nonce used in Content Security Policy checks.
///
/// - Parameter value: Cryptographic nonce used in Content Security Policy checks.
public static func nonce(_ value: String) -> Attribute {
return .init("nonce", value)
}
}
extension Attribute where Element == Tag.Source {
/// Applicable media.
///
/// - Parameter value: A media query list.
public static func media(_ value: String) -> Attribute {
return .init("media", value)
}
/// Images to use in different situations (e.g., high-resolution displays, small monitors, etc).
///
/// - Parameter value: Images to use in different situations (e.g., high-resolution displays, small monitors, etc).
public static func srcset(_ value: String) -> Attribute {
return .init("srcset", value)
}
}
public enum TextareaWrap: String {
/// Indicates that the text in the `<textarea>` is to have newlines added by the user agent so that the text is wrapped when it is submitted.
///
/// If the element's `wrap` attribute is in the `hard` state, the `cols` attribute must be specified.
case hard
/// Indicates that the text in the `<textarea>` is not to be wrapped when it is submitted (though it can still be wrapped in the rendering).
case soft
}
extension Attribute where Element == Tag.Textarea {
/// Maximum number of characters per line.
///
/// - Parameter value: Maximum number of characters per line.
public static func cols(_ value: Int) -> Attribute {
return .init("cols", String(value))
}
/// Number of lines to show.
///
/// - Parameter value: Number of lines to show.
public static func rows(_ value: Int) -> Attribute {
return .init("rows", String(value))
}
/// How the value of the form control is to be wrapped for form submission.
///
/// - Parameter value: How the value of the form control is to be wrapped for form submission.
public static func wrap(_ value: TextareaWrap) -> Attribute {
return .init("wrap", value.rawValue)
}
}
/// Specifies which cells the header cell applies to.
public enum ThScope: String {
/// The **auto** state makes the header cell apply to a set of cells selected based on context.
case auto = ""
/// The **column** state means the header cell applies to some of the subsequent cells in the same column(s).
case col
/// The **col group** state means the header cell applies to all the remaining cells in the column group. A `<th>` element's `scope` attribute must not be in the column group state if the element is not anchored in a column group.
case colgroup
/// The **row** state means the header cell applies to some of the subsequent cells in the same row(s).
case row
/// The **row group** state means the header cell applies to all the remaining cells in the row group. A `<th>` element's `scope` attribute must not be in the row group state if the element is not anchored in a row group.
case rowgroup
}
extension Attribute where Element == Tag.Th {
/// Alternative label to use for the header cell when referencing the cell in other contexts.
///
/// - Parameter value: Alternative label to use for the header cell when referencing the cell in other contexts.
public static func abbr(_ value: String) -> Attribute {
return .init("abbr", value)
}
/// Specifies which cells the header cell applies to.
///
/// - Parameter value: Specifies which cells the header cell applies to.
public static func scope(_ value: ThScope) -> Attribute {
return .init("scope", value.rawValue)
}
}
/// Kinds of text tracks for `<track>` elements.
public enum TrackKind: String {
/// Transcription or translation of the dialog, sound effects, relevant musical cues, and other relevant audio information, suitable for when sound is unavailable or not clearly audible (e.g., because it is muted, drowned-out by ambient noise, or because the user is deaf). Overlaid on the video; labeled as appropriate for the hard-of-hearing.
case captions
/// Chapter titles, intended to be used for navigating the media resource. Displayed as an interactive (potentially nested) list in the user agent's interface.
case chapters
/// Textual descriptions of the video component of the media resource, intended for audio synthesis when the visual component is obscured, unavailable, or not usable (e.g., because the user is interacting with the application without a screen while driving, or because the user is blind). Synthesized as audio
case descriptions
/// Tracks intended for use from script. Not displayed by the user agent.
case metadata
/// Transcription or translation of the dialog, suitable for when the sound is available but not understood (e.g., because the user does not understand the language of the media resource's audio track). Overlaid on the video.
case subtitles
}
extension Attribute where Element == Tag.Track {
public static func `default`(_ value: Bool) -> Attribute {
return .init("default", value ? "" : nil)
}
/// The type of text track.
///
/// - Parameter value: The type of text track.
public static func kind(_ value: TrackKind) -> Attribute {
return .init("kind", value.rawValue)
}
/// User-visible label for a `<track>` element.
///
/// - Parameter value: User-visible label.
public static func label(_ value: String) -> Attribute {
return .init("label", value)
}
/// Language of the text track.
///
/// - Parameter value: Language of the text track.
public static func srclang(_ value: Language) -> Attribute {
return .init("srclang", value.rawValue)
}
}
extension Attribute where Element == Tag.Video {
public static func poster(_ value: String) -> Attribute {
return .init("poster", value)
}
}
/// Conforming elements can have an `alt` attribute. Includes `<area>`, `<img>` and `<input>` elements.
public protocol HasAlt {}
extension Tag.Area: HasAlt {}
extension Tag.Img: HasAlt {}
extension Tag.Input: HasAlt {}
extension Attribute where Element: HasAlt {
/// Replacement text for use when images are not available. For `<area>`, `<img>`, and `<input>` elements.
///
/// - Parameter value: Replacement text for use when images are not available.
public static func alt(_ value: String) -> Attribute {
return .init("alt", value)
}
}
/// Conforming elements can have an `autofocus` attribute. Includes `<button>`, `<input>`, `<select>`, and `<textarea>` elements.
public protocol HasAutofocus {}
extension Tag.Button: HasAutofocus {}
extension Tag.Input: HasAutofocus {}
extension Tag.Select: HasAutofocus {}
extension Tag.Textarea: HasAutofocus {}
extension Attribute where Element: HasAutofocus {
/// The `autofocus` content attribute allows the author to indicate that a control is to be focused as soon as the page is loaded or as soon as the `dialog` within which it finds itself is shown, allowing the user to just start typing without having to manually focus the main control.
///
/// - Parameter value: Automatically focus the form control when the page is loaded.
public static func autofocus(_ value: Bool) -> Attribute {
return .init("autofocus", value ? "" : nil)
}
}
/// Conforming elements can have an `autoplay` attribute. Includes `<audio>` and `<video>` elements.
public protocol HasAutoplay {}
extension Tag.Audio: HasAutoplay {}
extension Tag.Video: HasAutoplay {}
extension Attribute where Element: HasAutoplay {
/// Hint that the media resource can be started automatically when the page is loaded.
///
/// - Parameter value: Hint that the media resource can be started automatically when the page is loaded.
public static func autoplay(_ value: Bool) -> Attribute {
return .init("autoplay", value ? "" : nil)
}
}
/// Conforming elements can have a `charset` attribute. Includes `<meta>` and `<script>` elements.
public protocol HasCharset {}
extension Tag.Meta: HasCharset {}
extension Tag.Script: HasCharset {}
extension Attribute where Element: HasCharset {
/// Character encoding declaration.
///
/// - Parameter value: A character encoding declaration.
public static func charset(_ value: Charset) -> Attribute {
return .init("charset", value.rawValue)
}
}
/// Conforming elements can have a `cite` attribute. Includes `<blockquote>`, `<del>`, `<ins>`, and `<q>` elements.
public protocol HasCite {}
extension Tag.Blockquote: HasCite {}
extension Tag.Del: HasCite {}
extension Tag.Ins: HasCite {}
extension Tag.Q: HasCite {}
extension Attribute where Element: HasCite {
/// Link to the source of the quotation.
///
/// - Parameter value: Link to the source of the quotation or more information about the edit.
public static func cite(_ value: String) -> Attribute {
return .init("cite", value)
}
}
/// Conforming elements can have a `colspan` attribute. Includes `<td>` and `<th>` elements.
public protocol HasColspan {}
extension Tag.Td: HasColspan {}
extension Tag.Th: HasColspan {}
extension Attribute where Element: HasColspan {
/// Number of columns that the cell is to span.
///
/// - Parameter value: Number of columns that the cell is to span.
public static func colspan(_ value: Int) -> Attribute {
return .init("colspan", String(value))
}
}
/// Conforming elements can have a `controls` attribute. Includes `<audio>` and `<video>` elements.
public protocol HasControls {}
extension Tag.Audio: HasControls {}
extension Tag.Video: HasControls {}
extension Attribute where Element: HasControls {
/// If present, it indicates that the author has not provided a scripted controller and would like the user agent to provide its own set of controls.
///
/// - Parameter value: Show user agent controls.
public static func controls(_ value: Bool) -> Attribute {
return .init("controls", value ? "" : nil)
}
}
/// Conforming elements can have a `crossorigin` attribute. Includes `<img>` and `<script>` elements.
public protocol HasCrossorigin {}
extension Tag.Img: HasCrossorigin {}
extension Tag.Script: HasCrossorigin {}
extension Tag.Link: HasCrossorigin {}
extension Attribute where Element: HasCrossorigin {
public enum Crossorigin: String {
/// Requests for the element will have their mode set to "`cors`" and their credentials mode set to "`same-origin`".
case anonymous = ""
/// Requests for the element will have their mode set to "`cors`" and their credentials mode set to "`include`".
case useCredentials = "use-credentials"
}
/// How the element handles crossorigin requests.
///
/// - Parameter value: How the element handles crossorigin requests.
public static func crossorigin(_ value: Crossorigin) -> Attribute {
return .init("crossorigin", value.rawValue)
}
}
private let iso8601DateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
formatter.timeZone = TimeZone(identifier: "UTC")
return formatter
}()
/// Conforming elements can have a `datetime` attribute. Includes `<del>`, `<ins>`, and `<time>` elements.
public protocol HasDatetime {}
extension Tag.Del: HasDatetime {}
extension Tag.Ins: HasDatetime {}
extension Tag.Time: HasDatetime {}
extension Attribute where Element: HasDatetime {
public static func datetime(_ value: Date) -> Attribute {