module FF.HTML.StyleSheet.Core where
open import Agda.Builtin.String using (String; primStringAppend)
open import Agda.Primitive using (Level; lsuc) renaming (Set to Type; _⊔_ to _lmax_)
open import Cubical.Data.Bool.Base using (if_then_else_)
open import Cubical.Data.List.Base using (List; []; _∷_)
open import Cubical.Data.Nat.Base using (ℕ; zero; suc; _+_)
import FF.CSS.Core as CSS
import FF.CSS.Render as CSSRender
import FF.HTML.Core as HTML
import FF.HTML.Render as HTMLRender
import FF.HTML.Selector.Core as Selector
import FF.HTML.Selector.Semantics as SelectorSemantics
private
infixr 5 _++_
infixr 5 _++L_
_++_ : String -> String -> String
_++_ = primStringAppend
_++L_ : {a : Level} {A : Type a} -> List A -> List A -> List A
[] ++L ys = ys
(x ∷ xs) ++L ys = x ∷ (xs ++L ys)
levelOf :
Level -> Level -> Level -> Level ->
Level -> Level ->
Level
levelOf tag key val atom property value =
tag lmax key lmax val lmax atom lmax property lmax value
data StyleRule
{tag key val atom property value : Level}
(H : HTML.Spec tag key val atom)
(C : CSS.Spec property value)
: Type (levelOf tag key val atom property value) where
styleRule :
Selector.SelectorList H ->
List (CSS.Declaration C) ->
StyleRule H C
data StyleSheet
{tag key val atom property value : Level}
(H : HTML.Spec tag key val atom)
(C : CSS.Spec property value)
: Type (levelOf tag key val atom property value) where
emptySheet : StyleSheet H C
ruleCons : StyleRule H C -> StyleSheet H C -> StyleSheet H C
infixr 5 ruleCons
fromRules :
{tag key val atom property value : Level}
{H : HTML.Spec tag key val atom}
{C : CSS.Spec property value} ->
List (StyleRule H C) ->
StyleSheet H C
fromRules [] = emptySheet
fromRules (rule ∷ rest) = ruleCons rule (fromRules rest)
toRules :
{tag key val atom property value : Level}
{H : HTML.Spec tag key val atom}
{C : CSS.Spec property value} ->
StyleSheet H C ->
List (StyleRule H C)
toRules emptySheet = []
toRules (ruleCons rule rest) = rule ∷ toRules rest
renderDeclarations :
{property value : Level}
{C : CSS.Spec property value} ->
CSSRender.Renderer C ->
List (CSS.Declaration C) ->
String
renderDeclarations renderer [] = ""
renderDeclarations renderer (declaration ∷ []) =
CSSRender.renderDeclarationWithSemicolon renderer declaration
renderDeclarations renderer (declaration ∷ rest@(_ ∷ _)) =
CSSRender.renderDeclarationWithSemicolon renderer declaration ++
" " ++
renderDeclarations renderer rest
renderRule :
{tag key val atom property value : Level}
{H : HTML.Spec tag key val atom}
{C : CSS.Spec property value} ->
HTMLRender.Renderer H ->
CSSRender.Renderer C ->
StyleRule H C ->
String
renderRule htmlRenderer cssRenderer (styleRule selectors declarations) =
Selector.renderSelectorList htmlRenderer selectors ++
" { " ++
renderDeclarations cssRenderer declarations ++
" }"
renderStyleSheet :
{tag key val atom property value : Level}
{H : HTML.Spec tag key val atom}
{C : CSS.Spec property value} ->
HTMLRender.Renderer H ->
CSSRender.Renderer C ->
StyleSheet H C ->
String
renderStyleSheet htmlRenderer cssRenderer emptySheet = ""
renderStyleSheet htmlRenderer cssRenderer (ruleCons rule emptySheet) =
renderRule htmlRenderer cssRenderer rule
renderStyleSheet htmlRenderer cssRenderer (ruleCons rule rest@(ruleCons _ _)) =
renderRule htmlRenderer cssRenderer rule ++
"\n" ++
renderStyleSheet htmlRenderer cssRenderer rest
mapDeclarations :
{property value property' value' : Level}
{From : CSS.Spec property value}
{To : CSS.Spec property' value'} ->
CSS.Map From To ->
List (CSS.Declaration From) ->
List (CSS.Declaration To)
mapDeclarations f [] = []
mapDeclarations f (declaration ∷ rest) =
CSS.mapDeclaration f declaration ∷ mapDeclarations f rest
mapRule :
{tag key val atom tag' key' val' atom' property value property' value' : Level}
{HtmlFrom : HTML.Spec tag key val atom}
{HtmlTo : HTML.Spec tag' key' val' atom'}
{CssFrom : CSS.Spec property value}
{CssTo : CSS.Spec property' value'} ->
HTML.Map HtmlFrom HtmlTo ->
CSS.Map CssFrom CssTo ->
StyleRule HtmlFrom CssFrom ->
StyleRule HtmlTo CssTo
mapRule htmlMap cssMap (styleRule selectors declarations) =
styleRule
(Selector.mapSelectorList htmlMap selectors)
(mapDeclarations cssMap declarations)
mapStyleSheet :
{tag key val atom tag' key' val' atom' property value property' value' : Level}
{HtmlFrom : HTML.Spec tag key val atom}
{HtmlTo : HTML.Spec tag' key' val' atom'}
{CssFrom : CSS.Spec property value}
{CssTo : CSS.Spec property' value'} ->
HTML.Map HtmlFrom HtmlTo ->
CSS.Map CssFrom CssTo ->
StyleSheet HtmlFrom CssFrom ->
StyleSheet HtmlTo CssTo
mapStyleSheet htmlMap cssMap emptySheet = emptySheet
mapStyleSheet htmlMap cssMap (ruleCons rule rest) =
ruleCons
(mapRule htmlMap cssMap rule)
(mapStyleSheet htmlMap cssMap rest)
record Specificity : Type where
constructor specificity
field
idCount : ℕ
classCount : ℕ
typeCount : ℕ
open Specificity public
zeroSpecificity : Specificity
zeroSpecificity = specificity zero zero zero
idSpecificity : Specificity
idSpecificity = specificity (suc zero) zero zero
classSpecificity : Specificity
classSpecificity = specificity zero (suc zero) zero
typeSpecificity : Specificity
typeSpecificity = specificity zero zero (suc zero)
_+specificity_ : Specificity -> Specificity -> Specificity
specificity ids classes types +specificity specificity ids' classes' types' =
specificity (ids + ids') (classes + classes') (types + types')
untaggedSimpleSpecificity :
{tag key val atom : Level}
{H : HTML.Spec tag key val atom} ->
Selector.UntypedSimple H ->
Specificity
untaggedSimpleSpecificity (Selector.rawId _) = idSpecificity
untaggedSimpleSpecificity (Selector.rawClass _) = classSpecificity
untaggedSimpleSpecificity (Selector.rawAttribute _ _) = classSpecificity
untaggedSimpleSpecificity (Selector.rawPseudoClass _) = classSpecificity
untaggedSimpleSpecificity (Selector.rawFunctionalPseudoClass _ _) = classSpecificity
typedSimpleSpecificity :
{tag key val atom : Level}
{H : HTML.Spec tag key val atom}
{tagName : HTML.Tag H} ->
Selector.TypedSimple H tagName ->
Specificity
typedSimpleSpecificity (Selector.id _) = idSpecificity
typedSimpleSpecificity (Selector.class _) = classSpecificity
typedSimpleSpecificity (Selector.attribute _ _) = classSpecificity
typedSimpleSpecificity (Selector.pseudoClass _) = classSpecificity
typedSimpleSpecificity (Selector.functionalPseudoClass _ _) = classSpecificity
untaggedSimplesSpecificity :
{tag key val atom : Level}
{H : HTML.Spec tag key val atom} ->
List (Selector.UntypedSimple H) ->
Specificity
untaggedSimplesSpecificity [] = zeroSpecificity
untaggedSimplesSpecificity (simple ∷ rest) =
untaggedSimpleSpecificity simple +specificity
untaggedSimplesSpecificity rest
typedSimplesSpecificity :
{tag key val atom : Level}
{H : HTML.Spec tag key val atom}
{tagName : HTML.Tag H} ->
List (Selector.TypedSimple H tagName) ->
Specificity
typedSimplesSpecificity [] = zeroSpecificity
typedSimplesSpecificity (simple ∷ rest) =
typedSimpleSpecificity simple +specificity
typedSimplesSpecificity rest
compoundSpecificity :
{tag key val atom : Level}
{H : HTML.Spec tag key val atom} ->
Selector.CompoundSelector H ->
Specificity
compoundSpecificity (Selector.universal simples) =
untaggedSimplesSpecificity simples
compoundSpecificity (Selector.typed _ simples) =
typeSpecificity +specificity typedSimplesSpecificity simples
complexSpecificity :
{tag key val atom : Level}
{H : HTML.Spec tag key val atom} ->
Selector.ComplexSelector H ->
Specificity
complexSpecificity (Selector.compound selector) =
compoundSpecificity selector
complexSpecificity (Selector.combine left _ right) =
complexSpecificity left +specificity compoundSpecificity right
selectorListSpecificities :
{tag key val atom : Level}
{H : HTML.Spec tag key val atom} ->
Selector.SelectorList H ->
List Specificity
selectorListSpecificities (Selector.one selector) =
complexSpecificity selector ∷ []
selectorListSpecificities (Selector.cons selector rest) =
complexSpecificity selector ∷ selectorListSpecificities rest
matchingSelectorSpecificities :
{tag key val atom : Level}
{H : HTML.Spec tag key val atom} ->
SelectorSemantics.SelectorMatcher H ->
SelectorSemantics.ElementContext H ->
Selector.SelectorList H ->
List Specificity
matchingSelectorSpecificities matcher context (Selector.one selector) =
if SelectorSemantics.matchesComplex matcher context selector
then complexSpecificity selector ∷ []
else []
matchingSelectorSpecificities matcher context (Selector.cons selector rest) =
if SelectorSemantics.matchesComplex matcher context selector
then complexSpecificity selector ∷
matchingSelectorSpecificities matcher context rest
else matchingSelectorSpecificities matcher context rest
data MatchedRule
{tag key val atom property value : Level}
(H : HTML.Spec tag key val atom)
(C : CSS.Spec property value)
: Type (levelOf tag key val atom property value) where
matchedRule :
ℕ ->
SelectorSemantics.ElementContext H ->
List Specificity ->
List (CSS.Declaration C) ->
MatchedRule H C
matchingRulesForContextFrom :
{tag key val atom property value : Level}
{H : HTML.Spec tag key val atom}
{C : CSS.Spec property value} ->
ℕ ->
SelectorSemantics.SelectorMatcher H ->
SelectorSemantics.ElementContext H ->
StyleSheet H C ->
List (MatchedRule H C)
matchingRulesForContextFrom order matcher context emptySheet = []
matchingRulesForContextFrom order matcher context (ruleCons (styleRule selectors declarations) rest)
with matchingSelectorSpecificities matcher context selectors
... | [] = matchingRulesForContextFrom (suc order) matcher context rest
... | firstSpecificity ∷ restSpecificities =
matchedRule order context (firstSpecificity ∷ restSpecificities) declarations ∷
matchingRulesForContextFrom (suc order) matcher context rest
matchingRulesForContext :
{tag key val atom property value : Level}
{H : HTML.Spec tag key val atom}
{C : CSS.Spec property value} ->
SelectorSemantics.SelectorMatcher H ->
SelectorSemantics.ElementContext H ->
StyleSheet H C ->
List (MatchedRule H C)
matchingRulesForContext = matchingRulesForContextFrom zero
declarationsForContext :
{tag key val atom property value : Level}
{H : HTML.Spec tag key val atom}
{C : CSS.Spec property value} ->
SelectorSemantics.SelectorMatcher H ->
SelectorSemantics.ElementContext H ->
StyleSheet H C ->
List (CSS.Declaration C)
declarationsForContext matcher context emptySheet = []
declarationsForContext matcher context (ruleCons (styleRule selectors declarations) rest)
with matchingSelectorSpecificities matcher context selectors
... | [] = declarationsForContext matcher context rest
... | _ ∷ _ = declarations ++L declarationsForContext matcher context rest
mutual
matchingStyleSheetWithContext :
{tag key val atom property value : Level}
{H : HTML.Spec tag key val atom}
{C : CSS.Spec property value} ->
SelectorSemantics.SelectorMatcher H ->
StyleSheet H C ->
SelectorSemantics.ElementContext H ->
HTML.Html H ->
List (MatchedRule H C)
matchingStyleSheetWithContext matcher sheet context (HTML.atomic _) = []
matchingStyleSheetWithContext matcher sheet context (HTML.node _ _ children) =
matchingRulesForContext matcher context sheet ++L
matchingStyleSheetChildren matcher sheet context [] children
matchingStyleSheetChildren :
{tag key val atom property value : Level}
{H : HTML.Spec tag key val atom}
{C : CSS.Spec property value} ->
SelectorSemantics.SelectorMatcher H ->
StyleSheet H C ->
SelectorSemantics.ElementContext H ->
List (HTML.Html H) ->
List (HTML.Html H) ->
List (MatchedRule H C)
matchingStyleSheetChildren matcher sheet parent previous [] = []
matchingStyleSheetChildren matcher sheet parent previous (HTML.atomic content ∷ rest) =
matchingStyleSheetChildren matcher sheet parent (HTML.atomic content ∷ previous) rest
matchingStyleSheetChildren matcher sheet parent previous
(HTML.node tagName attrs children ∷ rest) =
matchingStyleSheetWithContext matcher sheet
(SelectorSemantics.childContext parent previous tagName attrs children rest)
(HTML.node tagName attrs children)
++L
matchingStyleSheetChildren matcher sheet parent
(HTML.node tagName attrs children ∷ previous)
rest
matchingStyleSheet :
{tag key val atom property value : Level}
{H : HTML.Spec tag key val atom}
{C : CSS.Spec property value} ->
SelectorSemantics.SelectorMatcher H ->
StyleSheet H C ->
HTML.Html H ->
List (MatchedRule H C)
matchingStyleSheet matcher sheet (HTML.atomic _) = []
matchingStyleSheet matcher sheet (HTML.node tagName attrs children) =
matchingStyleSheetWithContext matcher sheet
(SelectorSemantics.rootContext tagName attrs children)
(HTML.node tagName attrs children)
data ElementStyle
{tag key val atom property value : Level}
(H : HTML.Spec tag key val atom)
(C : CSS.Spec property value)
: Type (levelOf tag key val atom property value) where
elementStyle :
SelectorSemantics.ElementContext H ->
List (CSS.Declaration C) ->
ElementStyle H C
mutual
styledElementsWithContext :
{tag key val atom property value : Level}
{H : HTML.Spec tag key val atom}
{C : CSS.Spec property value} ->
SelectorSemantics.SelectorMatcher H ->
StyleSheet H C ->
SelectorSemantics.ElementContext H ->
HTML.Html H ->
List (ElementStyle H C)
styledElementsWithContext matcher sheet context (HTML.atomic _) = []
styledElementsWithContext matcher sheet context (HTML.node _ _ children)
with declarationsForContext matcher context sheet
... | [] = styledElementChildren matcher sheet context [] children
... | declaration ∷ declarations =
elementStyle context (declaration ∷ declarations) ∷
styledElementChildren matcher sheet context [] children
styledElementChildren :
{tag key val atom property value : Level}
{H : HTML.Spec tag key val atom}
{C : CSS.Spec property value} ->
SelectorSemantics.SelectorMatcher H ->
StyleSheet H C ->
SelectorSemantics.ElementContext H ->
List (HTML.Html H) ->
List (HTML.Html H) ->
List (ElementStyle H C)
styledElementChildren matcher sheet parent previous [] = []
styledElementChildren matcher sheet parent previous (HTML.atomic content ∷ rest) =
styledElementChildren matcher sheet parent (HTML.atomic content ∷ previous) rest
styledElementChildren matcher sheet parent previous
(HTML.node tagName attrs children ∷ rest) =
styledElementsWithContext matcher sheet
(SelectorSemantics.childContext parent previous tagName attrs children rest)
(HTML.node tagName attrs children)
++L
styledElementChildren matcher sheet parent
(HTML.node tagName attrs children ∷ previous)
rest
styledElements :
{tag key val atom property value : Level}
{H : HTML.Spec tag key val atom}
{C : CSS.Spec property value} ->
SelectorSemantics.SelectorMatcher H ->
StyleSheet H C ->
HTML.Html H ->
List (ElementStyle H C)
styledElements matcher sheet (HTML.atomic _) = []
styledElements matcher sheet (HTML.node tagName attrs children) =
styledElementsWithContext matcher sheet
(SelectorSemantics.rootContext tagName attrs children)
(HTML.node tagName attrs children)