Skip to content

Instantly share code, notes, and snippets.

@dralletje
Last active November 14, 2020 17:02
Show Gist options
  • Select an option

  • Save dralletje/c9772c35dcb542325b24c9609c643c23 to your computer and use it in GitHub Desktop.

Select an option

Save dralletje/c9772c35dcb542325b24c9609c643c23 to your computer and use it in GitHub Desktop.
### A Pluto.jl notebook ###
# v0.12.10
using Markdown
using InteractiveUtils
# ╔═╡ 14bec8b8-13b2-11eb-2a31-5f0544ad5b56
function displayonly(m::Module)
if isdefined(m, :PlutoForceDisplay)
return m.PlutoForceDisplay
else
isdefined(m, :PlutoRunner) && parentmodule(m) == Main
end
end
# ╔═╡ c70c68d2-1300-11eb-1628-9b8b38294867
macro displayonly(ex) displayonly(__module__) ? esc(ex) : nothing end
# ╔═╡ 9b354d80-13b9-11eb-2efa-ffd84ab4ba5a
md"## Examples"
# ╔═╡ 0bfd6e84-12f8-11eb-2591-17ea18055c4b
# ╔═╡ 18de363a-13c0-11eb-0031-79878171f846
md"#### Interpolate a dict of styles"
# ╔═╡ e2753d84-1304-11eb-19af-4186896ccb45
@displayonly header_styles = Dict("font-size" => "25px", "padding-left" => "10px")
# ╔═╡ 9966edc0-1304-11eb-3316-5f95504aa597
@displayonly html_in_string = "<script>alert('Suckers!!!')</div>"
# ╔═╡ 5ca527d0-13bf-11eb-3001-71c88c91f01b
md"#### Interpolate an html compatible value as html"
# ╔═╡ 32fcd720-13bf-11eb-3c3b-955848d68b60
md"#### Interpolate a dict of attributes"
# ╔═╡ 72fcfbce-12ee-11eb-15b0-67a7b29ae044
@displayonly xxx = Dict(
"class" => "blue light",
"style" => Dict("padding-left" => "50px + 1em"),
)
# ╔═╡ 47cd213a-13b7-11eb-309c-893e7469adbf
md"## Julia interpolation parser"
# ╔═╡ 69890450-12f5-11eb-3917-9d663c615c4e
struct Escaped value end
# ╔═╡ 22bab624-13b6-11eb-182e-c136d825c2ff
md"## Interpolatable types"
# ╔═╡ ff833662-13bb-11eb-14c8-8b2aae2ef7db
md"###### Example helpers"
# ╔═╡ 12cc356c-13bb-11eb-2bc9-975719034118
@displayonly const INTERPOLATE_ME = "INTERPOLATE_ME"
# ╔═╡ 09e5ab8c-13bf-11eb-04dc-d3e4b982057e
md"#### Base type, nothing special"
# ╔═╡ 43baade0-13b6-11eb-013c-7f0863f3c190
abstract type InterpolatedValue end
# ╔═╡ 99d61060-13bd-11eb-2470-b35e3f5f86b1
function Base.show(io::IO, mime::MIME"text/html", x::InterpolatedValue)
throw("""
show text/html should be override for InterpolatedValue.
Got a $(typeof(x)) without html show overload.
""")
end
# ╔═╡ 7d18c9d2-13be-11eb-1652-51d07e7d1b16
md"#### interpolation is attribute"
# ╔═╡ 9bb8f20e-1283-11eb-3e92-57505be107dd
Base.@kwdef struct AttributeValue <: InterpolatedValue
name::String
value
end
# ╔═╡ 465a158c-1651-11eb-04bb-99ad4e4d311f
struct Javascript
content
end
# ╔═╡ 4bab6b12-1651-11eb-2841-332c94b5d4e2
Base.show(io::IO, mime::MIME"application/javascript", js::Javascript) = print(io, js.content)
# ╔═╡ 7aec637c-1651-11eb-2c95-07dd3cefff97
@displayonly onclickhandler = Javascript("""
alert("You clicked???")
""")
# ╔═╡ 6f610e5c-13ba-11eb-3062-fd8663b82dd0
md"#### Any text in between tags"
# ╔═╡ ebdef998-1285-11eb-14cc-8b85678a72c0
struct StateData <: InterpolatedValue value end
# ╔═╡ 4bdf4504-13bd-11eb-1746-e5b81e9e6d35
md"#### Inside an attribute, but without any quotes?!"
# ╔═╡ 04e1943a-1286-11eb-25eb-99f38df6c66a
struct AttributeUnquoted <: InterpolatedValue value end
# ╔═╡ e19fd3b8-13ba-11eb-04e4-2dde02446652
md"""#### Inside double quotes"""
# ╔═╡ 1e23dc34-1286-11eb-2957-335d24349a4d
struct AttributeDoubleQuoted <: InterpolatedValue value end
# ╔═╡ c62301c8-13ba-11eb-23a0-6b05cfbeaddd
md"""#### Inside single quotes"""
# ╔═╡ 24bb615c-1286-11eb-1106-478fafe4371c
struct AttributeSingleQuoted <: InterpolatedValue value end
# ╔═╡ 8aa807f6-13ba-11eb-155c-2784a14edf60
md"#### Interpolation anywhere inside a tag"
# ╔═╡ 48f3d7a2-1286-11eb-3598-6deee29c753a
struct BeforeAttributeName <: InterpolatedValue value end
# ╔═╡ 3d1e8308-12d3-11eb-3bf1-67d55eae7f50
function Base.show(io::IO, mime::MIME"text/html", x::BeforeAttributeName)
if x.value isa Dict
for (key, value) in pairs(x.value)
show(io, mime, AttributeValue(name=key, value=value))
print(io, " ")
end
elseif x.value isa Pair
show(io, mime, AttributeValue(name=x.value.first, value=x.value.second))
print(io, " ")
else
throw("invalid binding #2 $(typeof(x.value)) $(x.value)")
end
end
# ╔═╡ 7aabd782-13b6-11eb-38c0-89fda4e4513b
md"## The actual parser"
# ╔═╡ 93875e76-13b7-11eb-13c3-1d9acf4d6075
md"""
### InterpolateArray
An array that kinda tries to work as a string, but you can append `InterpolatedValue`s to it too.
"""
# ╔═╡ 2765fa4a-1281-11eb-18a4-f14999912807
struct InterpolateArray
arr::Array
end
# ╔═╡ 9229d198-13bb-11eb-096e-a17862e1e327
@displayonly function show_interpolation(arr::InterpolateArray)
for value in arr.arr
if value isa InterpolatedValue
return sprint(dump, value) |> Text
end
end
end
# ╔═╡ 1dff8458-164e-11eb-0468-633ba40ee340
const HtlString = InterpolateArray
# ╔═╡ 2d83c766-1281-11eb-04aa-25be5611842a
function Base.:*(arr::InterpolateArray, string::String)
if isempty(arr.arr)
InterpolateArray([string])
elseif typeof(last(arr.arr)) == String
InterpolateArray([
arr.arr[begin:end-1]...,
last(arr.arr) * string
])
else
InterpolateArray([arr.arr..., string])
end
end
# ╔═╡ 09cee43a-1282-11eb-0a46-c18ecd9625ce
function Base.:*(arr::InterpolateArray, something::InterpolatedValue)
InterpolateArray([arr.arr..., something])
end
# ╔═╡ 270fc9b6-12e5-11eb-2521-e5d37de17cce
function Base.length(arr::InterpolateArray)
sum(map(arr.arr) do x
if x isa AbstractString
length(x)
else
1
end
end)
end
# ╔═╡ 03487786-12e9-11eb-14db-4d97ecf364dc
function Base.getindex(arr::InterpolateArray, range::UnitRange)
InterpolateArray([
arr.arr[begin:end-1]...,
arr.arr[end][range]
])
end
# ╔═╡ 841f6af8-1286-11eb-2840-f509d5f19d52
function Base.show(io::IO, mime::MIME"text/html", array::InterpolateArray)
for item in array.arr
if item isa AbstractString
print(io, item)
else
show(io, mime, item)
end
end
end
# ╔═╡ 859f52b0-13b6-11eb-1845-612e0e717630
md"### Parser utils"
# ╔═╡ 7d7ecade-1274-11eb-1412-a78c710091d5
begin
const CODE_TAB = 9
const CODE_LF = 10
const CODE_FF = 12
const CODE_CR = 13
const CODE_SPACE = 32
const CODE_UPPER_A = 65
const CODE_UPPER_Z = 90
const CODE_LOWER_A = 97
const CODE_LOWER_Z = 122
const CODE_LT = 60
const CODE_GT = 62
const CODE_SLASH = 47
const CODE_DASH = 45
const CODE_BANG = 33
const CODE_EQ = 61
const CODE_DQUOTE = 34
const CODE_SQUOTE = 39
const CODE_QUESTION = 63
Text("> CharCode Constants")
end
# ╔═╡ 320ecfcc-12d8-11eb-3b17-4d428df9c64c
@enum HtlParserState STATE_DATA STATE_TAG_OPEN STATE_END_TAG_OPEN STATE_TAG_NAME STATE_BOGUS_COMMENT STATE_BEFORE_ATTRIBUTE_NAME STATE_AFTER_ATTRIBUTE_NAME STATE_ATTRIBUTE_NAME STATE_BEFORE_ATTRIBUTE_VALUE STATE_ATTRIBUTE_VALUE_DOUBLE_QUOTED STATE_ATTRIBUTE_VALUE_SINGLE_QUOTED STATE_ATTRIBUTE_VALUE_UNQUOTED STATE_AFTER_ATTRIBUTE_VALUE_QUOTED STATE_SELF_CLOSING_START_TAG STATE_COMMENT_START STATE_COMMENT_START_DASH STATE_COMMENT STATE_COMMENT_LESS_THAN_SIGN STATE_COMMENT_LESS_THAN_SIGN_BANG STATE_COMMENT_LESS_THAN_SIGN_BANG_DASH STATE_COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH STATE_COMMENT_END_DASH STATE_COMMENT_END STATE_COMMENT_END_BANG STATE_MARKUP_DECLARATION_OPEN
# ╔═╡ 401507ca-127a-11eb-0110-517057d53125
function entity(str::AbstractString)
@assert length(str) == 1
entity(str[1])
end
# ╔═╡ 8aa89fa0-1274-11eb-05a7-dfd699933cbe
entity(character::Char) = "&#$(Int(character));"
# ╔═╡ 829b96c6-1287-11eb-20cb-f3096f0b0703
function Base.show(io::IO, mime::MIME"text/html", child::StateData)
if showable(MIME("text/html"), child.value)
show(io, mime, child.value)
elseif child.value isa AbstractArray{HtlString}
for subchild in child.value
show(io, mime, subchild)
end
else
print(io, replace(string(child.value), r"[<&]" => entity))
end
end
# ╔═╡ d4a0546c-12cf-11eb-1d03-cd395f63f993
function Base.show(io::IO, ::MIME"text/html", x::AttributeUnquoted)
print(io, replace(x.value, r"[\s>&]" => entity))
end
# ╔═╡ 1edafc64-12d1-11eb-1a14-2f2e86cf7e94
function Base.show(io::IO, ::MIME"text/html", x::AttributeDoubleQuoted)
print(io, replace(x.value, r"[\"&]" => entity))
end
# ╔═╡ 42d2a93e-12d1-11eb-1816-e994ba4b209e
function Base.show(io::IO, ::MIME"text/html", x::AttributeSingleQuoted)
print(io, replace(x.value, r"['&]" => entity))
end
# ╔═╡ 8d20f660-1274-11eb-2bc8-95c2558b7f4b
function isAsciiAlphaCode(code::Int)::Bool
return (
CODE_UPPER_A <= code
&& code <= CODE_UPPER_Z
) || (
CODE_LOWER_A <= code
&& code <= CODE_LOWER_Z
)
end
# ╔═╡ a62252c6-1274-11eb-0605-45db63c8abfb
function isSpaceCode(code)
return ( code === CODE_TAB
|| code === CODE_LF
|| code === CODE_FF
|| code === CODE_SPACE
|| code === CODE_CR
) # normalize newlines
end
# ╔═╡ d97167b4-1273-11eb-2be4-ad073ff79006
function hypertext(args)
state = STATE_DATA
string = InterpolateArray([])
nameStart = 0
nameEnd = 0
for j in 1:length(args)
if args[j] isa Escaped
value = args[j].value
if state == STATE_DATA
string *= StateData(value)
elseif state == STATE_BEFORE_ATTRIBUTE_VALUE
state = STATE_ATTRIBUTE_VALUE_UNQUOTED
name = args[j - 1][nameStart:nameEnd]
prefixlength = length(string) - nameStart
string = InterpolateArray([
string.arr[begin:end-1]...,
string.arr[end][begin:nameStart - 1]
])
# string = string[1:(nameStart - length(strings[j - 1]))]
string *= AttributeValue(name, value)
elseif state == STATE_ATTRIBUTE_VALUE_UNQUOTED
string *= AttributeUnquoted(value)
elseif state == STATE_ATTRIBUTE_VALUE_SINGLE_QUOTED
string *= AttributeSingleQuoted(value)
elseif state == STATE_ATTRIBUTE_VALUE_DOUBLE_QUOTED
string *= AttributeDoubleQuoted(value)
elseif state == STATE_BEFORE_ATTRIBUTE_NAME
string *= BeforeAttributeName(value)
elseif state == STATE_COMMENT || true
throw("invalid binding #1 $(state)")
end
else
input = args[j]
inputlength = length(input)
i = 1
while i <= inputlength
code = Int(input[i])
if state == STATE_DATA
if code === CODE_LT
state = STATE_TAG_OPEN
end
elseif state == STATE_TAG_OPEN
if code === CODE_BANG
state = STATE_MARKUP_DECLARATION_OPEN
elseif code === CODE_SLASH
state = STATE_END_TAG_OPEN
elseif isAsciiAlphaCode(code)
state = STATE_TAG_NAME
i -= 1
elseif code === CODE_QUESTION
state = STATE_BOGUS_COMMENT
i -= 1
else
state = STATE_DATA
i -= 1
end
elseif state == STATE_END_TAG_OPEN
if isAsciiAlphaCode(code)
state = STATE_TAG_NAME
i -= 1
elseif code === CODE_GT
state = STATE_DATA
else
state = STATE_BOGUS_COMMENT
i -= 1
end
elseif state == STATE_TAG_NAME
if isSpaceCode(code)
state = STATE_BEFORE_ATTRIBUTE_NAME
elseif code === CODE_SLASH
state = STATE_SELF_CLOSING_START_TAG
elseif code === CODE_GT
state = STATE_DATA
end
elseif state == STATE_BEFORE_ATTRIBUTE_NAME
if isSpaceCode(code)
nothing
elseif code === CODE_SLASH || code === CODE_GT
state = STATE_AFTER_ATTRIBUTE_NAME
i -= 1
elseif code === CODE_EQ
state = STATE_ATTRIBUTE_NAME
nameStart = i + 1
nameEnd = nothing
else
state = STATE_ATTRIBUTE_NAME
i -= 1
nameStart = i + 1
nameEnd = nothing
end
elseif state == STATE_ATTRIBUTE_NAME
if isSpaceCode(code) || code === CODE_SLASH || code === CODE_GT
state = STATE_AFTER_ATTRIBUTE_NAME
nameEnd = i - 1
i -= 1
elseif code === CODE_EQ
state = STATE_BEFORE_ATTRIBUTE_VALUE
nameEnd = i - 1
end
elseif state == STATE_AFTER_ATTRIBUTE_NAME
if isSpaceCode(code)
# ignore
elseif code === CODE_SLASH
state = STATE_SELF_CLOSING_START_TAG
elseif code === CODE_EQ
state = STATE_BEFORE_ATTRIBUTE_VALUE
elseif code === CODE_GT
state = STATE_DATA
else
state = STATE_ATTRIBUTE_NAME
i -= 1
nameStart = i + 1
nameEnd = nothing
end
elseif state == STATE_BEFORE_ATTRIBUTE_VALUE
if isSpaceCode(code)
# continue
elseif code === CODE_DQUOTE
state = STATE_ATTRIBUTE_VALUE_DOUBLE_QUOTED
elseif code === CODE_SQUOTE
state = STATE_ATTRIBUTE_VALUE_SINGLE_QUOTED
elseif code === CODE_GT
state = STATE_DATA
else
state = STATE_ATTRIBUTE_VALUE_UNQUOTED
i -= 1
end
elseif state == STATE_ATTRIBUTE_VALUE_DOUBLE_QUOTED
if code === CODE_DQUOTE
state = STATE_AFTER_ATTRIBUTE_VALUE_QUOTED
end
elseif state == STATE_ATTRIBUTE_VALUE_SINGLE_QUOTED
if code === CODE_SQUOTE
state = STATE_AFTER_ATTRIBUTE_VALUE_QUOTED
end
elseif state == STATE_ATTRIBUTE_VALUE_UNQUOTED
if isSpaceCode(code)
state = STATE_BEFORE_ATTRIBUTE_NAME
elseif code === CODE_GT
state = STATE_DATA
end
elseif state == STATE_AFTER_ATTRIBUTE_VALUE_QUOTED
if isSpaceCode(code)
state = STATE_BEFORE_ATTRIBUTE_NAME
elseif code === CODE_SLASH
state = STATE_SELF_CLOSING_START_TAG
elseif code === CODE_GT
state = STATE_DATA
else
state = STATE_BEFORE_ATTRIBUTE_NAME
i -= 1
end
elseif state == STATE_SELF_CLOSING_START_TAG
if code === CODE_GT
state = STATE_DATA
else
state = STATE_BEFORE_ATTRIBUTE_NAME
i -= 1
end
elseif state == STATE_BOGUS_COMMENT
if code === CODE_GT
state = STATE_DATA
end
elseif state == STATE_COMMENT_START
if code === CODE_DASH
state = STATE_COMMENT_START_DASH
elseif code === CODE_GT
state = STATE_DATA
else
state = STATE_COMMENT
i -= 1
end
elseif state == STATE_COMMENT_START_DASH
if code === CODE_DASH
state = STATE_COMMENT_END
elseif code === CODE_GT
state = STATE_DATA
else
state = STATE_COMMENT
i -= 1
end
elseif state == STATE_COMMENT
if code === CODE_LT
state = STATE_COMMENT_LESS_THAN_SIGN
elseif code === CODE_DASH
state = STATE_COMMENT_END_DASH
end
elseif state == STATE_COMMENT_LESS_THAN_SIGN
if code === CODE_BANG
state = STATE_COMMENT_LESS_THAN_SIGN_BANG
elseif code !== CODE_LT
state = STATE_COMMENT
i -= 1
end
elseif state == STATE_COMMENT_LESS_THAN_SIGN_BANG
if code === CODE_DASH
state = STATE_COMMENT_LESS_THAN_SIGN_BANG_DASH
else
state = STATE_COMMENT
i -= 1
end
elseif state == STATE_COMMENT_LESS_THAN_SIGN_BANG_DASH
if code === CODE_DASH
state = STATE_COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH
else
state = STATE_COMMENT_END
i -= 1
end
elseif state == STATE_COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH
state = STATE_COMMENT_END
i -= 1
elseif state == STATE_COMMENT_END_DASH
if code === CODE_DASH
state = STATE_COMMENT_END
else
state = STATE_COMMENT
i -= 1
end
elseif state == STATE_COMMENT_END
if code === CODE_GT
state = STATE_DATA
elseif code === CODE_BANG
state = STATE_COMMENT_END_BANG
elseif code !== CODE_DASH
state = STATE_COMMENT
i -= 1
end
elseif state == STATE_COMMENT_END_BANG
if code === CODE_DASH
state = STATE_COMMENT_END_DASH
elseif code === CODE_GT
state = STATE_DATA
else
state = STATE_COMMENT
i -= 1
end
elseif state == STATE_MARKUP_DECLARATION_OPEN
if code === CODE_DASH && Int(input[i + 1]) == CODE_DASH
state = STATE_COMMENT_START
i += 1
else # Note: CDATA and DOCTYPE unsupported!
state = STATE_BOGUS_COMMENT
i -= 1
end
else
state = nothing
end
i = i + 1
end
string *= input
end
end
return string
end
# ╔═╡ 50b261ae-1284-11eb-1496-d300a0e1d2a0
macro htl(expr)
if expr isa String
return hypertext([expr])
end
@assert expr.head == :string
quote
local arguments::Array{Any} = []
$(map(expr.args) do interpolation
if interpolation isa Expr
if interpolation.head == :...
quote
for sub_interpolation in $(esc(interpolation.args[1]))
push!(arguments, Escaped(sub_interpolation))
end
end
else
quote
push!(arguments, Escaped($(esc(interpolation))))
end
end
elseif interpolation isa String
quote
push!(arguments, $(interpolation))
end
else
quote
push!(arguments, Escaped($(esc(interpolation))))
end
end
end...)
hypertext(arguments)
end
end
# ╔═╡ c4e9901e-12d5-11eb-1c3c-f7a267c38b06
@displayonly @htl """<style class="showthestyles">
.red {
background-color: red;
}
.blue {
background-color: blue;
}
.light {
color: white;
font-weight: bold;
}
"""
# ╔═╡ 6a44eea0-13b7-11eb-2ad5-4b6caa17b60b
@displayonly @htl("""
<div
class=$("blue" * " " * "light")
style=$(header_styles)
>
$(html"<div>Big Font</div>")
<div style=$(:fontSize => "12px")>
$(html_in_string)
</div>
</div>
""")
# ╔═╡ f8f20860-12ea-11eb-2ae2-a108ae05e1a1
@displayonly @htl("""
<div class=$("blue" * " " * "light")>
$(html"<div>WOW</div>")
</div>
""")
# ╔═╡ 47a8de5c-12ee-11eb-15a5-97783fdea760
@displayonly @htl("""
<div $(xxx...)>
Class: $(xxx["class"])
</div>
""")
# ╔═╡ 9bbfccaa-13be-11eb-0f5a-ab652326e658
@displayonly show_interpolation(
@htl """<div myattribute=$(INTERPOLATE_ME) />"""
)
# ╔═╡ 456689da-1651-11eb-2ea8-67c3eb7fd87e
@displayonly @htl """<div onclick=$(onclickhandler)>Click me</div>"""
# ╔═╡ 6413c7b2-13bd-11eb-399f-5df29575805e
@displayonly show_interpolation(
@htl """<div>$(INTERPOLATE_ME)</div>"""
)
# ╔═╡ 579e4f34-13bd-11eb-34d5-d5e71a889500
@displayonly show_interpolation(
@htl """<div style=font-size:$(INTERPOLATE_ME) />"""
)
# ╔═╡ 3a5eca5e-13bb-11eb-0722-ad238adcae34
@displayonly show_interpolation(
@htl """<div style="font-size: $(INTERPOLATE_ME)" />"""
)
# ╔═╡ ddc3d9a4-13bc-11eb-0b32-2f75c621cb29
@displayonly show_interpolation(
@htl """<div style='font-size: $(INTERPOLATE_ME)' />"""
)
# ╔═╡ 302d77d6-13bd-11eb-343b-91f686ef5ad8
@displayonly show_interpolation(
@htl """<div tagbefore="" $(INTERPOLATE_ME) tagafter='' />"""
)
# ╔═╡ a622b5cc-1274-11eb-164d-0b63d950630a
function isObjectLiteral(value)
typeof(value) == Dict
# return value && value.toString === Object.prototype.toString
end
# ╔═╡ e0a16876-13b7-11eb-3b44-297b398657d0
md"## Simple CSS formatter"
# ╔═╡ f7ed3046-1303-11eb-2174-b5995b50ed27
function camelcase_to_dashes(str::String)
replace(str, r"[A-Z]" => (x -> "-$(lowercase(x))"))
end
# ╔═╡ 6b87ab40-12f9-11eb-2656-796e5ace3f7d
css_value(key, value) = string(value)
# ╔═╡ 6d464c46-12ef-11eb-3ccd-375549d552ab
"Convert numbers into pixel values"
css_value(key, value::Real) = "$(value)px"
# ╔═╡ 5e9ff658-12ef-11eb-1725-bd2616fe3463
css_value(key, value::AbstractString) = value
# ╔═╡ d3807cae-1303-11eb-0813-d909f52fdfcb
"""Un-camelcase symbols if used as keys (eg :fontSize => "font-size")"""
css_key(key::Symbol) = camelcase_to_dashes(string(key))
# ╔═╡ 0082c536-13bd-11eb-0852-8f42e5e9a36a
css_key(key::String) = key
# ╔═╡ 2789a164-12ef-11eb-0363-cf772908ce7a
function render_inline_css(styles::Dict)
result = ""
for (key, value) in pairs(styles)
result *= render_inline_css(key => value)
end
result
end
# ╔═╡ 5476cce0-1300-11eb-0de4-0bf07d830d30
function render_inline_css(style::Tuple{Pair})
result = ""
for (key, value) in styles
result *= render_inline_css(key => value)
end
result
end
# ╔═╡ 6854c1f2-1300-11eb-0370-1dc63273e570
function render_inline_css((key, value)::Pair)
"$(css_key(key)): $(css_value(key, value));"
end
# ╔═╡ 3b5caba2-1287-11eb-193c-5bee1ef582c5
function Base.show(io::IO, mime::MIME"text/html", attribute::AttributeValue)
value = attribute.value
result = if value === nothing || value == false
""
else
righthandside = if value === true
"\"\""
elseif (
attribute.name === "style" &&
hasmethod(render_inline_css, Tuple{typeof(attribute.value)})
)
render_inline_css(attribute.value)
elseif showable(MIME("application/javascript"), attribute.value)
sprint(show, MIME("application/javascript"), attribute.value)
else
string(attribute.value)
end
escaped = replace(righthandside, r"^['\"]|[\s>&]" => entity)
"$(attribute.name)=$(escaped)"
end
print(io, result)
end
# ╔═╡ Cell order:
# ╠═c70c68d2-1300-11eb-1628-9b8b38294867
# ╠═14bec8b8-13b2-11eb-2a31-5f0544ad5b56
# ╟─9b354d80-13b9-11eb-2efa-ffd84ab4ba5a
# ╠═0bfd6e84-12f8-11eb-2591-17ea18055c4b
# ╠═c4e9901e-12d5-11eb-1c3c-f7a267c38b06
# ╟─18de363a-13c0-11eb-0031-79878171f846
# ╠═e2753d84-1304-11eb-19af-4186896ccb45
# ╟─9966edc0-1304-11eb-3316-5f95504aa597
# ╠═6a44eea0-13b7-11eb-2ad5-4b6caa17b60b
# ╟─5ca527d0-13bf-11eb-3001-71c88c91f01b
# ╠═f8f20860-12ea-11eb-2ae2-a108ae05e1a1
# ╟─32fcd720-13bf-11eb-3c3b-955848d68b60
# ╠═72fcfbce-12ee-11eb-15b0-67a7b29ae044
# ╠═47a8de5c-12ee-11eb-15a5-97783fdea760
# ╟─47cd213a-13b7-11eb-309c-893e7469adbf
# ╟─69890450-12f5-11eb-3917-9d663c615c4e
# ╟─50b261ae-1284-11eb-1496-d300a0e1d2a0
# ╟─22bab624-13b6-11eb-182e-c136d825c2ff
# ╟─ff833662-13bb-11eb-14c8-8b2aae2ef7db
# ╟─12cc356c-13bb-11eb-2bc9-975719034118
# ╠═9229d198-13bb-11eb-096e-a17862e1e327
# ╟─09e5ab8c-13bf-11eb-04dc-d3e4b982057e
# ╟─43baade0-13b6-11eb-013c-7f0863f3c190
# ╟─99d61060-13bd-11eb-2470-b35e3f5f86b1
# ╟─7d18c9d2-13be-11eb-1652-51d07e7d1b16
# ╠═9bbfccaa-13be-11eb-0f5a-ab652326e658
# ╟─9bb8f20e-1283-11eb-3e92-57505be107dd
# ╟─465a158c-1651-11eb-04bb-99ad4e4d311f
# ╟─4bab6b12-1651-11eb-2841-332c94b5d4e2
# ╠═7aec637c-1651-11eb-2c95-07dd3cefff97
# ╠═456689da-1651-11eb-2ea8-67c3eb7fd87e
# ╠═3b5caba2-1287-11eb-193c-5bee1ef582c5
# ╟─6f610e5c-13ba-11eb-3062-fd8663b82dd0
# ╠═6413c7b2-13bd-11eb-399f-5df29575805e
# ╟─ebdef998-1285-11eb-14cc-8b85678a72c0
# ╟─1dff8458-164e-11eb-0468-633ba40ee340
# ╠═829b96c6-1287-11eb-20cb-f3096f0b0703
# ╟─4bdf4504-13bd-11eb-1746-e5b81e9e6d35
# ╠═579e4f34-13bd-11eb-34d5-d5e71a889500
# ╠═04e1943a-1286-11eb-25eb-99f38df6c66a
# ╠═d4a0546c-12cf-11eb-1d03-cd395f63f993
# ╟─e19fd3b8-13ba-11eb-04e4-2dde02446652
# ╠═3a5eca5e-13bb-11eb-0722-ad238adcae34
# ╟─1e23dc34-1286-11eb-2957-335d24349a4d
# ╟─1edafc64-12d1-11eb-1a14-2f2e86cf7e94
# ╟─c62301c8-13ba-11eb-23a0-6b05cfbeaddd
# ╠═ddc3d9a4-13bc-11eb-0b32-2f75c621cb29
# ╟─24bb615c-1286-11eb-1106-478fafe4371c
# ╟─42d2a93e-12d1-11eb-1816-e994ba4b209e
# ╟─8aa807f6-13ba-11eb-155c-2784a14edf60
# ╠═302d77d6-13bd-11eb-343b-91f686ef5ad8
# ╟─48f3d7a2-1286-11eb-3598-6deee29c753a
# ╟─3d1e8308-12d3-11eb-3bf1-67d55eae7f50
# ╟─7aabd782-13b6-11eb-38c0-89fda4e4513b
# ╟─d97167b4-1273-11eb-2be4-ad073ff79006
# ╟─93875e76-13b7-11eb-13c3-1d9acf4d6075
# ╟─2765fa4a-1281-11eb-18a4-f14999912807
# ╟─2d83c766-1281-11eb-04aa-25be5611842a
# ╟─09cee43a-1282-11eb-0a46-c18ecd9625ce
# ╟─270fc9b6-12e5-11eb-2521-e5d37de17cce
# ╟─03487786-12e9-11eb-14db-4d97ecf364dc
# ╟─841f6af8-1286-11eb-2840-f509d5f19d52
# ╟─859f52b0-13b6-11eb-1845-612e0e717630
# ╟─7d7ecade-1274-11eb-1412-a78c710091d5
# ╟─320ecfcc-12d8-11eb-3b17-4d428df9c64c
# ╟─401507ca-127a-11eb-0110-517057d53125
# ╟─8aa89fa0-1274-11eb-05a7-dfd699933cbe
# ╟─8d20f660-1274-11eb-2bc8-95c2558b7f4b
# ╟─a62252c6-1274-11eb-0605-45db63c8abfb
# ╟─a622b5cc-1274-11eb-164d-0b63d950630a
# ╟─e0a16876-13b7-11eb-3b44-297b398657d0
# ╟─f7ed3046-1303-11eb-2174-b5995b50ed27
# ╟─6b87ab40-12f9-11eb-2656-796e5ace3f7d
# ╟─6d464c46-12ef-11eb-3ccd-375549d552ab
# ╟─5e9ff658-12ef-11eb-1725-bd2616fe3463
# ╟─d3807cae-1303-11eb-0813-d909f52fdfcb
# ╟─0082c536-13bd-11eb-0852-8f42e5e9a36a
# ╟─2789a164-12ef-11eb-0363-cf772908ce7a
# ╟─5476cce0-1300-11eb-0de4-0bf07d830d30
# ╟─6854c1f2-1300-11eb-0370-1dc63273e570
@dralletje

dralletje commented Nov 14, 2020 via email

Copy link
Copy Markdown
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment