1{-# LANGUAGE DataKinds #-}
2{-# LANGUAGE TypeFamilies #-}
3{-# LANGUAGE TypeOperators #-}
4{-# LANGUAGE TemplateHaskell #-}
5{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE ScopedTypeVariables #-}
7module Servant.JQuery.Internal where
9import Control.Applicative
11import Data.Char (toLower)
12import qualified Data.CharSet as Set
13import qualified Data.CharSet.Unicode.Category as Set
17import qualified Data.Text as T
23data Segment = Static String -- ^ a static path segment. like "/foo"
24 | Cap Arg -- ^ a capture. like "/:userid"
28isCapture :: Segment -> Bool
29isCapture (Cap _) = True
32captureArg :: Segment -> Arg
34captureArg _ = error "captureArg called on non capture"
36jsSegments :: [Segment] -> String
38jsSegments [x] = "/" ++ segmentToStr x False
39jsSegments (x:xs) = "/" ++ segmentToStr x True ++ jsSegments xs
41segmentToStr :: Segment -> Bool -> String
42segmentToStr (Static s) notTheEnd =
43 if notTheEnd then s else s ++ "'"
44segmentToStr (Cap s) notTheEnd =
45 "' + encodeURIComponent(" ++ s ++ if notTheEnd then ") + '" else ")"
55data QueryArg = QueryArg
60data HeaderArg = HeaderArg
61 { headerArgName :: String
64 { headerArgName :: String
65 , headerPattern :: String
68instance Show HeaderArg where
69 show (HeaderArg n) = toValidFunctionName ("header" <> n)
70 show (ReplaceHeaderArg n p)
71 | pn `isPrefixOf` p = pv <> " + \"" <> rp <> "\""
72 | pn `isSuffixOf` p = "\"" <> rp <> "\" + " <> pv
73 | pn `isInfixOf` p = "\"" <> (replace pn ("\" + " <> pv <> " + \"") p)
77 pv = toValidFunctionName ("header" <> n)
80 -- Use replace method from Data.Text
81 replace old new = T.unpack .
82 T.replace (T.pack old) (T.pack new) .
85-- | Attempts to reduce the function name provided to that allowed by JS.
86-- https://mathiasbynens.be/notes/javascript-identifiers
87-- Couldn't work out how to handle zero-width characters.
88-- @TODO: specify better default function name, or throw error?
89toValidFunctionName :: String -> String
90toValidFunctionName (x:xs) = [setFirstChar x] <> filter remainder xs
92 setFirstChar c = if firstChar c
95 firstChar c = (prefixOK c) || (or . map (Set.member c) $ firstLetterOK)
96 remainder c = (prefixOK c) || (or . map (Set.member c) $ remainderOK)
98 prefixOK c = c `elem` ['$','_']
99 -- Unicode character sets
100 firstLetterOK = [ Set.lowercaseLetter
101 , Set.uppercaseLetter
102 , Set.titlecaseLetter
106 remainderOK = firstLetterOK <> [ Set.nonSpacingMark
107 , Set.spacingCombiningMark
109 , Set.connectorPunctuation ]
110toValidFunctionName [] = "_"
114 , _queryStr :: [QueryArg]
115 } deriving (Eq, Show)
120type FunctionName = String
123data AjaxReq = AjaxReq
125 , _reqMethod :: Method
126 , _reqHeaders :: [HeaderArg]
128 , _funcName :: FunctionName
129 } deriving (Eq, Show)
135jsParams :: [QueryArg] -> String
137jsParams [x] = paramToStr x False
138jsParams (x:xs) = paramToStr x True ++ "&" ++ jsParams xs
140paramToStr :: QueryArg -> Bool -> String
141paramToStr qarg notTheEnd =
142 case qarg ^. argType of
144 ++ "=' + encodeURIComponent("
146 ++ if notTheEnd then ") + '" else ")"
151 ++ "[]=' + encodeURIComponent("
153 ++ if notTheEnd then ") + '" else ")"
155 where name = qarg ^. argName
158defReq = AjaxReq defUrl "GET" [] False ""
160class HasJQ layout where
162 jqueryFor :: Proxy layout -> AjaxReq -> JQ layout
164instance (HasJQ a, HasJQ b)
165 => HasJQ (a :<|> b) where
166 type JQ (a :<|> b) = JQ a :<|> JQ b
168 jqueryFor Proxy req =
169 jqueryFor (Proxy :: Proxy a) req
170 :<|> jqueryFor (Proxy :: Proxy b) req
172instance (KnownSymbol sym, HasJQ sublayout)
173 => HasJQ (Capture sym a :> sublayout) where
174 type JQ (Capture sym a :> sublayout) = JQ sublayout
176 jqueryFor Proxy req =
177 jqueryFor (Proxy :: Proxy sublayout) $
178 req & reqUrl.path <>~ [Cap str]
180 where str = symbolVal (Proxy :: Proxy sym)
182instance HasJQ Delete where
183 type JQ Delete = AjaxReq
185 jqueryFor Proxy req =
186 req & funcName %~ ("delete" <>)
187 & reqMethod .~ "DELETE"
189instance HasJQ (Get a) where
190 type JQ (Get a) = AjaxReq
192 jqueryFor Proxy req =
193 req & funcName %~ ("get" <>)
196instance (KnownSymbol sym, HasJQ sublayout)
197 => HasJQ (Header sym a :> sublayout) where
198 type JQ (Header sym a :> sublayout) = JQ sublayout
200 jqueryFor Proxy req =
201 jqueryFor subP (req & reqHeaders <>~ [HeaderArg hname])
203 where hname = symbolVal (Proxy :: Proxy sym)
204 subP = Proxy :: Proxy sublayout
206instance HasJQ (Post a) where
207 type JQ (Post a) = AjaxReq
209 jqueryFor Proxy req =
210 req & funcName %~ ("post" <>)
211 & reqMethod .~ "POST"
213instance HasJQ (Put a) where
214 type JQ (Put a) = AjaxReq
216 jqueryFor Proxy req =
217 req & funcName %~ ("put" <>)
220instance (KnownSymbol sym, HasJQ sublayout)
221 => HasJQ (QueryParam sym a :> sublayout) where
222 type JQ (QueryParam sym a :> sublayout) = JQ sublayout
224 jqueryFor Proxy req =
225 jqueryFor (Proxy :: Proxy sublayout) $
226 req & reqUrl.queryStr <>~ [QueryArg str Normal]
228 where str = symbolVal (Proxy :: Proxy sym)
229 strArg = str ++ "Value"
231instance (KnownSymbol sym, HasJQ sublayout)
232 => HasJQ (QueryParams sym a :> sublayout) where
233 type JQ (QueryParams sym a :> sublayout) = JQ sublayout
235 jqueryFor Proxy req =
236 jqueryFor (Proxy :: Proxy sublayout) $
237 req & reqUrl.queryStr <>~ [QueryArg str List]
239 where str = symbolVal (Proxy :: Proxy sym)
241instance (KnownSymbol sym, HasJQ sublayout)
242 => HasJQ (QueryFlag sym :> sublayout) where
243 type JQ (QueryFlag sym :> sublayout) = JQ sublayout
245 jqueryFor Proxy req =
246 jqueryFor (Proxy :: Proxy sublayout) $
247 req & reqUrl.queryStr <>~ [QueryArg str Flag]
249 where str = symbolVal (Proxy :: Proxy sym)
251instance HasJQ Raw where
252 type JQ Raw = Method -> AjaxReq
254 jqueryFor Proxy req method =
255 req & funcName %~ ((toLower <$> method) <>)
256 & reqMethod .~ method
258instance HasJQ sublayout => HasJQ (ReqBody a :> sublayout) where
259 type JQ (ReqBody a :> sublayout) = JQ sublayout
261 jqueryFor Proxy req =
262 jqueryFor (Proxy :: Proxy sublayout) $
263 req & reqBody .~ True
265instance (KnownSymbol path, HasJQ sublayout)
266 => HasJQ (path :> sublayout) where
267 type JQ (path :> sublayout) = JQ sublayout
269 jqueryFor Proxy req =
270 jqueryFor (Proxy :: Proxy sublayout) $
271 req & reqUrl.path <>~ [Static str]
272 & funcName %~ (str <>)
274 where str = map (\c -> if c == '.' then '_' else c) $ symbolVal (Proxy :: Proxy path)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list