aboutsummaryrefslogtreecommitdiff
path: root/stdlib/source/library/lux/data/format/html.lux
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--stdlib/source/library/lux/data/format/html.lux563
1 files changed, 563 insertions, 0 deletions
diff --git a/stdlib/source/library/lux/data/format/html.lux b/stdlib/source/library/lux/data/format/html.lux
new file mode 100644
index 000000000..6a8e0b24f
--- /dev/null
+++ b/stdlib/source/library/lux/data/format/html.lux
@@ -0,0 +1,563 @@
+(.module:
+ [library
+ [lux (#- Meta Source comment and)
+ ["." function]
+ [data
+ ["." product]
+ ["." maybe ("#\." functor)]
+ ["." text
+ ["%" format (#+ format)]]
+ [collection
+ ["." list ("#\." functor fold)]]]
+ [type
+ abstract]
+ [host
+ ["." js]]
+ [macro
+ ["." template]]
+ [world
+ [net (#+ URL)]]]]
+ [//
+ [css
+ ["." selector]
+ ["." style (#+ Style)]]
+ ["." xml (#+ XML)]])
+
+(type: #export Tag selector.Tag)
+(type: #export ID selector.ID)
+(type: #export Class selector.Class)
+
+(type: #export Attributes
+ {#.doc "Attributes for an HTML tag."}
+ (List [Text Text]))
+
+(type: #export Script js.Statement)
+
+(type: #export Target
+ #Blank
+ #Parent
+ #Self
+ #Top
+ (#Frame Text))
+
+(def: (target value)
+ (-> Target Text)
+ (case value
+ #Blank "_blank"
+ #Parent "_parent"
+ #Self "_self"
+ #Top "_top"
+ (#Frame name) name))
+
+(def: sanitize
+ {#.doc "Properly formats text to ensure no injection can happen on the HTML."}
+ (-> Text Text)
+ (|>> (text.replace-all "&" "&")
+ (text.replace-all "<" "&lt;")
+ (text.replace-all ">" "&gt;")
+ (text.replace-all text.double-quote "&quot;")
+ (text.replace-all "'" "&#x27;")
+ (text.replace-all "/" "&#x2F;")))
+
+(def: attributes
+ (-> Attributes Text)
+ (|>> (list\map (function (_ [key val])
+ (format key "=" text.double-quote (..sanitize val) text.double-quote)))
+ (text.join-with " ")))
+
+(def: (open tag attributes)
+ (-> Tag Attributes Text)
+ (|> attributes
+ ..attributes
+ (format tag " ")
+ (text.enclose ["<" ">"])))
+
+(def: close
+ (-> Tag Text)
+ (text.enclose ["</" ">"]))
+
+(abstract: #export (HTML brand)
+ Text
+
+ (template [<name> <brand>]
+ [(abstract: #export <brand> Any)
+ (type: #export <name> (HTML <brand>))]
+
+ [Meta Meta']
+ [Head Head']
+ [Item Item']
+ [Option Option']
+ [Input Input']
+ [Cell Cell']
+ [Header Header']
+ [Row Row']
+ [Column Column']
+ [Parameter Parameter']
+ [Body Body']
+ [Document Document']
+ )
+
+ (template [<super> <super-raw> <sub>+]
+ [(abstract: #export (<super-raw> brand) Any)
+ (type: #export <super> (HTML (<super-raw> Any)))
+
+ (`` (template [<sub> <sub-raw>]
+ [(abstract: #export <sub-raw> Any)
+ (type: #export <sub> (HTML (<super-raw> <sub-raw>)))]
+
+ (~~ (template.splice <sub>+))))]
+
+ [Element Element'
+ [[Content Content']
+ [Image Image']]]
+
+ [Media Media'
+ [[Source Source']
+ [Track Track']]]
+ )
+
+ (def: #export html
+ (-> Document Text)
+ (|>> :representation))
+
+ (def: #export (and pre post)
+ (All [brand] (-> (HTML brand) (HTML brand) (HTML brand)))
+ (:abstraction (format (:representation pre) (:representation post))))
+
+ (def: #export (comment content node)
+ (All [brand] (-> Text (HTML brand) (HTML brand)))
+ (:abstraction
+ (format (text.enclose ["<!--" "-->"] content)
+ (:representation node))))
+
+ (def: (empty name attributes)
+ (-> Tag Attributes HTML)
+ (:abstraction
+ (format (..open name attributes)
+ (..close name))))
+
+ (def: (simple tag attributes)
+ (-> Tag Attributes HTML)
+ (|> attributes
+ (..open tag)
+ :abstraction))
+
+ (def: (tag name attributes content)
+ (-> Tag Attributes (HTML Any) HTML)
+ (:abstraction
+ (format (..open name attributes)
+ (:representation content)
+ (..close name))))
+
+ (def: (raw tag attributes content)
+ (-> Text Attributes Text HTML)
+ (:abstraction
+ (format (..open tag attributes)
+ content
+ (..close tag))))
+
+ (template [<name> <tag> <brand>]
+ [(def: #export <name>
+ (-> Attributes <brand>)
+ (..simple <tag>))]
+
+ [link "link" Meta]
+ [meta "meta" Meta]
+ [input "input" Input]
+ [embedded "embed" Element]
+ [column "col" Column]
+ [parameter "param" Parameter]
+ )
+
+ (def: #export (base href target)
+ (-> URL (Maybe Target) Meta)
+ (let [partial (list ["href" href])
+ full (case target
+ (#.Some target)
+ (list& ["target" (..target target)] partial)
+
+ #.None
+ partial)]
+ (..simple "base" full)))
+
+ (def: #export style
+ (-> Style Meta)
+ (|>> style.inline (..raw "style" (list))))
+
+ (def: #export (script attributes inline)
+ (-> Attributes (Maybe Script) Meta)
+ (|> inline
+ (maybe\map js.code)
+ (maybe.default "")
+ (..raw "script" attributes)))
+
+ (def: #export text
+ (-> Text Content)
+ (|>> ..sanitize
+ :abstraction))
+
+ (template [<tag> <alias> <name>]
+ [(def: #export <name>
+ Element
+ (..simple <tag> (list)))
+
+ (def: #export <alias> <name>)]
+ ["br" br line-break]
+ ["wbr" wbr word-break]
+ ["hr" hr separator]
+ )
+
+ (def: #export (image source attributes)
+ (-> URL Attributes Image)
+ (|> attributes
+ (#.Cons ["src" source])
+ (..simple "img")))
+
+ (def: #export (svg attributes content)
+ (-> Attributes XML Element)
+ (|> content
+ (\ xml.codec encode)
+ (..raw "svg" attributes)))
+
+ (type: #export Coord
+ {#horizontal Nat
+ #vertical Nat})
+
+ (def: metric-separator ",")
+ (def: coord-separator ",")
+
+ (def: (%coord [horizontal vertical])
+ (Format Coord)
+ (format (%.nat horizontal) ..metric-separator (%.nat vertical)))
+
+ (type: #export Rectangle
+ {#start Coord
+ #end Coord})
+
+ (type: #export Circle
+ {#center Coord
+ #radius Nat})
+
+ (type: #export Polygon
+ {#first Coord
+ #second Coord
+ #third Coord
+ #extra (List Coord)})
+
+ (def: (%rectangle [start end])
+ (Format Rectangle)
+ (format (%coord start) ..coord-separator (%coord end)))
+
+ (def: (%circle [center radius])
+ (Format Circle)
+ (format (%coord center) ..metric-separator (%.nat radius)))
+
+ (def: (%polygon [first second third extra])
+ (Format Polygon)
+ (|> (list& first second third extra)
+ (list\map %coord)
+ (text.join-with ..coord-separator)))
+
+ (type: #export Shape
+ (#Rectangle Rectangle)
+ (#Circle Circle)
+ (#Polygon Polygon))
+
+ (template [<name> <shape> <type> <format>]
+ [(def: (<name> attributes shape)
+ (-> Attributes <type> (HTML Any))
+ (..simple "area" (list& ["shape" <shape>]
+ ["coords" (<format> shape)]
+ attributes)))]
+
+ [rectangle "rect" Rectangle ..%rectangle]
+ [circle "circle" Circle ..%circle]
+ [polygon "poly" Polygon ..%polygon]
+ )
+
+ (def: (area attributes shape)
+ (-> Attributes Shape (HTML Any))
+ (case shape
+ (#Rectangle rectangle)
+ (..rectangle attributes rectangle)
+
+ (#Circle circle)
+ (..circle attributes circle)
+
+ (#Polygon polygon)
+ (..polygon attributes polygon)))
+
+ (def: #export (map attributes areas for)
+ (-> Attributes (List [Attributes Shape]) Image Image)
+ ($_ ..and
+ for
+ (case (list\map (product.uncurry ..area) areas)
+ #.Nil
+ (..empty "map" attributes)
+
+ (#.Cons head tail)
+ (..tag "map" attributes
+ (list\fold (function.flip ..and) head tail)))))
+
+ (template [<name> <tag> <type>]
+ [(def: #export <name>
+ (-> Attributes <type>)
+ (..empty <tag>))]
+
+ [canvas "canvas" Element]
+ [progress "progress" Element]
+ [output "output" Input]
+ [source "source" Source]
+ [track "track" Track]
+ )
+
+ (template [<name> <tag>]
+ [(def: #export (<name> attributes media on-unsupported)
+ (-> Attributes Media (Maybe Content) Element)
+ (..tag <tag> attributes
+ (|> on-unsupported
+ (maybe.default (..text ""))
+ (..and media))))]
+
+ [audio "audio"]
+ [video "video"]
+ )
+
+ (def: #export (picture attributes sources image)
+ (-> Attributes Source Image Element)
+ (..tag "picture" attributes (..and sources image)))
+
+ (def: #export (anchor href attributes content)
+ (-> URL Attributes Element Element)
+ (..tag "a" (list& ["href" href] attributes) content))
+
+ (def: #export label
+ (-> ID Input)
+ (|>> ["for"] list (..empty "label")))
+
+ (template [<name> <container-tag> <description-tag> <type>]
+ [(def: #export (<name> description attributes content)
+ (-> (Maybe Content) Attributes <type> <type>)
+ (..tag <container-tag> attributes
+ (case description
+ (#.Some description)
+ ($_ ..and
+ (..tag <description-tag> (list) description)
+ content)
+
+ #.None
+ content)))]
+
+ [details "details" "summary" Element]
+ [field-set "fieldset" "legend" Input]
+ [figure "figure" "figcaption" Element]
+ )
+
+ (template [<name> <tag> <type>]
+ [(def: #export (<name> attributes content)
+ (-> Attributes (Maybe Content) <type>)
+ (|> content
+ (maybe.default (..text ""))
+ (..tag <tag> attributes)))]
+
+ [text-area "textarea" Input]
+ [iframe "iframe" Element]
+ )
+
+ (type: #export Phrase (-> Attributes Content Element))
+
+ (template [<name> <tag>]
+ [(def: #export <name>
+ Phrase
+ (..tag <tag>))]
+
+ [abbrebiation "abbr"]
+ [block-quote "blockquote"]
+ [bold "b"]
+ [cite "cite"]
+ [code "code"]
+ [definition "dfn"]
+ [deleted "del"]
+ [emphasized "em"]
+ [h1 "h1"]
+ [h2 "h2"]
+ [h3 "h3"]
+ [h4 "h4"]
+ [h5 "h5"]
+ [h6 "h6"]
+ [inserted "ins"]
+ [italic "i"]
+ [keyboard "kbd"]
+ [marked "mark"]
+ [meter "meter"]
+ [pre "pre"]
+ [quote "q"]
+ [sample "samp"]
+ [struck "s"]
+ [small "small"]
+ [sub "sub"]
+ [super "sup"]
+ [strong "strong"]
+ [time "time"]
+ [underlined "u"]
+ [variable "var"]
+ )
+
+ (def: #export incorrect ..struck)
+
+ (def: (ruby-pronunciation pronunciation)
+ (-> Content (HTML Any))
+ (..tag "rt" (list)
+ ($_ ..and
+ (..tag "rp" (list) (..text "("))
+ pronunciation
+ (..tag "rp" (list) (..text ")")))))
+
+ (def: #export (ruby attributes content pronunciation)
+ (-> Attributes Content Content Element)
+ (..tag "ruby" attributes
+ ($_ ..and
+ content
+ (ruby-pronunciation pronunciation))))
+
+ (type: #export Composite (-> Attributes Element Element))
+
+ (template [<name> <tag>]
+ [(def: #export <name>
+ Composite
+ (..tag <tag>))]
+
+ [article "article"]
+ [aside "aside"]
+ [dialog "dialog"]
+ [div "div"]
+ [footer "footer"]
+ [header "header"]
+ [main "main"]
+ [navigation "nav"]
+ [paragraph "p"]
+ [section "section"]
+ [span "span"]
+ )
+
+ (template [<tag> <name> <input>]
+ [(def: <name>
+ (-> <input> (HTML Any))
+ (..tag <tag> (list)))]
+
+ ["dt" term Content]
+ ["dd" description Element]
+ )
+
+ (def: #export (description-list attributes descriptions)
+ (-> Attributes (List [Content Element]) Element)
+ (case (list\map (function (_ [term description])
+ ($_ ..and
+ (..term term)
+ (..description description)))
+ descriptions)
+ #.Nil
+ (..empty "dl" attributes)
+
+ (#.Cons head tail)
+ (..tag "dl" attributes
+ (list\fold (function.flip ..and) head tail))))
+
+ (def: #export p ..paragraph)
+
+ (template [<name> <tag> <input> <output>]
+ [(def: #export <name>
+ (-> Attributes <input> <output>)
+ (..tag <tag>))]
+
+ [button "button" Element Input]
+ [item "li" Element Item]
+ [ordered-list "ol" Item Element]
+ [unordered-list "ul" Item Element]
+ [option "option" Content Option]
+ [option-group "optgroup" Option Option]
+ [data-list "datalist" Option Element]
+ [select "select" Option Input]
+ [address "address" Element Element]
+ [form "form" Input Element]
+ [data "data" Element Element]
+ [object "object" Parameter Element]
+ )
+
+ (template [<name> <tag> <input> <output>]
+ [(def: #export <name>
+ (-> <input> <output>)
+ (..tag <tag> (list)))]
+
+ [title "title" Content Meta]
+ [no-script "noscript" Content Meta]
+ [template "template" (HTML Any) (HTML Nothing)]
+ [table-header "th" Element Header]
+ [table-cell "td" Element Cell]
+ [head "head" Meta Head]
+ [body "body" Element Body]
+ )
+
+ (template [<name> <tag> <input> <output>]
+ [(def: <name>
+ (-> <input> <output>)
+ (..tag <tag> (list)))]
+
+ [table-row "tr" (HTML Any) Row]
+ [table-head "thead" Row HTML]
+ [table-body "tbody" Row HTML]
+ [table-foot "tfoot" Row HTML]
+ [columns-group "colgroup" Column HTML]
+ )
+
+ (def: #export (table attributes caption columns headers rows footer)
+ (-> Attributes (Maybe Content) (Maybe Column) Header (List Cell) (Maybe Cell) Element)
+ (let [head (..table-head (..table-row headers))
+ content (case (list\map table-row rows)
+ #.Nil
+ head
+
+ (#.Cons first rest)
+ (..and head
+ (..table-body
+ (list\fold (function.flip ..and) first rest))))
+ content (case footer
+ #.None
+ content
+
+ (#.Some footer)
+ (..and content
+ (..table-foot (..table-row footer))))
+ content (case columns
+ #.None
+ content
+
+ (#.Some columns)
+ (..and (..columns-group columns)
+ content))
+ content (case caption
+ #.None
+ content
+
+ (#.Some caption)
+ (..and (:as HTML caption)
+ content))]
+ (..tag "table" attributes
+ content)))
+
+ (template [<name> <doc-type>]
+ [(def: #export <name>
+ (-> Head Body Document)
+ (let [doc-type <doc-type>]
+ (function (_ head body)
+ (|> (..tag "html" (list) (..and head body))
+ :representation
+ (format doc-type)
+ :abstraction))))]
+
+ [html-5 "<!DOCTYPE html>"]
+ [html-4_01 (format "<!DOCTYPE HTML PUBLIC " text.double-quote "-//W3C//DTD HTML 4.01//EN" text.double-quote " " text.double-quote "http://www.w3.org/TR/html4/strict.dtd" text.double-quote ">")]
+ [xhtml-1_0 (format "<!DOCTYPE html PUBLIC " text.double-quote "-//W3C//DTD XHTML 1.0 Strict//EN" text.double-quote " " text.double-quote "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" text.double-quote ">")]
+ [xhtml-1_1 (format "<!DOCTYPE html PUBLIC " text.double-quote "-//W3C//DTD XHTML 1.1//EN" text.double-quote " " text.double-quote "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" text.double-quote ">")]
+ )
+ )