{-# LANGUAGE CPP #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-missing-fields #-}
module Text.Julius
(
js
, julius
, juliusFile
, jsFile
, juliusFileDebug
, jsFileDebug
, juliusFileReload
, jsFileReload
, JavascriptUrl
, Javascript (..)
, RawJavascript (..)
, ToJavascript (..)
, RawJS (..)
, renderJavascript
, renderJavascriptUrl
, javascriptSettings
, juliusUsedIdentifiers
, asJavascriptUrl
) where
import Language.Haskell.TH.Quote (QuasiQuoter (..))
import Language.Haskell.TH.Syntax
import Data.Text.Lazy.Builder (Builder, fromText, toLazyText, fromLazyText)
import Data.Monoid (Monoid(..))
import Data.Semigroup (Semigroup(..))
import qualified Data.Text as TS
import qualified Data.Text.Lazy as TL
import Text.Shakespeare
import Data.Aeson (Value, toJSON)
#if MIN_VERSION_aeson(2,0,0)
import qualified Data.Aeson.KeyMap as KeyMap
#endif
import Data.Aeson.Types (Value(..))
import Numeric (showHex)
import qualified Data.HashMap.Strict as H
import qualified Data.Vector as V
import Data.Text.Lazy.Builder (singleton, fromString)
import qualified Data.Text as T
import Data.Scientific (FPFormat(..), Scientific, base10Exponent)
import Data.Text.Lazy.Builder.Scientific (formatScientificBuilder)
renderJavascript :: Javascript -> TL.Text
renderJavascript :: Javascript -> Text
renderJavascript (Javascript Builder
b) = Builder -> Text
toLazyText Builder
b
renderJavascriptUrl :: (url -> [(TS.Text, TS.Text)] -> TS.Text) -> JavascriptUrl url -> TL.Text
renderJavascriptUrl :: forall url.
(url -> [(Text, Text)] -> Text) -> JavascriptUrl url -> Text
renderJavascriptUrl url -> [(Text, Text)] -> Text
r JavascriptUrl url
s = Javascript -> Text
renderJavascript forall a b. (a -> b) -> a -> b
$ JavascriptUrl url
s url -> [(Text, Text)] -> Text
r
newtype Javascript = Javascript { Javascript -> Builder
unJavascript :: Builder }
deriving (NonEmpty Javascript -> Javascript
Javascript -> Javascript -> Javascript
forall b. Integral b => b -> Javascript -> Javascript
forall a.
(a -> a -> a)
-> (NonEmpty a -> a)
-> (forall b. Integral b => b -> a -> a)
-> Semigroup a
stimes :: forall b. Integral b => b -> Javascript -> Javascript
$cstimes :: forall b. Integral b => b -> Javascript -> Javascript
sconcat :: NonEmpty Javascript -> Javascript
$csconcat :: NonEmpty Javascript -> Javascript
<> :: Javascript -> Javascript -> Javascript
$c<> :: Javascript -> Javascript -> Javascript
Semigroup, Semigroup Javascript
Javascript
[Javascript] -> Javascript
Javascript -> Javascript -> Javascript
forall a.
Semigroup a -> a -> (a -> a -> a) -> ([a] -> a) -> Monoid a
mconcat :: [Javascript] -> Javascript
$cmconcat :: [Javascript] -> Javascript
mappend :: Javascript -> Javascript -> Javascript
$cmappend :: Javascript -> Javascript -> Javascript
mempty :: Javascript
$cmempty :: Javascript
Monoid)
type JavascriptUrl url = (url -> [(TS.Text, TS.Text)] -> TS.Text) -> Javascript
asJavascriptUrl :: JavascriptUrl url -> JavascriptUrl url
asJavascriptUrl :: forall url. JavascriptUrl url -> JavascriptUrl url
asJavascriptUrl = forall a. a -> a
id
class ToJavascript a where
toJavascript :: a -> Javascript
instance ToJavascript Bool where toJavascript :: Bool -> Javascript
toJavascript = Builder -> Javascript
Javascript forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Builder
fromText forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
TS.toLower forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
TS.pack forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Show a => a -> String
show
instance ToJavascript Value where toJavascript :: Value -> Javascript
toJavascript = Builder -> Javascript
Javascript forall b c a. (b -> c) -> (a -> b) -> a -> c
. Value -> Builder
encodeToTextBuilder
instance ToJavascript String where toJavascript :: String -> Javascript
toJavascript = forall a. ToJavascript a => a -> Javascript
toJavascript forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. ToJSON a => a -> Value
toJSON
instance ToJavascript TS.Text where toJavascript :: Text -> Javascript
toJavascript = forall a. ToJavascript a => a -> Javascript
toJavascript forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. ToJSON a => a -> Value
toJSON
instance ToJavascript TL.Text where toJavascript :: Text -> Javascript
toJavascript = forall a. ToJavascript a => a -> Javascript
toJavascript forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. ToJSON a => a -> Value
toJSON
encodeToTextBuilder :: Value -> Builder
encodeToTextBuilder :: Value -> Builder
encodeToTextBuilder =
Value -> Builder
go
where
go :: Value -> Builder
go Value
Null = {-# SCC "go/Null" #-} Builder
"null"
go (Bool Bool
b) = {-# SCC "go/Bool" #-} if Bool
b then Builder
"true" else Builder
"false"
go (Number Scientific
s) = {-# SCC "go/Number" #-} Scientific -> Builder
fromScientific Scientific
s
go (String Text
s) = {-# SCC "go/String" #-} Text -> Builder
string Text
s
go (Array Array
v)
| forall a. Vector a -> Bool
V.null Array
v = {-# SCC "go/Array" #-} Builder
"[]"
| Bool
otherwise = {-# SCC "go/Array" #-}
Char -> Builder
singleton Char
'[' forall a. Semigroup a => a -> a -> a
<>
Value -> Builder
go (forall a. Vector a -> a
V.unsafeHead Array
v) forall a. Semigroup a => a -> a -> a
<>
forall a b. (a -> b -> b) -> b -> Vector a -> b
V.foldr Value -> Builder -> Builder
f (Char -> Builder
singleton Char
']') (forall a. Vector a -> Vector a
V.unsafeTail Array
v)
where f :: Value -> Builder -> Builder
f Value
a Builder
z = Char -> Builder
singleton Char
',' forall a. Semigroup a => a -> a -> a
<> Value -> Builder
go Value
a forall a. Semigroup a => a -> a -> a
<> Builder
z
go (Object Object
m) = {-# SCC "go/Object" #-}
case forall {v}. KeyMap v -> [(Text, v)]
fromObject Object
m of
((Text, Value)
x:[(Text, Value)]
xs) -> Char -> Builder
singleton Char
'{' forall a. Semigroup a => a -> a -> a
<> (Text, Value) -> Builder
one (Text, Value)
x forall a. Semigroup a => a -> a -> a
<> forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (Text, Value) -> Builder -> Builder
f (Char -> Builder
singleton Char
'}') [(Text, Value)]
xs
[(Text, Value)]
_ -> Builder
"{}"
where f :: (Text, Value) -> Builder -> Builder
f (Text, Value)
a Builder
z = Char -> Builder
singleton Char
',' forall a. Semigroup a => a -> a -> a
<> (Text, Value) -> Builder
one (Text, Value)
a forall a. Semigroup a => a -> a -> a
<> Builder
z
one :: (Text, Value) -> Builder
one (Text
k,Value
v) = Text -> Builder
string Text
k forall a. Semigroup a => a -> a -> a
<> Char -> Builder
singleton Char
':' forall a. Semigroup a => a -> a -> a
<> Value -> Builder
go Value
v
#if MIN_VERSION_aeson(2,0,0)
fromObject :: KeyMap v -> [(Text, v)]
fromObject = forall k v. HashMap k v -> [(k, v)]
H.toList forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall v. KeyMap v -> HashMap Text v
KeyMap.toHashMapText
#else
fromObject = H.toList
#endif
string :: T.Text -> Builder
string :: Text -> Builder
string Text
s = {-# SCC "string" #-} Char -> Builder
singleton Char
'"' forall a. Semigroup a => a -> a -> a
<> Text -> Builder
quote Text
s forall a. Semigroup a => a -> a -> a
<> Char -> Builder
singleton Char
'"'
where
quote :: Text -> Builder
quote Text
q = case Text -> Maybe (Char, Text)
T.uncons Text
t of
Maybe (Char, Text)
Nothing -> Text -> Builder
fromText Text
h
Just (!Char
c,Text
t') -> Text -> Builder
fromText Text
h forall a. Semigroup a => a -> a -> a
<> Char -> Builder
escape Char
c forall a. Semigroup a => a -> a -> a
<> Text -> Builder
quote Text
t'
where (Text
h,Text
t) = {-# SCC "break" #-} (Char -> Bool) -> Text -> (Text, Text)
T.break Char -> Bool
isEscape Text
q
isEscape :: Char -> Bool
isEscape Char
c = Char
c forall a. Eq a => a -> a -> Bool
== Char
'\"' Bool -> Bool -> Bool
||
Char
c forall a. Eq a => a -> a -> Bool
== Char
'\'' Bool -> Bool -> Bool
||
Char
c forall a. Eq a => a -> a -> Bool
== Char
'\\' Bool -> Bool -> Bool
||
Char
c forall a. Eq a => a -> a -> Bool
== Char
'<' Bool -> Bool -> Bool
||
Char
c forall a. Eq a => a -> a -> Bool
== Char
'>' Bool -> Bool -> Bool
||
Char
c forall a. Eq a => a -> a -> Bool
== Char
'&' Bool -> Bool -> Bool
||
Char
c forall a. Ord a => a -> a -> Bool
< Char
'\x20'
escape :: Char -> Builder
escape Char
'\"' = Builder
"\\\""
escape Char
'\'' = Builder
"\\\'"
escape Char
'\\' = Builder
"\\\\"
escape Char
'\n' = Builder
"\\n"
escape Char
'\r' = Builder
"\\r"
escape Char
'\t' = Builder
"\\t"
escape Char
'<' = Builder
"\\u003c"
escape Char
'>' = Builder
"\\u003e"
escape Char
'&' = Builder
"\\u0026"
escape Char
c
| Char
c forall a. Ord a => a -> a -> Bool
< Char
'\x20' = String -> Builder
fromString forall a b. (a -> b) -> a -> b
$ String
"\\u" forall a. [a] -> [a] -> [a]
++ forall a. Int -> a -> [a]
replicate (Int
4 forall a. Num a => a -> a -> a
- forall (t :: * -> *) a. Foldable t => t a -> Int
length String
h) Char
'0' forall a. [a] -> [a] -> [a]
++ String
h
| Bool
otherwise = Char -> Builder
singleton Char
c
where h :: String
h = forall a. (Integral a, Show a) => a -> ShowS
showHex (forall a. Enum a => a -> Int
fromEnum Char
c) String
""
fromScientific :: Scientific -> Builder
fromScientific :: Scientific -> Builder
fromScientific Scientific
s = FPFormat -> Maybe Int -> Scientific -> Builder
formatScientificBuilder FPFormat
format Maybe Int
prec Scientific
s
where
(FPFormat
format, Maybe Int
prec)
| Scientific -> Int
base10Exponent Scientific
s forall a. Ord a => a -> a -> Bool
< Int
0 = (FPFormat
Generic, forall a. Maybe a
Nothing)
| Bool
otherwise = (FPFormat
Fixed, forall a. a -> Maybe a
Just Int
0)
newtype RawJavascript = RawJavascript Builder
instance ToJavascript RawJavascript where
toJavascript :: RawJavascript -> Javascript
toJavascript (RawJavascript Builder
a) = Builder -> Javascript
Javascript Builder
a
class RawJS a where
rawJS :: a -> RawJavascript
instance RawJS [Char] where rawJS :: String -> RawJavascript
rawJS = Builder -> RawJavascript
RawJavascript forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Builder
fromLazyText forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
TL.pack
instance RawJS TS.Text where rawJS :: Text -> RawJavascript
rawJS = Builder -> RawJavascript
RawJavascript forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Builder
fromText
instance RawJS TL.Text where rawJS :: Text -> RawJavascript
rawJS = Builder -> RawJavascript
RawJavascript forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Builder
fromLazyText
instance RawJS Builder where rawJS :: Builder -> RawJavascript
rawJS = Builder -> RawJavascript
RawJavascript
instance RawJS Bool where rawJS :: Bool -> RawJavascript
rawJS = Builder -> RawJavascript
RawJavascript forall b c a. (b -> c) -> (a -> b) -> a -> c
. Javascript -> Builder
unJavascript forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. ToJavascript a => a -> Javascript
toJavascript
javascriptSettings :: Q ShakespeareSettings
javascriptSettings :: Q ShakespeareSettings
javascriptSettings = do
Exp
toJExp <- [|toJavascript|]
Exp
wrapExp <- [|Javascript|]
Exp
unWrapExp <- [|unJavascript|]
Exp
asJavascriptUrl' <- [|asJavascriptUrl|]
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ ShakespeareSettings
defaultShakespeareSettings { toBuilder :: Exp
toBuilder = Exp
toJExp
, wrap :: Exp
wrap = Exp
wrapExp
, unwrap :: Exp
unwrap = Exp
unWrapExp
, modifyFinalValue :: Maybe Exp
modifyFinalValue = forall a. a -> Maybe a
Just Exp
asJavascriptUrl'
}
js, julius :: QuasiQuoter
js :: QuasiQuoter
js = QuasiQuoter { quoteExp :: String -> Q Exp
quoteExp = \String
s -> do
ShakespeareSettings
rs <- Q ShakespeareSettings
javascriptSettings
QuasiQuoter -> String -> Q Exp
quoteExp (ShakespeareSettings -> QuasiQuoter
shakespeare ShakespeareSettings
rs) String
s
}
julius :: QuasiQuoter
julius = QuasiQuoter
js
jsFile, juliusFile :: FilePath -> Q Exp
jsFile :: String -> Q Exp
jsFile String
fp = do
ShakespeareSettings
rs <- Q ShakespeareSettings
javascriptSettings
ShakespeareSettings -> String -> Q Exp
shakespeareFile ShakespeareSettings
rs String
fp
juliusFile :: String -> Q Exp
juliusFile = String -> Q Exp
jsFile
jsFileReload, juliusFileReload :: FilePath -> Q Exp
jsFileReload :: String -> Q Exp
jsFileReload String
fp = do
ShakespeareSettings
rs <- Q ShakespeareSettings
javascriptSettings
ShakespeareSettings -> String -> Q Exp
shakespeareFileReload ShakespeareSettings
rs String
fp
juliusFileReload :: String -> Q Exp
juliusFileReload = String -> Q Exp
jsFileReload
jsFileDebug, juliusFileDebug :: FilePath -> Q Exp
juliusFileDebug :: String -> Q Exp
juliusFileDebug = String -> Q Exp
jsFileReload
{-# DEPRECATED juliusFileDebug "Please use juliusFileReload instead." #-}
jsFileDebug :: String -> Q Exp
jsFileDebug = String -> Q Exp
jsFileReload
{-# DEPRECATED jsFileDebug "Please use jsFileReload instead." #-}
juliusUsedIdentifiers :: String -> [(Deref, VarType)]
juliusUsedIdentifiers :: String -> [(Deref, VarType)]
juliusUsedIdentifiers = ShakespeareSettings -> String -> [(Deref, VarType)]
shakespeareUsedIdentifiers ShakespeareSettings
defaultShakespeareSettings