This repository has been archived by the owner on Nov 15, 2022. It is now read-only.
/
Styler.purs
92 lines (79 loc) · 2.89 KB
/
Styler.purs
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
module Freedom.Styler
( Styler
, createStyler
, registerStyle
) where
import Prelude
import Data.Char (toCharCode)
import Data.Foldable (foldr)
import Data.Int (base36, toStringAs)
import Data.Int.Bits (xor, zshr)
import Data.Maybe (Maybe(..), fromJust)
import Data.String (Pattern(..), Replacement(..), joinWith, replaceAll, trim)
import Data.String.CodeUnits (toCharArray)
import Data.String.Regex (replace)
import Data.String.Regex.Flags (global)
import Data.String.Regex.Unsafe (unsafeRegex)
import Effect (Effect)
import Effect.Ref (Ref, modify_, new, read)
import Foreign.Object (Object, empty, insert, member, values)
import Partial.Unsafe (unsafePartial)
import SimpleEmitter (Emitter, createEmitter, emit, subscribe)
import Web.DOM.Document (createElement)
import Web.DOM.Element as E
import Web.DOM.Node (Node, appendChild, setTextContent)
import Web.DOM.ParentNode (QuerySelector(..), querySelector)
import Web.HTML (window)
import Web.HTML.HTMLDocument (body, toDocument, toParentNode)
import Web.HTML.HTMLElement (toNode)
import Web.HTML.Window (document)
data Event = Register
derive instance eqEvent :: Eq Event
derive instance ordEvent :: Ord Event
newtype Styler = Styler
{ emitter :: Emitter Event
, stylesRef :: Ref (Object String)
}
createStyler :: Effect Styler
createStyler = do
emitter <- createEmitter
stylesRef <- new empty
flip (subscribe Register) emitter do
styles <- joinWith "" <<< values <$> read stylesRef
getStyleNode >>= setTextContent styles
pure $ Styler { emitter, stylesRef }
registerStyle :: String -> Styler -> Effect String
registerStyle style (Styler s) = do
let minified = minify style
name = "d" <> generateHash minified
styles <- read s.stylesRef
unless (member name styles) do
let output = replaceToken name minified
modify_ (insert name output) s.stylesRef
emit Register s.emitter
pure name
replaceToken :: String -> String -> String
replaceToken instead target =
replaceAll (Pattern "&") (Replacement instead) target
minify :: String -> String
minify = trim >>> replaceReturns >>> replaceWhitespaces
where
replaceReturns = replaceAll (Pattern "\n") (Replacement "")
replaceWhitespaces = replace (unsafeRegex "\\s\\s+" global) " "
getStyleNode :: Effect Node
getStyleNode = do
maybeElement <- window >>= document <#> toParentNode >>= querySelector (QuerySelector $ "." <> className)
case maybeElement of
Just el -> pure $ E.toNode el
Nothing -> do
el <- window >>= document <#> toDocument >>= createElement "style"
E.setClassName className el
body' <- unsafePartial $ fromJust <$> (window >>= document >>= body) <#> toNode
appendChild (E.toNode el) body'
className :: String
className = "freedom-styler"
generateHash :: String -> String
generateHash str = toStringAs base36 $ zshr seed 0
where
culc char value = xor (value * 33) (toCharCode char)
seed = foldr culc 5381 $ toCharArray str