Merge branch 'master' into fix_js_function_identifiers
This commit is contained in:
commit
128579bdb4
72 changed files with 564 additions and 2851 deletions
28
.travis.yml
28
.travis.yml
|
@ -3,33 +3,27 @@ sudo: false
|
||||||
language: c
|
language: c
|
||||||
|
|
||||||
env:
|
env:
|
||||||
- GHCVER=7.8.4 CABALVER=1.22
|
- STACK_YAML=stack-ghc-7.8.4.yaml
|
||||||
- GHCVER=7.10.3 CABALVER=1.22
|
- STACK_YAML=stack.yaml
|
||||||
- GHCVER=8.0.1 CABALVER=1.24
|
- STACK_YAML=stack-ghc-8.0.1.yaml
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
sources:
|
|
||||||
- hvr-ghc
|
|
||||||
packages:
|
packages:
|
||||||
- ghc-7.8.4
|
|
||||||
- ghc-7.10.3
|
|
||||||
- ghc-8.0.1
|
|
||||||
- cabal-install-1.22
|
|
||||||
- cabal-install-1.24
|
|
||||||
- libgmp-dev
|
- libgmp-dev
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- (mkdir -p $HOME/.local/bin && cd $HOME/.local/bin && wget https://zalora-public.s3.amazonaws.com/tinc && chmod +x tinc)
|
- mkdir -p ~/.local/bin
|
||||||
- export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
|
- export PATH=$HOME/.local/bin:$PATH
|
||||||
- ghc --version
|
- travis_retry curl -L https://www.stackage.org/stack/linux-x86_64 | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack'
|
||||||
- cabal --version
|
- stack --version
|
||||||
- travis_retry cabal update
|
- stack setup --no-terminal
|
||||||
- sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
|
- (cd $HOME/.local/bin && wget https://zalora-public.s3.amazonaws.com/tinc && chmod +x tinc)
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- ./travis.sh
|
- if [ "$TRAVIS_EVENT_TYPE" = "cron" ] ; then ./scripts/ci-cron.sh ; else stack test --ghc-options=-Werror --no-terminal ; fi
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
- $HOME/.tinc/cache
|
- $HOME/.tinc/cache
|
||||||
|
- $HOME/.stack
|
||||||
|
|
|
@ -8,9 +8,10 @@ repository. You can use `cabal`:
|
||||||
./scripts/test-all.sh # Run all the tests
|
./scripts/test-all.sh # Run all the tests
|
||||||
```
|
```
|
||||||
|
|
||||||
`stack`:
|
Or `stack`:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
stack setup # Downloads and installs a proper GHC version if necessary
|
||||||
stack build # Install and build packages
|
stack build # Install and build packages
|
||||||
stack test # Run all the tests
|
stack test # Run all the tests
|
||||||
```
|
```
|
||||||
|
|
|
@ -17,7 +17,7 @@ recommonmark==0.4.0
|
||||||
singledispatch==3.4.0.3
|
singledispatch==3.4.0.3
|
||||||
six==1.10.0
|
six==1.10.0
|
||||||
snowballstemmer==1.2.1
|
snowballstemmer==1.2.1
|
||||||
Sphinx==1.3.4
|
Sphinx==1.3.6
|
||||||
sphinx-autobuild==0.5.2
|
sphinx-autobuild==0.5.2
|
||||||
sphinx-rtd-theme==0.1.9
|
sphinx-rtd-theme==0.1.9
|
||||||
tornado==4.3
|
tornado==4.3
|
||||||
|
|
|
@ -320,7 +320,7 @@ Which is used like so:
|
||||||
``` haskell
|
``` haskell
|
||||||
type ProtectedAPI12
|
type ProtectedAPI12
|
||||||
= UserAPI -- this is public
|
= UserAPI -- this is public
|
||||||
:<|> BasicAuth "my-real" User :> UserAPI2 -- this is protected by auth
|
:<|> BasicAuth "my-realm" User :> UserAPI2 -- this is protected by auth
|
||||||
```
|
```
|
||||||
|
|
||||||
### Interoperability with `wai`: `Raw`
|
### Interoperability with `wai`: `Raw`
|
||||||
|
|
|
@ -15,11 +15,10 @@ need to have some language extensions and imports:
|
||||||
|
|
||||||
module Client where
|
module Client where
|
||||||
|
|
||||||
import Control.Monad.Trans.Except (ExceptT, runExceptT)
|
|
||||||
import Data.Aeson
|
import Data.Aeson
|
||||||
import Data.Proxy
|
import Data.Proxy
|
||||||
import GHC.Generics
|
import GHC.Generics
|
||||||
import Network.HTTP.Client (Manager, newManager, defaultManagerSettings)
|
import Network.HTTP.Client (newManager, defaultManagerSettings)
|
||||||
import Servant.API
|
import Servant.API
|
||||||
import Servant.Client
|
import Servant.Client
|
||||||
```
|
```
|
||||||
|
@ -71,19 +70,13 @@ What we are going to get with **servant-client** here is 3 functions, one to que
|
||||||
``` haskell
|
``` haskell
|
||||||
position :: Int -- ^ value for "x"
|
position :: Int -- ^ value for "x"
|
||||||
-> Int -- ^ value for "y"
|
-> Int -- ^ value for "y"
|
||||||
-> Manager -- ^ the HTTP client to use
|
-> ClientM Position
|
||||||
-> BaseUrl -- ^ the URL at which the API can be found
|
|
||||||
-> ExceptT ServantError IO Position
|
|
||||||
|
|
||||||
hello :: Maybe String -- ^ an optional value for "name"
|
hello :: Maybe String -- ^ an optional value for "name"
|
||||||
-> Manager -- ^ the HTTP client to use
|
-> ClientM HelloMessage
|
||||||
-> BaseUrl -- ^ the URL at which the API can be found
|
|
||||||
-> ExceptT ServantError IO HelloMessage
|
|
||||||
|
|
||||||
marketing :: ClientInfo -- ^ value for the request body
|
marketing :: ClientInfo -- ^ value for the request body
|
||||||
-> Manager -- ^ the HTTP client to use
|
-> ClientM Email
|
||||||
-> BaseUrl -- ^ the URL at which the API can be found
|
|
||||||
-> ExceptT ServantError IO Email
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Each function makes available as an argument any value that the response may
|
Each function makes available as an argument any value that the response may
|
||||||
|
@ -120,17 +113,17 @@ data BaseUrl = BaseUrl
|
||||||
That's it. Let's now write some code that uses our client functions.
|
That's it. Let's now write some code that uses our client functions.
|
||||||
|
|
||||||
``` haskell
|
``` haskell
|
||||||
queries :: Manager -> BaseUrl -> ExceptT ServantError IO (Position, HelloMessage, Email)
|
queries :: ClientM (Position, HelloMessage, Email)
|
||||||
queries manager baseurl = do
|
queries = do
|
||||||
pos <- position 10 10 manager baseurl
|
pos <- position 10 10
|
||||||
message <- hello (Just "servant") manager baseurl
|
message <- hello (Just "servant")
|
||||||
em <- marketing (ClientInfo "Alp" "alp@foo.com" 26 ["haskell", "mathematics"]) manager baseurl
|
em <- marketing (ClientInfo "Alp" "alp@foo.com" 26 ["haskell", "mathematics"])
|
||||||
return (pos, message, em)
|
return (pos, message, em)
|
||||||
|
|
||||||
run :: IO ()
|
run :: IO ()
|
||||||
run = do
|
run = do
|
||||||
manager <- newManager defaultManagerSettings
|
manager <- newManager defaultManagerSettings
|
||||||
res <- runExceptT (queries manager (BaseUrl Http "localhost" 8081 ""))
|
res <- runClientM queries (ClientEnv manager (BaseUrl Http "localhost" 8081 ""))
|
||||||
case res of
|
case res of
|
||||||
Left err -> putStrLn $ "Error: " ++ show err
|
Left err -> putStrLn $ "Error: " ++ show err
|
||||||
Right (pos, message, em) -> do
|
Right (pos, message, em) -> do
|
||||||
|
|
|
@ -151,21 +151,120 @@ so we need a `Proxy` for our API type `API'` without its `Raw` endpoint.
|
||||||
|
|
||||||
Very similarly to how one can derive haskell functions, we can derive the
|
Very similarly to how one can derive haskell functions, we can derive the
|
||||||
javascript with just a simple function call to `jsForAPI` from
|
javascript with just a simple function call to `jsForAPI` from
|
||||||
`Servant.JQuery`.
|
`Servant.JS`.
|
||||||
|
|
||||||
``` haskell
|
``` haskell
|
||||||
apiJS :: Text
|
apiJS1 :: Text
|
||||||
apiJS = jsForAPI api vanillaJS
|
apiJS1 = jsForAPI api jquery
|
||||||
```
|
```
|
||||||
|
|
||||||
This `Text` contains 2 Javascript functions, 'getPoint' and 'getBooks':
|
This `Text` contains 2 Javascript functions, 'getPoint' and 'getBooks':
|
||||||
|
|
||||||
``` javascript
|
``` javascript
|
||||||
|
|
||||||
|
var getPoint = function(onSuccess, onError)
|
||||||
|
{
|
||||||
|
$.ajax(
|
||||||
|
{ url: '/point'
|
||||||
|
, success: onSuccess
|
||||||
|
, error: onError
|
||||||
|
, type: 'GET'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var getBooks = function(q, onSuccess, onError)
|
||||||
|
{
|
||||||
|
$.ajax(
|
||||||
|
{ url: '/books' + '?q=' + encodeURIComponent(q)
|
||||||
|
, success: onSuccess
|
||||||
|
, error: onError
|
||||||
|
, type: 'GET'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We created a directory `static` that contains two static files: `index.html`,
|
||||||
|
which is the entrypoint to our little web application; and `ui.js`, which
|
||||||
|
contains some hand-written javascript. This javascript code assumes the two
|
||||||
|
generated functions `getPoint` and `getBooks` in scope. Therefore we need to
|
||||||
|
write the generated javascript into a file:
|
||||||
|
|
||||||
|
``` haskell
|
||||||
|
writeJSFiles :: IO ()
|
||||||
|
writeJSFiles = do
|
||||||
|
T.writeFile "static/api.js" apiJS1
|
||||||
|
jq <- T.readFile =<< Language.Javascript.JQuery.file
|
||||||
|
T.writeFile "static/jq.js" jq
|
||||||
|
```
|
||||||
|
|
||||||
|
(We're also writing the jquery library into a file, as it's also used by
|
||||||
|
`ui.js`.) `static/api.js` will be included in `index.html` and the two
|
||||||
|
generated functions will therefore be available in `ui.js`.
|
||||||
|
|
||||||
|
And we're good to go. You can start the `main` function of this file and go to
|
||||||
|
`http://localhost:8000/`. Start typing in the name of one of the authors in our
|
||||||
|
database or part of a book title, and check out how long it takes to
|
||||||
|
approximate pi using the method mentioned above.
|
||||||
|
|
||||||
|
## Customizations
|
||||||
|
|
||||||
|
Instead of calling `jquery`, you can call its variant `jqueryWith`.
|
||||||
|
Here are the type definitions
|
||||||
|
|
||||||
|
```haskell ignore
|
||||||
|
jquery :: JavaScriptGenerator
|
||||||
|
jqueryWith :: CommonGeneratorOptions -> JavaScriptGenerator
|
||||||
|
```
|
||||||
|
|
||||||
|
The `CommonGeneratorOptions` will let you define different behaviors to
|
||||||
|
change how functions are generated. Here is the definition of currently
|
||||||
|
available options:
|
||||||
|
|
||||||
|
```haskell ignore
|
||||||
|
data CommonGeneratorOptions = CommonGeneratorOptions
|
||||||
|
{
|
||||||
|
-- | function generating function names
|
||||||
|
functionNameBuilder :: FunctionName -> Text
|
||||||
|
-- | name used when a user want to send the request body (to let you redefine it)
|
||||||
|
, requestBody :: Text
|
||||||
|
-- | name of the callback parameter when the request was successful
|
||||||
|
, successCallback :: Text
|
||||||
|
-- | name of the callback parameter when the request reported an error
|
||||||
|
, errorCallback :: Text
|
||||||
|
-- | namespace on which we define the js function (empty mean local var)
|
||||||
|
, moduleName :: Text
|
||||||
|
-- | a prefix that should be prepended to the URL in the generated JS
|
||||||
|
, urlPrefix :: Text
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This pattern is available with all supported backends, and default values are provided.
|
||||||
|
|
||||||
|
## Vanilla support
|
||||||
|
|
||||||
|
If you don't use JQuery for your application, you can reduce your
|
||||||
|
dependencies to simply use the `XMLHttpRequest` object from the standard API.
|
||||||
|
|
||||||
|
Use the same code as before but simply replace the previous `apiJS` with
|
||||||
|
the following one:
|
||||||
|
|
||||||
|
``` haskell
|
||||||
|
apiJS2 :: Text
|
||||||
|
apiJS2 = jsForAPI api vanillaJS
|
||||||
|
```
|
||||||
|
|
||||||
|
The rest is *completely* unchanged.
|
||||||
|
|
||||||
|
The output file is a bit different, but it has the same parameters,
|
||||||
|
|
||||||
|
``` javascript
|
||||||
|
|
||||||
|
|
||||||
var getPoint = function(onSuccess, onError)
|
var getPoint = function(onSuccess, onError)
|
||||||
{
|
{
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
xhr.open('GET', '/point', true);
|
xhr.open('GET', '/point', true);
|
||||||
xhr.setRequestHeader("Accept","application/json");
|
xhr.setRequestHeader(\"Accept\",\"application/json\");
|
||||||
xhr.onreadystatechange = function (e) {
|
xhr.onreadystatechange = function (e) {
|
||||||
if (xhr.readyState == 4) {
|
if (xhr.readyState == 4) {
|
||||||
if (xhr.status == 204 || xhr.status == 205) {
|
if (xhr.status == 204 || xhr.status == 205) {
|
||||||
|
@ -186,7 +285,7 @@ var getBooks = function(q, onSuccess, onError)
|
||||||
{
|
{
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
xhr.open('GET', '/books' + '?q=' + encodeURIComponent(q), true);
|
xhr.open('GET', '/books' + '?q=' + encodeURIComponent(q), true);
|
||||||
xhr.setRequestHeader("Accept","application/json");
|
xhr.setRequestHeader(\"Accept\",\"application/json\");
|
||||||
xhr.onreadystatechange = function (e) {
|
xhr.onreadystatechange = function (e) {
|
||||||
if (xhr.readyState == 4) {
|
if (xhr.readyState == 4) {
|
||||||
if (xhr.status == 204 || xhr.status == 205) {
|
if (xhr.status == 204 || xhr.status == 205) {
|
||||||
|
@ -202,27 +301,218 @@ var getBooks = function(q, onSuccess, onError)
|
||||||
}
|
}
|
||||||
xhr.send(null);
|
xhr.send(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
We created a directory `static` that contains two static files: `index.html`,
|
And that's all, your web service can of course be accessible from those
|
||||||
which is the entrypoint to our little web application; and `ui.js`, which
|
two clients at the same time!
|
||||||
contains some hand-written javascript. This javascript code assumes the two
|
|
||||||
generated functions `getPoint` and `getBooks` in scope. Therefore we need to
|
## Axios support
|
||||||
write the generated javascript into a file:
|
|
||||||
|
### Simple usage
|
||||||
|
|
||||||
|
If you use Axios library for your application, we support that too!
|
||||||
|
|
||||||
|
Use the same code as before but simply replace the previous `apiJS` with
|
||||||
|
the following one:
|
||||||
|
|
||||||
``` haskell
|
``` haskell
|
||||||
writeJSFiles :: IO ()
|
apiJS3 :: Text
|
||||||
writeJSFiles = do
|
apiJS3 = jsForAPI api $ axios defAxiosOptions
|
||||||
T.writeFile "static/api.js" apiJS
|
|
||||||
jq <- T.readFile =<< Language.Javascript.JQuery.file
|
|
||||||
T.writeFile "static/jq.js" jq
|
|
||||||
```
|
```
|
||||||
|
|
||||||
(We're also writing the jquery library into a file, as it's also used by
|
The rest is *completely* unchanged.
|
||||||
`ui.js`.) `static/api.js` will be included in `index.html` and the two
|
|
||||||
generated functions will therefore be available in `ui.js`.
|
The output file is a bit different,
|
||||||
|
|
||||||
|
``` javascript
|
||||||
|
|
||||||
|
|
||||||
|
var getPoint = function()
|
||||||
|
{
|
||||||
|
return axios({ url: '/point'
|
||||||
|
, method: 'get'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var getBooks = function(q)
|
||||||
|
{
|
||||||
|
return axios({ url: '/books' + '?q=' + encodeURIComponent(q)
|
||||||
|
, method: 'get'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
**Caution:** In order to support the promise style of the API, there are no onSuccess
|
||||||
|
nor onError callback functions.
|
||||||
|
|
||||||
|
### Defining Axios configuration
|
||||||
|
|
||||||
|
Axios lets you define a 'configuration' to determine the behavior of the
|
||||||
|
program when the AJAX request is sent.
|
||||||
|
|
||||||
|
We mapped this into a configuration
|
||||||
|
|
||||||
|
``` haskell
|
||||||
|
data AxiosOptions = AxiosOptions
|
||||||
|
{ -- | indicates whether or not cross-site Access-Control requests
|
||||||
|
-- should be made using credentials
|
||||||
|
withCredentials :: !Bool
|
||||||
|
-- | the name of the cookie to use as a value for xsrf token
|
||||||
|
, xsrfCookieName :: !(Maybe Text)
|
||||||
|
-- | the name of the header to use as a value for xsrf token
|
||||||
|
, xsrfHeaderName :: !(Maybe Text)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Angular support
|
||||||
|
|
||||||
|
### Simple usage
|
||||||
|
|
||||||
|
You can apply the same procedure as with `vanillaJS` and `jquery`, and
|
||||||
|
generate top level functions.
|
||||||
|
|
||||||
|
The difference is that `angular` Generator always takes an argument.
|
||||||
|
|
||||||
|
``` haskell
|
||||||
|
apiJS4 :: Text
|
||||||
|
apiJS4 = jsForAPI api $ angular defAngularOptions
|
||||||
|
```
|
||||||
|
|
||||||
|
The generated code will be a bit different than previous generators. An extra
|
||||||
|
argument `$http` will be added to let Angular magical Dependency Injector
|
||||||
|
operate.
|
||||||
|
|
||||||
|
**Caution:** In order to support the promise style of the API, there are no onSuccess
|
||||||
|
nor onError callback functions.
|
||||||
|
|
||||||
|
``` javascript
|
||||||
|
|
||||||
|
|
||||||
|
var getPoint = function($http)
|
||||||
|
{
|
||||||
|
return $http(
|
||||||
|
{ url: '/point'
|
||||||
|
, method: 'GET'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var getBooks = function($http, q)
|
||||||
|
{
|
||||||
|
return $http(
|
||||||
|
{ url: '/books' + '?q=' + encodeURIComponent(q)
|
||||||
|
, method: 'GET'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then build your controllers easily
|
||||||
|
|
||||||
|
``` javascript
|
||||||
|
|
||||||
|
app.controller("MyController", function($http) {
|
||||||
|
this.getPoint = getPoint($http)
|
||||||
|
.success(/* Do something */)
|
||||||
|
.error(/* Report error */);
|
||||||
|
|
||||||
|
this.getPoint = getBooks($http, q)
|
||||||
|
.success(/* Do something */)
|
||||||
|
.error(/* Report error */);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service generator
|
||||||
|
|
||||||
|
You can also generate automatically a service to wrap the whole API as
|
||||||
|
a single Angular service:
|
||||||
|
|
||||||
|
``` javascript
|
||||||
|
app.service('MyService', function($http) {
|
||||||
|
return ({
|
||||||
|
postCounter: function()
|
||||||
|
{
|
||||||
|
return $http(
|
||||||
|
{ url: '/counter'
|
||||||
|
, method: 'POST'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getCounter: function()
|
||||||
|
{
|
||||||
|
return $http(
|
||||||
|
{ url: '/books' + '?q=' + encodeURIComponent(q), true);
|
||||||
|
, method: 'GET'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
To do so, you just have to use an alternate generator.
|
||||||
|
|
||||||
|
``` haskell
|
||||||
|
apiJS5 :: Text
|
||||||
|
apiJS5 = jsForAPI api $ angularService defAngularOptions
|
||||||
|
```
|
||||||
|
|
||||||
|
Again, it is possible to customize some portions with the options.
|
||||||
|
|
||||||
|
``` haskell
|
||||||
|
data AngularOptions = AngularOptions
|
||||||
|
{ -- | When generating code with wrapInService, name of the service to generate, default is 'app'
|
||||||
|
serviceName :: Text
|
||||||
|
, -- | beginning of the service definition
|
||||||
|
prologue :: Text -> Text -> Text
|
||||||
|
, -- | end of the service definition
|
||||||
|
epilogue :: Text
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
# Custom function name builder
|
||||||
|
|
||||||
|
Servant comes with three name builders included:
|
||||||
|
|
||||||
|
- camelCase (the default)
|
||||||
|
- concatCase
|
||||||
|
- snakeCase
|
||||||
|
|
||||||
|
Keeping the JQuery as an example, let's see the impact:
|
||||||
|
|
||||||
|
``` haskell
|
||||||
|
apiJS6 :: Text
|
||||||
|
apiJS6 = jsForAPI api $ jqueryWith defCommonGeneratorOptions { functionNameBuilder= snakeCase }
|
||||||
|
```
|
||||||
|
|
||||||
|
This `Text` contains 2 Javascript functions:
|
||||||
|
|
||||||
|
``` javascript
|
||||||
|
|
||||||
|
|
||||||
|
var get_point = function(onSuccess, onError)
|
||||||
|
{
|
||||||
|
$.ajax(
|
||||||
|
{ url: '/point'
|
||||||
|
, success: onSuccess
|
||||||
|
, error: onError
|
||||||
|
, type: 'GET'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var get_books = function(q, onSuccess, onError)
|
||||||
|
{
|
||||||
|
$.ajax(
|
||||||
|
{ url: '/books' + '?q=' + encodeURIComponent(q)
|
||||||
|
, success: onSuccess
|
||||||
|
, error: onError
|
||||||
|
, type: 'GET'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
And we're good to go. You can start the `main` function of this file and go to
|
|
||||||
`http://localhost:8000/`. Start typing in the name of one of the authors in our
|
|
||||||
database or part of a book title, and check out how long it takes to
|
|
||||||
approximate pi using the method mentioned above.
|
|
||||||
|
|
|
@ -15,7 +15,10 @@ spec = do
|
||||||
describe "apiJS" $ do
|
describe "apiJS" $ do
|
||||||
it "is contained verbatim in Javascript.lhs" $ do
|
it "is contained verbatim in Javascript.lhs" $ do
|
||||||
code <- readFile "Javascript.lhs"
|
code <- readFile "Javascript.lhs"
|
||||||
cs apiJS `shouldSatisfy` (`isInfixOf` code)
|
cs apiJS1 `shouldSatisfy` (`isInfixOf` code)
|
||||||
|
cs apiJS3 `shouldSatisfy` (`isInfixOf` code)
|
||||||
|
cs apiJS4 `shouldSatisfy` (`isInfixOf` code)
|
||||||
|
cs apiJS6 `shouldSatisfy` (`isInfixOf` code)
|
||||||
|
|
||||||
describe "writeJSFiles" $ do
|
describe "writeJSFiles" $ do
|
||||||
it "[not a test] write apiJS to static/api.js" $ do
|
it "[not a test] write apiJS to static/api.js" $ do
|
||||||
|
@ -24,7 +27,7 @@ spec = do
|
||||||
describe "app" $ with (return app) $ do
|
describe "app" $ with (return app) $ do
|
||||||
context "/api.js" $ do
|
context "/api.js" $ do
|
||||||
it "delivers apiJS" $ do
|
it "delivers apiJS" $ do
|
||||||
get "/api.js" `shouldRespondWith` (fromString (cs apiJS))
|
get "/api.js" `shouldRespondWith` (fromString (cs apiJS1))
|
||||||
|
|
||||||
context "/" $ do
|
context "/" $ do
|
||||||
it "delivers something" $ do
|
it "delivers something" $ do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name: tutorial
|
name: tutorial
|
||||||
version: 0.8
|
version: 0.9
|
||||||
synopsis: The servant tutorial
|
synopsis: The servant tutorial
|
||||||
homepage: http://haskell-servant.readthedocs.org/
|
homepage: http://haskell-servant.readthedocs.org/
|
||||||
license: BSD3
|
license: BSD3
|
||||||
|
@ -25,11 +25,11 @@ library
|
||||||
, directory
|
, directory
|
||||||
, blaze-markup
|
, blaze-markup
|
||||||
, containers
|
, containers
|
||||||
, servant == 0.8.*
|
, servant == 0.9.*
|
||||||
, servant-server == 0.8.*
|
, servant-server == 0.9.*
|
||||||
, servant-client == 0.8.*
|
, servant-client == 0.9.*
|
||||||
, servant-docs == 0.8.*
|
, servant-docs == 0.9.*
|
||||||
, servant-js == 0.8.*
|
, servant-js == 0.9.*
|
||||||
, warp
|
, warp
|
||||||
, http-media
|
, http-media
|
||||||
, lucid
|
, lucid
|
||||||
|
|
|
@ -1,8 +1,15 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -o nounset
|
||||||
set -o errexit
|
set -o errexit
|
||||||
|
set -o verbose
|
||||||
|
|
||||||
for package in $(cat sources.txt) doc/tutorial ; do
|
export PATH=$(stack path --bin-path):$PATH
|
||||||
|
|
||||||
|
stack install cabal-install
|
||||||
|
cabal update
|
||||||
|
|
||||||
|
for package in $(cat sources.txt) ; do
|
||||||
echo testing $package
|
echo testing $package
|
||||||
pushd $package
|
pushd $package
|
||||||
tinc
|
tinc
|
|
@ -1,3 +1,10 @@
|
||||||
|
0.9
|
||||||
|
---
|
||||||
|
|
||||||
|
* BACKWARDS INCOMPATIBLE: `client` now returns a ClientM which is a Reader for
|
||||||
|
BasicEnv. BasicEnv comprises the HttpManager and BaseUrl that have had to be
|
||||||
|
passed to each method returned by `client`.
|
||||||
|
|
||||||
0.7.1
|
0.7.1
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name: servant-client
|
name: servant-client
|
||||||
version: 0.8
|
version: 0.9
|
||||||
synopsis: automatical derivation of querying functions for servant webservices
|
synopsis: automatical derivation of querying functions for servant webservices
|
||||||
description:
|
description:
|
||||||
This library lets you derive automatically Haskell functions that
|
This library lets you derive automatically Haskell functions that
|
||||||
|
@ -36,23 +36,24 @@ library
|
||||||
Servant.Common.Req
|
Servant.Common.Req
|
||||||
build-depends:
|
build-depends:
|
||||||
base >= 4.7 && < 4.10
|
base >= 4.7 && < 4.10
|
||||||
, aeson >= 0.7 && < 0.12
|
, aeson >= 0.7 && < 1.1
|
||||||
, attoparsec >= 0.12 && < 0.14
|
, attoparsec >= 0.12 && < 0.14
|
||||||
, base64-bytestring >= 1.0.0.1 && < 1.1
|
, base64-bytestring >= 1.0.0.1 && < 1.1
|
||||||
, bytestring >= 0.10 && < 0.11
|
, bytestring >= 0.10 && < 0.11
|
||||||
, exceptions >= 0.8 && < 0.9
|
, exceptions >= 0.8 && < 0.9
|
||||||
, http-api-data >= 0.1 && < 0.3
|
, http-api-data >= 0.3 && < 0.4
|
||||||
, http-client >= 0.4.18.1 && < 0.6
|
, http-client >= 0.4.18.1 && < 0.6
|
||||||
, http-client-tls >= 0.2.2 && < 0.4
|
, http-client-tls >= 0.2.2 && < 0.4
|
||||||
, http-media >= 0.6.2 && < 0.7
|
, http-media >= 0.6.2 && < 0.7
|
||||||
, http-types >= 0.8.6 && < 0.10
|
, http-types >= 0.8.6 && < 0.10
|
||||||
, network-uri >= 2.6 && < 2.7
|
, network-uri >= 2.6 && < 2.7
|
||||||
, safe >= 0.3.9 && < 0.4
|
, safe >= 0.3.9 && < 0.4
|
||||||
, servant == 0.8.*
|
, servant == 0.9.*
|
||||||
, string-conversions >= 0.3 && < 0.5
|
, string-conversions >= 0.3 && < 0.5
|
||||||
, text >= 1.2 && < 1.3
|
, text >= 1.2 && < 1.3
|
||||||
, transformers >= 0.3 && < 0.6
|
, transformers >= 0.3 && < 0.6
|
||||||
, transformers-compat >= 0.4 && < 0.6
|
, transformers-compat >= 0.4 && < 0.6
|
||||||
|
, mtl
|
||||||
hs-source-dirs: src
|
hs-source-dirs: src
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
ghc-options: -Wall
|
ghc-options: -Wall
|
||||||
|
@ -77,15 +78,16 @@ test-suite spec
|
||||||
, bytestring
|
, bytestring
|
||||||
, deepseq
|
, deepseq
|
||||||
, hspec == 2.*
|
, hspec == 2.*
|
||||||
|
, http-api-data
|
||||||
, http-client
|
, http-client
|
||||||
, http-media
|
, http-media
|
||||||
, http-types
|
, http-types
|
||||||
, HUnit
|
, HUnit
|
||||||
, network >= 2.6
|
, network >= 2.6
|
||||||
, QuickCheck >= 2.7
|
, QuickCheck >= 2.7
|
||||||
, servant == 0.8.*
|
, servant == 0.9.*
|
||||||
, servant-client
|
, servant-client
|
||||||
, servant-server == 0.8.*
|
, servant-server == 0.9.*
|
||||||
, text
|
, text
|
||||||
, wai
|
, wai
|
||||||
, warp
|
, warp
|
||||||
|
|
|
@ -20,6 +20,8 @@ module Servant.Client
|
||||||
, client
|
, client
|
||||||
, HasClient(..)
|
, HasClient(..)
|
||||||
, ClientM
|
, ClientM
|
||||||
|
, runClientM
|
||||||
|
, ClientEnv (ClientEnv)
|
||||||
, mkAuthenticateReq
|
, mkAuthenticateReq
|
||||||
, ServantError(..)
|
, ServantError(..)
|
||||||
, module Servant.Common.BaseUrl
|
, module Servant.Common.BaseUrl
|
||||||
|
@ -34,7 +36,7 @@ import Data.Proxy
|
||||||
import Data.String.Conversions
|
import Data.String.Conversions
|
||||||
import Data.Text (unpack)
|
import Data.Text (unpack)
|
||||||
import GHC.TypeLits
|
import GHC.TypeLits
|
||||||
import Network.HTTP.Client (Manager, Response)
|
import Network.HTTP.Client (Response)
|
||||||
import Network.HTTP.Media
|
import Network.HTTP.Media
|
||||||
import qualified Network.HTTP.Types as H
|
import qualified Network.HTTP.Types as H
|
||||||
import qualified Network.HTTP.Types.Header as HTTP
|
import qualified Network.HTTP.Types.Header as HTTP
|
||||||
|
@ -154,17 +156,17 @@ instance OVERLAPPABLE_
|
||||||
-- Note [Non-Empty Content Types]
|
-- Note [Non-Empty Content Types]
|
||||||
(MimeUnrender ct a, ReflectMethod method, cts' ~ (ct ': cts)
|
(MimeUnrender ct a, ReflectMethod method, cts' ~ (ct ': cts)
|
||||||
) => HasClient (Verb method status cts' a) where
|
) => HasClient (Verb method status cts' a) where
|
||||||
type Client (Verb method status cts' a) = Manager -> BaseUrl -> ClientM a
|
type Client (Verb method status cts' a) = ClientM a
|
||||||
clientWithRoute Proxy req manager baseurl =
|
clientWithRoute Proxy req = do
|
||||||
snd <$> performRequestCT (Proxy :: Proxy ct) method req manager baseurl
|
snd <$> performRequestCT (Proxy :: Proxy ct) method req
|
||||||
where method = reflectMethod (Proxy :: Proxy method)
|
where method = reflectMethod (Proxy :: Proxy method)
|
||||||
|
|
||||||
instance OVERLAPPING_
|
instance OVERLAPPING_
|
||||||
(ReflectMethod method) => HasClient (Verb method status cts NoContent) where
|
(ReflectMethod method) => HasClient (Verb method status cts NoContent) where
|
||||||
type Client (Verb method status cts NoContent)
|
type Client (Verb method status cts NoContent)
|
||||||
= Manager -> BaseUrl -> ClientM NoContent
|
= ClientM NoContent
|
||||||
clientWithRoute Proxy req manager baseurl =
|
clientWithRoute Proxy req = do
|
||||||
performRequestNoBody method req manager baseurl >> return NoContent
|
performRequestNoBody method req >> return NoContent
|
||||||
where method = reflectMethod (Proxy :: Proxy method)
|
where method = reflectMethod (Proxy :: Proxy method)
|
||||||
|
|
||||||
instance OVERLAPPING_
|
instance OVERLAPPING_
|
||||||
|
@ -172,10 +174,10 @@ instance OVERLAPPING_
|
||||||
( MimeUnrender ct a, BuildHeadersTo ls, ReflectMethod method, cts' ~ (ct ': cts)
|
( MimeUnrender ct a, BuildHeadersTo ls, ReflectMethod method, cts' ~ (ct ': cts)
|
||||||
) => HasClient (Verb method status cts' (Headers ls a)) where
|
) => HasClient (Verb method status cts' (Headers ls a)) where
|
||||||
type Client (Verb method status cts' (Headers ls a))
|
type Client (Verb method status cts' (Headers ls a))
|
||||||
= Manager -> BaseUrl -> ClientM (Headers ls a)
|
= ClientM (Headers ls a)
|
||||||
clientWithRoute Proxy req manager baseurl = do
|
clientWithRoute Proxy req = do
|
||||||
let method = reflectMethod (Proxy :: Proxy method)
|
let method = reflectMethod (Proxy :: Proxy method)
|
||||||
(hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) method req manager baseurl
|
(hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) method req
|
||||||
return $ Headers { getResponse = resp
|
return $ Headers { getResponse = resp
|
||||||
, getHeadersHList = buildHeadersTo hdrs
|
, getHeadersHList = buildHeadersTo hdrs
|
||||||
}
|
}
|
||||||
|
@ -184,10 +186,10 @@ instance OVERLAPPING_
|
||||||
( BuildHeadersTo ls, ReflectMethod method
|
( BuildHeadersTo ls, ReflectMethod method
|
||||||
) => HasClient (Verb method status cts (Headers ls NoContent)) where
|
) => HasClient (Verb method status cts (Headers ls NoContent)) where
|
||||||
type Client (Verb method status cts (Headers ls NoContent))
|
type Client (Verb method status cts (Headers ls NoContent))
|
||||||
= Manager -> BaseUrl -> ClientM (Headers ls NoContent)
|
= ClientM (Headers ls NoContent)
|
||||||
clientWithRoute Proxy req manager baseurl = do
|
clientWithRoute Proxy req = do
|
||||||
let method = reflectMethod (Proxy :: Proxy method)
|
let method = reflectMethod (Proxy :: Proxy method)
|
||||||
hdrs <- performRequestNoBody method req manager baseurl
|
hdrs <- performRequestNoBody method req
|
||||||
return $ Headers { getResponse = NoContent
|
return $ Headers { getResponse = NoContent
|
||||||
, getHeadersHList = buildHeadersTo hdrs
|
, getHeadersHList = buildHeadersTo hdrs
|
||||||
}
|
}
|
||||||
|
@ -372,7 +374,7 @@ instance (KnownSymbol sym, HasClient api)
|
||||||
-- back the full `Response`.
|
-- back the full `Response`.
|
||||||
instance HasClient Raw where
|
instance HasClient Raw where
|
||||||
type Client Raw
|
type Client Raw
|
||||||
= H.Method -> Manager -> BaseUrl -> ClientM (Int, ByteString, MediaType, [HTTP.Header], Response ByteString)
|
= H.Method -> ClientM (Int, ByteString, MediaType, [HTTP.Header], Response ByteString)
|
||||||
|
|
||||||
clientWithRoute :: Proxy Raw -> Req -> Client Raw
|
clientWithRoute :: Proxy Raw -> Req -> Client Raw
|
||||||
clientWithRoute Proxy req httpMethod = do
|
clientWithRoute Proxy req httpMethod = do
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
{-# LANGUAGE DeriveDataTypeable #-}
|
{-# LANGUAGE DeriveDataTypeable #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
{-# LANGUAGE CPP #-}
|
{-# LANGUAGE CPP #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE ScopedTypeVariables #-}
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||||
|
|
||||||
module Servant.Common.Req where
|
module Servant.Common.Req where
|
||||||
|
|
||||||
#if !MIN_VERSION_base(4,8,0)
|
#if !MIN_VERSION_base(4,8,0)
|
||||||
|
@ -10,8 +13,18 @@ import Control.Applicative
|
||||||
import Control.Exception
|
import Control.Exception
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
import Control.Monad.Catch (MonadThrow)
|
import Control.Monad.Catch (MonadThrow)
|
||||||
import Control.Monad.IO.Class
|
|
||||||
|
#if MIN_VERSION_mtl(2,2,0)
|
||||||
|
import Control.Monad.Except (MonadError(..))
|
||||||
|
#else
|
||||||
|
import Control.Monad.Error.Class (MonadError(..))
|
||||||
|
#endif
|
||||||
import Control.Monad.Trans.Except
|
import Control.Monad.Trans.Except
|
||||||
|
|
||||||
|
|
||||||
|
import GHC.Generics
|
||||||
|
import Control.Monad.IO.Class ()
|
||||||
|
import Control.Monad.Reader
|
||||||
import Data.ByteString.Lazy hiding (pack, filter, map, null, elem)
|
import Data.ByteString.Lazy hiding (pack, filter, map, null, elem)
|
||||||
import Data.String
|
import Data.String
|
||||||
import Data.String.Conversions
|
import Data.String.Conversions
|
||||||
|
@ -19,9 +32,9 @@ import Data.Proxy
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import Data.Text.Encoding
|
import Data.Text.Encoding
|
||||||
import Data.Typeable
|
import Data.Typeable
|
||||||
import Network.HTTP.Client hiding (Proxy, path)
|
|
||||||
import Network.HTTP.Media
|
import Network.HTTP.Media
|
||||||
import Network.HTTP.Types
|
import Network.HTTP.Types
|
||||||
|
import Network.HTTP.Client hiding (Proxy, path)
|
||||||
import qualified Network.HTTP.Types.Header as HTTP
|
import qualified Network.HTTP.Types.Header as HTTP
|
||||||
import Network.URI hiding (path)
|
import Network.URI hiding (path)
|
||||||
import Servant.API.ContentTypes
|
import Servant.API.ContentTypes
|
||||||
|
@ -103,7 +116,7 @@ setRQBody b t req = req { reqBody = Just (b, t) }
|
||||||
|
|
||||||
reqToRequest :: (Functor m, MonadThrow m) => Req -> BaseUrl -> m Request
|
reqToRequest :: (Functor m, MonadThrow m) => Req -> BaseUrl -> m Request
|
||||||
reqToRequest req (BaseUrl reqScheme reqHost reqPort path) =
|
reqToRequest req (BaseUrl reqScheme reqHost reqPort path) =
|
||||||
setheaders . setAccept . setrqb . setQS <$> parseUrlThrow url
|
setheaders . setAccept . setrqb . setQS <$> parseRequest url
|
||||||
|
|
||||||
where url = show $ nullURI { uriScheme = case reqScheme of
|
where url = show $ nullURI { uriScheme = case reqScheme of
|
||||||
Http -> "http:"
|
Http -> "http:"
|
||||||
|
@ -129,8 +142,18 @@ reqToRequest req (BaseUrl reqScheme reqHost reqPort path) =
|
||||||
| not . null . reqAccept $ req] }
|
| not . null . reqAccept $ req] }
|
||||||
toProperHeader (name, val) =
|
toProperHeader (name, val) =
|
||||||
(fromString name, encodeUtf8 val)
|
(fromString name, encodeUtf8 val)
|
||||||
|
|
||||||
#if !MIN_VERSION_http_client(0,4,30)
|
#if !MIN_VERSION_http_client(0,4,30)
|
||||||
parseUrlThrow = parseUrl
|
-- 'parseRequest' is introduced in http-client-0.4.30
|
||||||
|
-- it differs from 'parseUrl', by not throwing exceptions on non-2xx http statuses
|
||||||
|
--
|
||||||
|
-- See for implementations:
|
||||||
|
-- http://hackage.haskell.org/package/http-client-0.4.30/docs/src/Network-HTTP-Client-Request.html#parseRequest
|
||||||
|
-- http://hackage.haskell.org/package/http-client-0.5.0/docs/src/Network-HTTP-Client-Request.html#parseRequest
|
||||||
|
parseRequest :: MonadThrow m => String -> m Request
|
||||||
|
parseRequest url = liftM disableStatusCheck (parseUrl url)
|
||||||
|
where
|
||||||
|
disableStatusCheck req = req { checkStatus = \ _status _headers _cookies -> Nothing }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -139,21 +162,40 @@ reqToRequest req (BaseUrl reqScheme reqHost reqPort path) =
|
||||||
displayHttpRequest :: Method -> String
|
displayHttpRequest :: Method -> String
|
||||||
displayHttpRequest httpmethod = "HTTP " ++ cs httpmethod ++ " request"
|
displayHttpRequest httpmethod = "HTTP " ++ cs httpmethod ++ " request"
|
||||||
|
|
||||||
type ClientM = ExceptT ServantError IO
|
data ClientEnv
|
||||||
|
= ClientEnv
|
||||||
|
{ manager :: Manager
|
||||||
|
, baseUrl :: BaseUrl
|
||||||
|
}
|
||||||
|
|
||||||
performRequest :: Method -> Req -> Manager -> BaseUrl
|
|
||||||
|
-- | @ClientM@ is the monad in which client functions run. Contains the
|
||||||
|
-- 'Manager' and 'BaseUrl' used for requests in the reader environment.
|
||||||
|
|
||||||
|
newtype ClientM a = ClientM { runClientM' :: ReaderT ClientEnv (ExceptT ServantError IO) a }
|
||||||
|
deriving ( Functor, Applicative, Monad, MonadIO, Generic
|
||||||
|
, MonadReader ClientEnv
|
||||||
|
, MonadError ServantError
|
||||||
|
)
|
||||||
|
|
||||||
|
runClientM :: ClientM a -> ClientEnv -> IO (Either ServantError a)
|
||||||
|
runClientM cm env = runExceptT $ (flip runReaderT env) $ runClientM' cm
|
||||||
|
|
||||||
|
|
||||||
|
performRequest :: Method -> Req
|
||||||
-> ClientM ( Int, ByteString, MediaType
|
-> ClientM ( Int, ByteString, MediaType
|
||||||
, [HTTP.Header], Response ByteString)
|
, [HTTP.Header], Response ByteString)
|
||||||
performRequest reqMethod req manager reqHost = do
|
performRequest reqMethod req = do
|
||||||
|
m <- asks manager
|
||||||
|
reqHost <- asks baseUrl
|
||||||
partialRequest <- liftIO $ reqToRequest req reqHost
|
partialRequest <- liftIO $ reqToRequest req reqHost
|
||||||
|
|
||||||
let request = disableStatusCheck $
|
let request = partialRequest { Client.method = reqMethod }
|
||||||
partialRequest { Client.method = reqMethod }
|
|
||||||
|
|
||||||
eResponse <- liftIO $ catchConnectionError $ Client.httpLbs request manager
|
eResponse <- liftIO $ catchConnectionError $ Client.httpLbs request m
|
||||||
case eResponse of
|
case eResponse of
|
||||||
Left err ->
|
Left err ->
|
||||||
throwE . ConnectionError $ SomeException err
|
throwError . ConnectionError $ SomeException err
|
||||||
|
|
||||||
Right response -> do
|
Right response -> do
|
||||||
let status = Client.responseStatus response
|
let status = Client.responseStatus response
|
||||||
|
@ -163,35 +205,26 @@ performRequest reqMethod req manager reqHost = do
|
||||||
ct <- case lookup "Content-Type" $ Client.responseHeaders response of
|
ct <- case lookup "Content-Type" $ Client.responseHeaders response of
|
||||||
Nothing -> pure $ "application"//"octet-stream"
|
Nothing -> pure $ "application"//"octet-stream"
|
||||||
Just t -> case parseAccept t of
|
Just t -> case parseAccept t of
|
||||||
Nothing -> throwE $ InvalidContentTypeHeader (cs t) body
|
Nothing -> throwError $ InvalidContentTypeHeader (cs t) body
|
||||||
Just t' -> pure t'
|
Just t' -> pure t'
|
||||||
unless (status_code >= 200 && status_code < 300) $
|
unless (status_code >= 200 && status_code < 300) $
|
||||||
throwE $ FailureResponse status ct body
|
throwError $ FailureResponse status ct body
|
||||||
return (status_code, body, ct, hdrs, response)
|
return (status_code, body, ct, hdrs, response)
|
||||||
|
|
||||||
disableStatusCheck :: Request -> Request
|
performRequestCT :: MimeUnrender ct result => Proxy ct -> Method -> Req
|
||||||
#if MIN_VERSION_http_client(0,5,0)
|
|
||||||
disableStatusCheck req = req { checkResponse = \ _req _res -> return () }
|
|
||||||
#else
|
|
||||||
disableStatusCheck req = req { checkStatus = \ _status _headers _cookies -> Nothing }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
performRequestCT :: MimeUnrender ct result =>
|
|
||||||
Proxy ct -> Method -> Req -> Manager -> BaseUrl
|
|
||||||
-> ClientM ([HTTP.Header], result)
|
-> ClientM ([HTTP.Header], result)
|
||||||
performRequestCT ct reqMethod req manager reqHost = do
|
performRequestCT ct reqMethod req = do
|
||||||
let acceptCT = contentType ct
|
let acceptCT = contentType ct
|
||||||
(_status, respBody, respCT, hdrs, _response) <-
|
(_status, respBody, respCT, hdrs, _response) <-
|
||||||
performRequest reqMethod (req { reqAccept = [acceptCT] }) manager reqHost
|
performRequest reqMethod (req { reqAccept = [acceptCT] })
|
||||||
unless (matches respCT (acceptCT)) $ throwE $ UnsupportedContentType respCT respBody
|
unless (matches respCT (acceptCT)) $ throwError $ UnsupportedContentType respCT respBody
|
||||||
case mimeUnrender ct respBody of
|
case mimeUnrender ct respBody of
|
||||||
Left err -> throwE $ DecodeFailure err respCT respBody
|
Left err -> throwError $ DecodeFailure err respCT respBody
|
||||||
Right val -> return (hdrs, val)
|
Right val -> return (hdrs, val)
|
||||||
|
|
||||||
performRequestNoBody :: Method -> Req -> Manager -> BaseUrl
|
performRequestNoBody :: Method -> Req -> ClientM [HTTP.Header]
|
||||||
-> ClientM [HTTP.Header]
|
performRequestNoBody reqMethod req = do
|
||||||
performRequestNoBody reqMethod req manager reqHost = do
|
(_status, _body, _ct, hdrs, _response) <- performRequest reqMethod req
|
||||||
(_status, _body, _ct, hdrs, _response) <- performRequest reqMethod req manager reqHost
|
|
||||||
return hdrs
|
return hdrs
|
||||||
|
|
||||||
catchConnectionError :: IO a -> IO (Either ServantError a)
|
catchConnectionError :: IO a -> IO (Either ServantError a)
|
||||||
|
|
|
@ -32,14 +32,13 @@ import Control.Applicative ((<$>))
|
||||||
import Control.Arrow (left)
|
import Control.Arrow (left)
|
||||||
import Control.Concurrent (forkIO, killThread, ThreadId)
|
import Control.Concurrent (forkIO, killThread, ThreadId)
|
||||||
import Control.Exception (bracket)
|
import Control.Exception (bracket)
|
||||||
import Control.Monad.Trans.Except (throwE, runExceptT)
|
import Control.Monad.Trans.Except (throwE )
|
||||||
import Data.Aeson
|
import Data.Aeson
|
||||||
import qualified Data.ByteString.Lazy as BS
|
import qualified Data.ByteString.Lazy as BS
|
||||||
import Data.Char (chr, isPrint)
|
import Data.Char (chr, isPrint)
|
||||||
import Data.Foldable (forM_)
|
import Data.Foldable (forM_)
|
||||||
import Data.Monoid hiding (getLast)
|
import Data.Monoid hiding (getLast)
|
||||||
import Data.Proxy
|
import Data.Proxy
|
||||||
import qualified Data.Text as T
|
|
||||||
import GHC.Generics (Generic)
|
import GHC.Generics (Generic)
|
||||||
import qualified Network.HTTP.Client as C
|
import qualified Network.HTTP.Client as C
|
||||||
import Network.HTTP.Media
|
import Network.HTTP.Media
|
||||||
|
@ -52,6 +51,7 @@ import Test.Hspec
|
||||||
import Test.Hspec.QuickCheck
|
import Test.Hspec.QuickCheck
|
||||||
import Test.HUnit
|
import Test.HUnit
|
||||||
import Test.QuickCheck
|
import Test.QuickCheck
|
||||||
|
import Web.FormUrlEncoded (FromForm, ToForm)
|
||||||
|
|
||||||
import Servant.API
|
import Servant.API
|
||||||
import Servant.API.Internal.Test.ComprehensiveAPI
|
import Servant.API.Internal.Test.ComprehensiveAPI
|
||||||
|
@ -82,19 +82,8 @@ data Person = Person {
|
||||||
instance ToJSON Person
|
instance ToJSON Person
|
||||||
instance FromJSON Person
|
instance FromJSON Person
|
||||||
|
|
||||||
instance ToFormUrlEncoded Person where
|
instance ToForm Person where
|
||||||
toFormUrlEncoded Person{..} =
|
instance FromForm Person where
|
||||||
[("name", T.pack name), ("age", T.pack (show age))]
|
|
||||||
|
|
||||||
lookupEither :: (Show a, Eq a) => a -> [(a,b)] -> Either String b
|
|
||||||
lookupEither x xs = do
|
|
||||||
maybe (Left $ "could not find key " <> show x) return $ lookup x xs
|
|
||||||
|
|
||||||
instance FromFormUrlEncoded Person where
|
|
||||||
fromFormUrlEncoded xs = do
|
|
||||||
n <- lookupEither "name" xs
|
|
||||||
a <- lookupEither "age" xs
|
|
||||||
return $ Person (T.unpack n) (read $ T.unpack a)
|
|
||||||
|
|
||||||
alice :: Person
|
alice :: Person
|
||||||
alice = Person "Alice" 42
|
alice = Person "Alice" 42
|
||||||
|
@ -123,22 +112,22 @@ type Api =
|
||||||
api :: Proxy Api
|
api :: Proxy Api
|
||||||
api = Proxy
|
api = Proxy
|
||||||
|
|
||||||
getGet :: C.Manager -> BaseUrl -> SCR.ClientM Person
|
getGet :: SCR.ClientM Person
|
||||||
getDeleteEmpty :: C.Manager -> BaseUrl -> SCR.ClientM NoContent
|
getDeleteEmpty :: SCR.ClientM NoContent
|
||||||
getCapture :: String -> C.Manager -> BaseUrl -> SCR.ClientM Person
|
getCapture :: String -> SCR.ClientM Person
|
||||||
getCaptureAll :: [String] -> C.Manager -> BaseUrl -> SCR.ClientM [Person]
|
getCaptureAll :: [String] -> SCR.ClientM [Person]
|
||||||
getBody :: Person -> C.Manager -> BaseUrl -> SCR.ClientM Person
|
getBody :: Person -> SCR.ClientM Person
|
||||||
getQueryParam :: Maybe String -> C.Manager -> BaseUrl -> SCR.ClientM Person
|
getQueryParam :: Maybe String -> SCR.ClientM Person
|
||||||
getQueryParams :: [String] -> C.Manager -> BaseUrl -> SCR.ClientM [Person]
|
getQueryParams :: [String] -> SCR.ClientM [Person]
|
||||||
getQueryFlag :: Bool -> C.Manager -> BaseUrl -> SCR.ClientM Bool
|
getQueryFlag :: Bool -> SCR.ClientM Bool
|
||||||
getRawSuccess :: HTTP.Method -> C.Manager -> BaseUrl
|
getRawSuccess :: HTTP.Method
|
||||||
-> SCR.ClientM (Int, BS.ByteString, MediaType, [HTTP.Header], C.Response BS.ByteString)
|
-> SCR.ClientM (Int, BS.ByteString, MediaType, [HTTP.Header], C.Response BS.ByteString)
|
||||||
getRawFailure :: HTTP.Method -> C.Manager -> BaseUrl
|
getRawFailure :: HTTP.Method
|
||||||
-> SCR.ClientM (Int, BS.ByteString, MediaType, [HTTP.Header], C.Response BS.ByteString)
|
-> SCR.ClientM (Int, BS.ByteString, MediaType, [HTTP.Header], C.Response BS.ByteString)
|
||||||
getMultiple :: String -> Maybe Int -> Bool -> [(String, [Rational])] -> C.Manager -> BaseUrl
|
getMultiple :: String -> Maybe Int -> Bool -> [(String, [Rational])]
|
||||||
-> SCR.ClientM (String, Maybe Int, Bool, [(String, [Rational])])
|
-> SCR.ClientM (String, Maybe Int, Bool, [(String, [Rational])])
|
||||||
getRespHeaders :: C.Manager -> BaseUrl -> SCR.ClientM (Headers TestHeaders Bool)
|
getRespHeaders :: SCR.ClientM (Headers TestHeaders Bool)
|
||||||
getDeleteContentType :: C.Manager -> BaseUrl -> SCR.ClientM NoContent
|
getDeleteContentType :: SCR.ClientM NoContent
|
||||||
getGet
|
getGet
|
||||||
:<|> getDeleteEmpty
|
:<|> getDeleteEmpty
|
||||||
:<|> getCapture
|
:<|> getCapture
|
||||||
|
@ -242,42 +231,42 @@ sucessSpec :: Spec
|
||||||
sucessSpec = beforeAll (startWaiApp server) $ afterAll endWaiApp $ do
|
sucessSpec = beforeAll (startWaiApp server) $ afterAll endWaiApp $ do
|
||||||
|
|
||||||
it "Servant.API.Get" $ \(_, baseUrl) -> do
|
it "Servant.API.Get" $ \(_, baseUrl) -> do
|
||||||
(left show <$> runExceptT (getGet manager baseUrl)) `shouldReturn` Right alice
|
(left show <$> (runClientM getGet (ClientEnv manager baseUrl))) `shouldReturn` Right alice
|
||||||
|
|
||||||
describe "Servant.API.Delete" $ do
|
describe "Servant.API.Delete" $ do
|
||||||
it "allows empty content type" $ \(_, baseUrl) -> do
|
it "allows empty content type" $ \(_, baseUrl) -> do
|
||||||
(left show <$> runExceptT (getDeleteEmpty manager baseUrl)) `shouldReturn` Right NoContent
|
(left show <$> (runClientM getDeleteEmpty (ClientEnv manager baseUrl))) `shouldReturn` Right NoContent
|
||||||
|
|
||||||
it "allows content type" $ \(_, baseUrl) -> do
|
it "allows content type" $ \(_, baseUrl) -> do
|
||||||
(left show <$> runExceptT (getDeleteContentType manager baseUrl)) `shouldReturn` Right NoContent
|
(left show <$> (runClientM getDeleteContentType (ClientEnv manager baseUrl))) `shouldReturn` Right NoContent
|
||||||
|
|
||||||
it "Servant.API.Capture" $ \(_, baseUrl) -> do
|
it "Servant.API.Capture" $ \(_, baseUrl) -> do
|
||||||
(left show <$> runExceptT (getCapture "Paula" manager baseUrl)) `shouldReturn` Right (Person "Paula" 0)
|
(left show <$> (runClientM (getCapture "Paula") (ClientEnv manager baseUrl))) `shouldReturn` Right (Person "Paula" 0)
|
||||||
|
|
||||||
it "Servant.API.CaptureAll" $ \(_, baseUrl) -> do
|
it "Servant.API.CaptureAll" $ \(_, baseUrl) -> do
|
||||||
let expected = [(Person "Paula" 0), (Person "Peta" 1)]
|
let expected = [(Person "Paula" 0), (Person "Peta" 1)]
|
||||||
(left show <$> runExceptT (getCaptureAll ["Paula", "Peta"] manager baseUrl)) `shouldReturn` Right expected
|
(left show <$> (runClientM (getCaptureAll ["Paula", "Peta"]) (ClientEnv manager baseUrl))) `shouldReturn` Right expected
|
||||||
|
|
||||||
it "Servant.API.ReqBody" $ \(_, baseUrl) -> do
|
it "Servant.API.ReqBody" $ \(_, baseUrl) -> do
|
||||||
let p = Person "Clara" 42
|
let p = Person "Clara" 42
|
||||||
(left show <$> runExceptT (getBody p manager baseUrl)) `shouldReturn` Right p
|
(left show <$> runClientM (getBody p) (ClientEnv manager baseUrl)) `shouldReturn` Right p
|
||||||
|
|
||||||
it "Servant.API.QueryParam" $ \(_, baseUrl) -> do
|
it "Servant.API.QueryParam" $ \(_, baseUrl) -> do
|
||||||
left show <$> runExceptT (getQueryParam (Just "alice") manager baseUrl) `shouldReturn` Right alice
|
left show <$> runClientM (getQueryParam (Just "alice")) (ClientEnv manager baseUrl) `shouldReturn` Right alice
|
||||||
Left FailureResponse{..} <- runExceptT (getQueryParam (Just "bob") manager baseUrl)
|
Left FailureResponse{..} <- runClientM (getQueryParam (Just "bob")) (ClientEnv manager baseUrl)
|
||||||
responseStatus `shouldBe` HTTP.Status 400 "bob not found"
|
responseStatus `shouldBe` HTTP.Status 400 "bob not found"
|
||||||
|
|
||||||
it "Servant.API.QueryParam.QueryParams" $ \(_, baseUrl) -> do
|
it "Servant.API.QueryParam.QueryParams" $ \(_, baseUrl) -> do
|
||||||
(left show <$> runExceptT (getQueryParams [] manager baseUrl)) `shouldReturn` Right []
|
(left show <$> runClientM (getQueryParams []) (ClientEnv manager baseUrl)) `shouldReturn` Right []
|
||||||
(left show <$> runExceptT (getQueryParams ["alice", "bob"] manager baseUrl))
|
(left show <$> runClientM (getQueryParams ["alice", "bob"]) (ClientEnv manager baseUrl))
|
||||||
`shouldReturn` Right [Person "alice" 0, Person "bob" 1]
|
`shouldReturn` Right [Person "alice" 0, Person "bob" 1]
|
||||||
|
|
||||||
context "Servant.API.QueryParam.QueryFlag" $
|
context "Servant.API.QueryParam.QueryFlag" $
|
||||||
forM_ [False, True] $ \ flag -> it (show flag) $ \(_, baseUrl) -> do
|
forM_ [False, True] $ \ flag -> it (show flag) $ \(_, baseUrl) -> do
|
||||||
(left show <$> runExceptT (getQueryFlag flag manager baseUrl)) `shouldReturn` Right flag
|
(left show <$> runClientM (getQueryFlag flag) (ClientEnv manager baseUrl)) `shouldReturn` Right flag
|
||||||
|
|
||||||
it "Servant.API.Raw on success" $ \(_, baseUrl) -> do
|
it "Servant.API.Raw on success" $ \(_, baseUrl) -> do
|
||||||
res <- runExceptT (getRawSuccess HTTP.methodGet manager baseUrl)
|
res <- runClientM (getRawSuccess HTTP.methodGet) (ClientEnv manager baseUrl)
|
||||||
case res of
|
case res of
|
||||||
Left e -> assertFailure $ show e
|
Left e -> assertFailure $ show e
|
||||||
Right (code, body, ct, _, response) -> do
|
Right (code, body, ct, _, response) -> do
|
||||||
|
@ -286,7 +275,7 @@ sucessSpec = beforeAll (startWaiApp server) $ afterAll endWaiApp $ do
|
||||||
C.responseStatus response `shouldBe` HTTP.ok200
|
C.responseStatus response `shouldBe` HTTP.ok200
|
||||||
|
|
||||||
it "Servant.API.Raw should return a Left in case of failure" $ \(_, baseUrl) -> do
|
it "Servant.API.Raw should return a Left in case of failure" $ \(_, baseUrl) -> do
|
||||||
res <- runExceptT (getRawFailure HTTP.methodGet manager baseUrl)
|
res <- runClientM (getRawFailure HTTP.methodGet) (ClientEnv manager baseUrl)
|
||||||
case res of
|
case res of
|
||||||
Right _ -> assertFailure "expected Left, but got Right"
|
Right _ -> assertFailure "expected Left, but got Right"
|
||||||
Left e -> do
|
Left e -> do
|
||||||
|
@ -294,7 +283,7 @@ sucessSpec = beforeAll (startWaiApp server) $ afterAll endWaiApp $ do
|
||||||
Servant.Client.responseBody e `shouldBe` "rawFailure"
|
Servant.Client.responseBody e `shouldBe` "rawFailure"
|
||||||
|
|
||||||
it "Returns headers appropriately" $ \(_, baseUrl) -> do
|
it "Returns headers appropriately" $ \(_, baseUrl) -> do
|
||||||
res <- runExceptT (getRespHeaders manager baseUrl)
|
res <- runClientM getRespHeaders (ClientEnv manager baseUrl)
|
||||||
case res of
|
case res of
|
||||||
Left e -> assertFailure $ show e
|
Left e -> assertFailure $ show e
|
||||||
Right val -> getHeaders val `shouldBe` [("X-Example1", "1729"), ("X-Example2", "eg2")]
|
Right val -> getHeaders val `shouldBe` [("X-Example1", "1729"), ("X-Example2", "eg2")]
|
||||||
|
@ -303,7 +292,7 @@ sucessSpec = beforeAll (startWaiApp server) $ afterAll endWaiApp $ do
|
||||||
it "works for a combination of Capture, QueryParam, QueryFlag and ReqBody" $ \(_, baseUrl) ->
|
it "works for a combination of Capture, QueryParam, QueryFlag and ReqBody" $ \(_, baseUrl) ->
|
||||||
property $ forAllShrink pathGen shrink $ \(NonEmpty cap) num flag body ->
|
property $ forAllShrink pathGen shrink $ \(NonEmpty cap) num flag body ->
|
||||||
ioProperty $ do
|
ioProperty $ do
|
||||||
result <- left show <$> runExceptT (getMultiple cap num flag body manager baseUrl)
|
result <- left show <$> runClientM (getMultiple cap num flag body) (ClientEnv manager baseUrl)
|
||||||
return $
|
return $
|
||||||
result === Right (cap, num, flag, body)
|
result === Right (cap, num, flag, body)
|
||||||
|
|
||||||
|
@ -315,9 +304,9 @@ wrappedApiSpec = describe "error status codes" $ do
|
||||||
let test :: (WrappedApi, String) -> Spec
|
let test :: (WrappedApi, String) -> Spec
|
||||||
test (WrappedApi api, desc) =
|
test (WrappedApi api, desc) =
|
||||||
it desc $ bracket (startWaiApp $ serveW api) endWaiApp $ \(_, baseUrl) -> do
|
it desc $ bracket (startWaiApp $ serveW api) endWaiApp $ \(_, baseUrl) -> do
|
||||||
let getResponse :: C.Manager -> BaseUrl -> SCR.ClientM ()
|
let getResponse :: SCR.ClientM ()
|
||||||
getResponse = client api
|
getResponse = client api
|
||||||
Left FailureResponse{..} <- runExceptT (getResponse manager baseUrl)
|
Left FailureResponse{..} <- runClientM getResponse (ClientEnv manager baseUrl)
|
||||||
responseStatus `shouldBe` (HTTP.Status 500 "error message")
|
responseStatus `shouldBe` (HTTP.Status 500 "error message")
|
||||||
in mapM_ test $
|
in mapM_ test $
|
||||||
(WrappedApi (Proxy :: Proxy (Delete '[JSON] ())), "Delete") :
|
(WrappedApi (Proxy :: Proxy (Delete '[JSON] ())), "Delete") :
|
||||||
|
@ -332,42 +321,42 @@ failSpec = beforeAll (startWaiApp failServer) $ afterAll endWaiApp $ do
|
||||||
context "client returns errors appropriately" $ do
|
context "client returns errors appropriately" $ do
|
||||||
it "reports FailureResponse" $ \(_, baseUrl) -> do
|
it "reports FailureResponse" $ \(_, baseUrl) -> do
|
||||||
let (_ :<|> getDeleteEmpty :<|> _) = client api
|
let (_ :<|> getDeleteEmpty :<|> _) = client api
|
||||||
Left res <- runExceptT (getDeleteEmpty manager baseUrl)
|
Left res <- runClientM getDeleteEmpty (ClientEnv manager baseUrl)
|
||||||
case res of
|
case res of
|
||||||
FailureResponse (HTTP.Status 404 "Not Found") _ _ -> return ()
|
FailureResponse (HTTP.Status 404 "Not Found") _ _ -> return ()
|
||||||
_ -> fail $ "expected 404 response, but got " <> show res
|
_ -> fail $ "expected 404 response, but got " <> show res
|
||||||
|
|
||||||
it "reports DecodeFailure" $ \(_, baseUrl) -> do
|
it "reports DecodeFailure" $ \(_, baseUrl) -> do
|
||||||
let (_ :<|> _ :<|> getCapture :<|> _) = client api
|
let (_ :<|> _ :<|> getCapture :<|> _) = client api
|
||||||
Left res <- runExceptT (getCapture "foo" manager baseUrl)
|
Left res <- runClientM (getCapture "foo") (ClientEnv manager baseUrl)
|
||||||
case res of
|
case res of
|
||||||
DecodeFailure _ ("application/json") _ -> return ()
|
DecodeFailure _ ("application/json") _ -> return ()
|
||||||
_ -> fail $ "expected DecodeFailure, but got " <> show res
|
_ -> fail $ "expected DecodeFailure, but got " <> show res
|
||||||
|
|
||||||
it "reports ConnectionError" $ \_ -> do
|
it "reports ConnectionError" $ \_ -> do
|
||||||
let (getGetWrongHost :<|> _) = client api
|
let (getGetWrongHost :<|> _) = client api
|
||||||
Left res <- runExceptT (getGetWrongHost manager (BaseUrl Http "127.0.0.1" 19872 ""))
|
Left res <- runClientM getGetWrongHost (ClientEnv manager (BaseUrl Http "127.0.0.1" 19872 ""))
|
||||||
case res of
|
case res of
|
||||||
ConnectionError _ -> return ()
|
ConnectionError _ -> return ()
|
||||||
_ -> fail $ "expected ConnectionError, but got " <> show res
|
_ -> fail $ "expected ConnectionError, but got " <> show res
|
||||||
|
|
||||||
it "reports UnsupportedContentType" $ \(_, baseUrl) -> do
|
it "reports UnsupportedContentType" $ \(_, baseUrl) -> do
|
||||||
let (getGet :<|> _ ) = client api
|
let (getGet :<|> _ ) = client api
|
||||||
Left res <- runExceptT (getGet manager baseUrl)
|
Left res <- runClientM getGet (ClientEnv manager baseUrl)
|
||||||
case res of
|
case res of
|
||||||
UnsupportedContentType ("application/octet-stream") _ -> return ()
|
UnsupportedContentType ("application/octet-stream") _ -> return ()
|
||||||
_ -> fail $ "expected UnsupportedContentType, but got " <> show res
|
_ -> fail $ "expected UnsupportedContentType, but got " <> show res
|
||||||
|
|
||||||
it "reports InvalidContentTypeHeader" $ \(_, baseUrl) -> do
|
it "reports InvalidContentTypeHeader" $ \(_, baseUrl) -> do
|
||||||
let (_ :<|> _ :<|> _ :<|> _ :<|> getBody :<|> _) = client api
|
let (_ :<|> _ :<|> _ :<|> _ :<|> getBody :<|> _) = client api
|
||||||
Left res <- runExceptT (getBody alice manager baseUrl)
|
Left res <- runClientM (getBody alice) (ClientEnv manager baseUrl)
|
||||||
case res of
|
case res of
|
||||||
InvalidContentTypeHeader "fooooo" _ -> return ()
|
InvalidContentTypeHeader "fooooo" _ -> return ()
|
||||||
_ -> fail $ "expected InvalidContentTypeHeader, but got " <> show res
|
_ -> fail $ "expected InvalidContentTypeHeader, but got " <> show res
|
||||||
|
|
||||||
data WrappedApi where
|
data WrappedApi where
|
||||||
WrappedApi :: (HasServer (api :: *) '[], Server api ~ Handler a,
|
WrappedApi :: (HasServer (api :: *) '[], Server api ~ Handler a,
|
||||||
HasClient api, Client api ~ (C.Manager -> BaseUrl -> SCR.ClientM ())) =>
|
HasClient api, Client api ~ SCR.ClientM ()) =>
|
||||||
Proxy api -> WrappedApi
|
Proxy api -> WrappedApi
|
||||||
|
|
||||||
basicAuthSpec :: Spec
|
basicAuthSpec :: Spec
|
||||||
|
@ -377,14 +366,14 @@ basicAuthSpec = beforeAll (startWaiApp basicAuthServer) $ afterAll endWaiApp $ d
|
||||||
it "Authenticates a BasicAuth protected server appropriately" $ \(_,baseUrl) -> do
|
it "Authenticates a BasicAuth protected server appropriately" $ \(_,baseUrl) -> do
|
||||||
let getBasic = client basicAuthAPI
|
let getBasic = client basicAuthAPI
|
||||||
let basicAuthData = BasicAuthData "servant" "server"
|
let basicAuthData = BasicAuthData "servant" "server"
|
||||||
(left show <$> runExceptT (getBasic basicAuthData manager baseUrl)) `shouldReturn` Right alice
|
(left show <$> runClientM (getBasic basicAuthData) (ClientEnv manager baseUrl)) `shouldReturn` Right alice
|
||||||
|
|
||||||
context "Authentication is rejected when requests are not authenticated properly" $ do
|
context "Authentication is rejected when requests are not authenticated properly" $ do
|
||||||
|
|
||||||
it "Authenticates a BasicAuth protected server appropriately" $ \(_,baseUrl) -> do
|
it "Authenticates a BasicAuth protected server appropriately" $ \(_,baseUrl) -> do
|
||||||
let getBasic = client basicAuthAPI
|
let getBasic = client basicAuthAPI
|
||||||
let basicAuthData = BasicAuthData "not" "password"
|
let basicAuthData = BasicAuthData "not" "password"
|
||||||
Left FailureResponse{..} <- runExceptT (getBasic basicAuthData manager baseUrl)
|
Left FailureResponse{..} <- runClientM (getBasic basicAuthData) (ClientEnv manager baseUrl)
|
||||||
responseStatus `shouldBe` HTTP.Status 403 "Forbidden"
|
responseStatus `shouldBe` HTTP.Status 403 "Forbidden"
|
||||||
|
|
||||||
genAuthSpec :: Spec
|
genAuthSpec :: Spec
|
||||||
|
@ -394,14 +383,14 @@ genAuthSpec = beforeAll (startWaiApp genAuthServer) $ afterAll endWaiApp $ do
|
||||||
it "Authenticates a AuthProtect protected server appropriately" $ \(_, baseUrl) -> do
|
it "Authenticates a AuthProtect protected server appropriately" $ \(_, baseUrl) -> do
|
||||||
let getProtected = client genAuthAPI
|
let getProtected = client genAuthAPI
|
||||||
let authRequest = mkAuthenticateReq () (\_ req -> SCR.addHeader "AuthHeader" ("cool" :: String) req)
|
let authRequest = mkAuthenticateReq () (\_ req -> SCR.addHeader "AuthHeader" ("cool" :: String) req)
|
||||||
(left show <$> runExceptT (getProtected authRequest manager baseUrl)) `shouldReturn` Right alice
|
(left show <$> runClientM (getProtected authRequest) (ClientEnv manager baseUrl)) `shouldReturn` Right alice
|
||||||
|
|
||||||
context "Authentication is rejected when requests are not authenticated properly" $ do
|
context "Authentication is rejected when requests are not authenticated properly" $ do
|
||||||
|
|
||||||
it "Authenticates a AuthProtect protected server appropriately" $ \(_, baseUrl) -> do
|
it "Authenticates a AuthProtect protected server appropriately" $ \(_, baseUrl) -> do
|
||||||
let getProtected = client genAuthAPI
|
let getProtected = client genAuthAPI
|
||||||
let authRequest = mkAuthenticateReq () (\_ req -> SCR.addHeader "Wrong" ("header" :: String) req)
|
let authRequest = mkAuthenticateReq () (\_ req -> SCR.addHeader "Wrong" ("header" :: String) req)
|
||||||
Left FailureResponse{..} <- runExceptT (getProtected authRequest manager baseUrl)
|
Left FailureResponse{..} <- runClientM (getProtected authRequest) (ClientEnv manager baseUrl)
|
||||||
responseStatus `shouldBe` (HTTP.Status 401 "Unauthorized")
|
responseStatus `shouldBe` (HTTP.Status 401 "Unauthorized")
|
||||||
|
|
||||||
-- * utils
|
-- * utils
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name: servant-docs
|
name: servant-docs
|
||||||
version: 0.8
|
version: 0.9
|
||||||
synopsis: generate API docs for your servant webservice
|
synopsis: generate API docs for your servant webservice
|
||||||
description:
|
description:
|
||||||
Library for generating API docs from a servant API definition.
|
Library for generating API docs from a servant API definition.
|
||||||
|
@ -36,13 +36,12 @@ library
|
||||||
, aeson
|
, aeson
|
||||||
, aeson-pretty
|
, aeson-pretty
|
||||||
, bytestring
|
, bytestring
|
||||||
, bytestring-conversion
|
|
||||||
, case-insensitive
|
, case-insensitive
|
||||||
, hashable
|
, hashable
|
||||||
, http-media >= 0.6
|
, http-media >= 0.6
|
||||||
, http-types >= 0.7
|
, http-types >= 0.7
|
||||||
, lens
|
, lens
|
||||||
, servant == 0.8.*
|
, servant == 0.9.*
|
||||||
, string-conversions
|
, string-conversions
|
||||||
, text
|
, text
|
||||||
, unordered-containers
|
, unordered-containers
|
||||||
|
@ -61,7 +60,6 @@ executable greet-docs
|
||||||
build-depends:
|
build-depends:
|
||||||
base
|
base
|
||||||
, aeson
|
, aeson
|
||||||
, bytestring-conversion
|
|
||||||
, lens
|
, lens
|
||||||
, servant
|
, servant
|
||||||
, servant-docs
|
, servant-docs
|
||||||
|
|
|
@ -25,7 +25,6 @@ import Control.Arrow (second)
|
||||||
import Control.Lens (makeLenses, mapped, over, traversed, view, (%~),
|
import Control.Lens (makeLenses, mapped, over, traversed, view, (%~),
|
||||||
(&), (.~), (<>~), (^.), (|>))
|
(&), (.~), (<>~), (^.), (|>))
|
||||||
import qualified Control.Monad.Omega as Omega
|
import qualified Control.Monad.Omega as Omega
|
||||||
import Data.ByteString.Conversion (ToByteString, toByteString)
|
|
||||||
import Data.ByteString.Lazy.Char8 (ByteString)
|
import Data.ByteString.Lazy.Char8 (ByteString)
|
||||||
import qualified Data.ByteString.Char8 as BSC
|
import qualified Data.ByteString.Char8 as BSC
|
||||||
import qualified Data.CaseInsensitive as CI
|
import qualified Data.CaseInsensitive as CI
|
||||||
|
@ -461,12 +460,12 @@ class AllHeaderSamples ls where
|
||||||
instance AllHeaderSamples '[] where
|
instance AllHeaderSamples '[] where
|
||||||
allHeaderToSample _ = []
|
allHeaderToSample _ = []
|
||||||
|
|
||||||
instance (ToByteString l, AllHeaderSamples ls, ToSample l, KnownSymbol h)
|
instance (ToHttpApiData l, AllHeaderSamples ls, ToSample l, KnownSymbol h)
|
||||||
=> AllHeaderSamples (Header h l ': ls) where
|
=> AllHeaderSamples (Header h l ': ls) where
|
||||||
allHeaderToSample _ = mkHeader (toSample (Proxy :: Proxy l)) :
|
allHeaderToSample _ = mkHeader (toSample (Proxy :: Proxy l)) :
|
||||||
allHeaderToSample (Proxy :: Proxy ls)
|
allHeaderToSample (Proxy :: Proxy ls)
|
||||||
where headerName = CI.mk . cs $ symbolVal (Proxy :: Proxy h)
|
where headerName = CI.mk . cs $ symbolVal (Proxy :: Proxy h)
|
||||||
mkHeader (Just x) = (headerName, cs $ toByteString x)
|
mkHeader (Just x) = (headerName, cs $ toHeader x)
|
||||||
mkHeader Nothing = (headerName, "<no header sample provided>")
|
mkHeader Nothing = (headerName, "<no header sample provided>")
|
||||||
|
|
||||||
-- | Synthesise a sample value of a type, encoded in the specified media types.
|
-- | Synthesise a sample value of a type, encoded in the specified media types.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name: servant-foreign
|
name: servant-foreign
|
||||||
version: 0.8
|
version: 0.9
|
||||||
synopsis: Helpers for generating clients for servant APIs in any programming language
|
synopsis: Helpers for generating clients for servant APIs in any programming language
|
||||||
description:
|
description:
|
||||||
Helper types and functions for generating client functions for servant APIs in any programming language
|
Helper types and functions for generating client functions for servant APIs in any programming language
|
||||||
|
@ -32,7 +32,7 @@ library
|
||||||
, Servant.Foreign.Inflections
|
, Servant.Foreign.Inflections
|
||||||
build-depends: base == 4.*
|
build-depends: base == 4.*
|
||||||
, lens == 4.*
|
, lens == 4.*
|
||||||
, servant == 0.8.*
|
, servant == 0.9.*
|
||||||
, text >= 1.2 && < 1.3
|
, text >= 1.2 && < 1.3
|
||||||
, http-types
|
, http-types
|
||||||
hs-source-dirs: src
|
hs-source-dirs: src
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
0.5
|
|
||||||
----
|
|
||||||
|
|
||||||
* Extract javascript-obvlious types and helpers to *servant-foreign*
|
|
||||||
* Use `text` package instead of `String`
|
|
||||||
* Provide new targets for code generation along with the old jQuery one: vanilla Javascript and Angular.js
|
|
||||||
* Greatly simplify usage of this library by reducing down the API to just 2 functions: `jsForAPI` and `writeJSForAPI` + the choice of a code generator
|
|
||||||
* Support for the `HttpVersion`, `IsSecure`, `RemoteHost` and `Vault` combinators
|
|
||||||
* Remove matrix params.
|
|
||||||
|
|
||||||
0.4
|
|
||||||
---
|
|
||||||
* `Delete` now is like `Get`, `Post`, `Put`, and `Patch` and returns a response body
|
|
||||||
* Extend `HeaderArg` to support more advanced HTTP header handling (https://github.com/haskell-servant/servant-jquery/pull/6)
|
|
||||||
* Support content-type aware combinators (but require that endpoints support JSON)
|
|
||||||
* Add support for Matrix params (https://github.com/haskell-servant/servant-jquery/pull/11)
|
|
||||||
* Add functions that directly generate the Javascript code from the API type without having to manually pattern match on the result.
|
|
||||||
|
|
||||||
0.2.2
|
|
||||||
-----
|
|
||||||
|
|
||||||
* Fix an issue where toplevel Raw endpoints would generate a JS function with no name (https://github.com/haskell-servant/servant-jquery/issues/2)
|
|
||||||
* Replace dots by _ in paths (https://github.com/haskell-servant/servant-jquery/issues/1)
|
|
|
@ -1,30 +0,0 @@
|
||||||
Copyright (c) 2014-2016, Zalora South East Asia Pte Ltd, Servant Contributors
|
|
||||||
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following
|
|
||||||
disclaimer in the documentation and/or other materials provided
|
|
||||||
with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of Zalora South East Asia Pte Ltd nor the names of other
|
|
||||||
contributors may be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
@ -1,99 +0,0 @@
|
||||||
# servant-js
|
|
||||||
|
|
||||||
![servant](https://raw.githubusercontent.com/haskell-servant/servant/master/servant.png)
|
|
||||||
|
|
||||||
This library lets you derive automatically Javascript functions that let you query each endpoint of a *servant* webservice.
|
|
||||||
|
|
||||||
It contains a powerful system allowing you to generate functions for several frameworks (Angular, AXios, JQuery) as well as
|
|
||||||
vanilla (framework-free) javascript code.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
Read more about the following example [here](https://github.com/haskell-servant/servant/tree/master/servant-js/examples#examples).
|
|
||||||
|
|
||||||
``` haskell
|
|
||||||
{-# LANGUAGE DataKinds #-}
|
|
||||||
{-# LANGUAGE TypeOperators #-}
|
|
||||||
{-# LANGUAGE DeriveGeneric #-}
|
|
||||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
|
||||||
|
|
||||||
import Control.Concurrent.STM
|
|
||||||
import Control.Monad.IO.Class
|
|
||||||
import Data.Aeson
|
|
||||||
import Data.Proxy
|
|
||||||
import GHC.Generics
|
|
||||||
import Network.Wai.Handler.Warp (run)
|
|
||||||
import Servant
|
|
||||||
import Servant.JS
|
|
||||||
import System.FilePath
|
|
||||||
|
|
||||||
-- * A simple Counter data type
|
|
||||||
newtype Counter = Counter { value :: Int }
|
|
||||||
deriving (Generic, Show, Num)
|
|
||||||
|
|
||||||
instance ToJSON Counter
|
|
||||||
|
|
||||||
-- * Shared counter operations
|
|
||||||
|
|
||||||
-- Creating a counter that starts from 0
|
|
||||||
newCounter :: IO (TVar Counter)
|
|
||||||
newCounter = newTVarIO 0
|
|
||||||
|
|
||||||
-- Increasing the counter by 1
|
|
||||||
counterPlusOne :: MonadIO m => TVar Counter -> m Counter
|
|
||||||
counterPlusOne counter = liftIO . atomically $ do
|
|
||||||
oldValue <- readTVar counter
|
|
||||||
let newValue = oldValue + 1
|
|
||||||
writeTVar counter newValue
|
|
||||||
return newValue
|
|
||||||
|
|
||||||
currentValue :: MonadIO m => TVar Counter -> m Counter
|
|
||||||
currentValue counter = liftIO $ readTVarIO counter
|
|
||||||
|
|
||||||
-- * Our API type
|
|
||||||
type TestApi = "counter" :> Post '[JSON] Counter -- endpoint for increasing the counter
|
|
||||||
:<|> "counter" :> Get '[JSON] Counter -- endpoint to get the current value
|
|
||||||
|
|
||||||
type TestApi' = TestApi -- The API we want a JS handler for
|
|
||||||
:<|> Raw -- used for serving static files
|
|
||||||
|
|
||||||
-- this proxy only targets the proper endpoints of our API,
|
|
||||||
-- not the static file serving bit
|
|
||||||
testApi :: Proxy TestApi
|
|
||||||
testApi = Proxy
|
|
||||||
|
|
||||||
-- this proxy targets everything
|
|
||||||
testApi' :: Proxy TestApi'
|
|
||||||
testApi' = Proxy
|
|
||||||
|
|
||||||
-- * Server-side handler
|
|
||||||
|
|
||||||
-- where our static files reside
|
|
||||||
www :: FilePath
|
|
||||||
www = "examples/www"
|
|
||||||
|
|
||||||
-- defining handlers
|
|
||||||
server :: TVar Counter -> Server TestApi
|
|
||||||
server counter = counterPlusOne counter -- (+1) on the TVar
|
|
||||||
:<|> currentValue counter -- read the TVar
|
|
||||||
|
|
||||||
server' :: TVar Counter -> Server TestApi'
|
|
||||||
server counter = server counter
|
|
||||||
:<|> serveDirectory www -- serve static files
|
|
||||||
|
|
||||||
runServer :: TVar Counter -- ^ shared variable for the counter
|
|
||||||
-> Int -- ^ port the server should listen on
|
|
||||||
-> IO ()
|
|
||||||
runServer var port = run port (serve testApi' $ server' var)
|
|
||||||
|
|
||||||
main :: IO ()
|
|
||||||
main = do
|
|
||||||
-- write the JS code to www/api.js at startup
|
|
||||||
writeJSForAPI testApi jquery (www </> "api.js")
|
|
||||||
|
|
||||||
-- setup a shared counter
|
|
||||||
cnt <- newCounter
|
|
||||||
|
|
||||||
-- listen to requests on port 8080
|
|
||||||
runServer cnt 8080
|
|
||||||
```
|
|
|
@ -1,2 +0,0 @@
|
||||||
import Distribution.Simple
|
|
||||||
main = defaultMain
|
|
|
@ -1 +0,0 @@
|
||||||
- Investigate the best way to offer cross-origin requests
|
|
|
@ -1,52 +0,0 @@
|
||||||
SERVANT_DIR=/tmp/servant-js-gh-pages
|
|
||||||
|
|
||||||
# Make a temporary clone
|
|
||||||
|
|
||||||
rm -rf $SERVANT_DIR
|
|
||||||
|
|
||||||
git clone . $SERVANT_DIR
|
|
||||||
|
|
||||||
cd $SERVANT_DIR
|
|
||||||
|
|
||||||
# Make sure to pull the latest
|
|
||||||
|
|
||||||
git remote add haskell-servant git@github.com:haskell-servant/servant-js.git
|
|
||||||
|
|
||||||
git fetch haskell-servant
|
|
||||||
|
|
||||||
git reset --hard haskell-servant/gh-pages
|
|
||||||
|
|
||||||
# Clear everything away
|
|
||||||
|
|
||||||
git rm -rf $SERVANT_DIR/*
|
|
||||||
|
|
||||||
# Switch back and build the haddocks
|
|
||||||
|
|
||||||
cd -
|
|
||||||
|
|
||||||
cabal configure --builddir=$SERVANT_DIR
|
|
||||||
|
|
||||||
cabal haddock --hoogle --hyperlink-source --html-location='https://hackage.haskell.org/package/$pkg-$version/docs' --builddir=$SERVANT_DIR
|
|
||||||
|
|
||||||
commit_hash=$(git rev-parse HEAD)
|
|
||||||
|
|
||||||
# Move the HTML docs to the root
|
|
||||||
|
|
||||||
cd $SERVANT_DIR
|
|
||||||
|
|
||||||
rm *
|
|
||||||
rm -rf build
|
|
||||||
mv doc/html/servant-js/* .
|
|
||||||
rm -r doc/
|
|
||||||
|
|
||||||
# Add everything
|
|
||||||
|
|
||||||
git add .
|
|
||||||
|
|
||||||
git commit -m "Built from $commit_hash"
|
|
||||||
|
|
||||||
# Push to update the pages
|
|
||||||
|
|
||||||
git push haskell-servant HEAD:gh-pages
|
|
||||||
|
|
||||||
rm -rf $SERVANT_DIR
|
|
|
@ -1,17 +0,0 @@
|
||||||
# Examples
|
|
||||||
|
|
||||||
## counter
|
|
||||||
|
|
||||||
This example demonstrates a *servant* server that holds a shared variable (using a `TVar`) and exposes an endpoint for reading its current value and another one for increasing its current value by 1.
|
|
||||||
|
|
||||||
In addition to that, it shows how you can generate the jquery-powered javascript functions corresponding to each endpoint, i.e one for reading the current value and one for increasing the value, and integrates all of that in a very simple HTML page. All these static files are served using the `serveDirectory` function from *servant*.
|
|
||||||
|
|
||||||
To see this all in action, simply run:
|
|
||||||
|
|
||||||
``` bash
|
|
||||||
$ cabal run counter
|
|
||||||
```
|
|
||||||
|
|
||||||
And point your browser to [http://localhost:8080/index.html](http://localhost:8080/index.html).
|
|
||||||
|
|
||||||
Copies of the generated javascript functions and of the generated docs are included in `www/api.js` and `counter.md` respectively.
|
|
|
@ -1,103 +0,0 @@
|
||||||
{-# LANGUAGE DataKinds #-}
|
|
||||||
{-# LANGUAGE DeriveGeneric #-}
|
|
||||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
|
||||||
{-# LANGUAGE TypeOperators #-}
|
|
||||||
|
|
||||||
import Control.Concurrent.STM
|
|
||||||
import Control.Monad.IO.Class
|
|
||||||
import Data.Aeson
|
|
||||||
import Data.Proxy
|
|
||||||
import GHC.Generics
|
|
||||||
import Network.Wai.Handler.Warp (run)
|
|
||||||
import Servant
|
|
||||||
import Servant.JS
|
|
||||||
import qualified Servant.JS as SJS
|
|
||||||
import qualified Servant.JS.Angular as NG
|
|
||||||
import System.FilePath
|
|
||||||
|
|
||||||
-- * A simple Counter data type
|
|
||||||
newtype Counter = Counter { value :: Int }
|
|
||||||
deriving (Generic, Show, Num)
|
|
||||||
|
|
||||||
instance ToJSON Counter
|
|
||||||
|
|
||||||
-- * Shared counter operations
|
|
||||||
|
|
||||||
-- Creating a counter that starts from 0
|
|
||||||
newCounter :: IO (TVar Counter)
|
|
||||||
newCounter = newTVarIO 0
|
|
||||||
|
|
||||||
-- Increasing the counter by 1
|
|
||||||
counterPlusOne :: MonadIO m => TVar Counter -> m Counter
|
|
||||||
counterPlusOne counter = liftIO . atomically $ do
|
|
||||||
oldValue <- readTVar counter
|
|
||||||
let newValue = oldValue + 1
|
|
||||||
writeTVar counter newValue
|
|
||||||
return newValue
|
|
||||||
|
|
||||||
currentValue :: MonadIO m => TVar Counter -> m Counter
|
|
||||||
currentValue counter = liftIO $ readTVarIO counter
|
|
||||||
|
|
||||||
-- * Our API type
|
|
||||||
type TestApi = "counter" :> Post '[JSON] Counter -- endpoint for increasing the counter
|
|
||||||
:<|> "counter" :> Get '[JSON] Counter -- endpoint to get the current value
|
|
||||||
|
|
||||||
type TestApi' = TestApi
|
|
||||||
:<|> Raw -- used for serving static files
|
|
||||||
|
|
||||||
-- this proxy only targets the proper endpoints of our API,
|
|
||||||
-- not the static file serving bit
|
|
||||||
testApi :: Proxy TestApi
|
|
||||||
testApi = Proxy
|
|
||||||
|
|
||||||
-- this proxy targets everything
|
|
||||||
testApi' :: Proxy TestApi'
|
|
||||||
testApi' = Proxy
|
|
||||||
|
|
||||||
-- * Server-side handler
|
|
||||||
|
|
||||||
-- where our static files reside
|
|
||||||
www :: FilePath
|
|
||||||
www = "examples/www"
|
|
||||||
|
|
||||||
-- defining handlers of our endpoints
|
|
||||||
server :: TVar Counter -> Server TestApi
|
|
||||||
server counter = counterPlusOne counter -- (+1) on the TVar
|
|
||||||
:<|> currentValue counter -- read the TVar
|
|
||||||
|
|
||||||
-- the whole server, including static file serving
|
|
||||||
server' :: TVar Counter -> Server TestApi'
|
|
||||||
server' counter = server counter
|
|
||||||
:<|> serveDirectory www -- serve static files
|
|
||||||
|
|
||||||
runServer :: TVar Counter -- ^ shared variable for the counter
|
|
||||||
-> Int -- ^ port the server should listen on
|
|
||||||
-> IO ()
|
|
||||||
runServer var port = run port (serve testApi' $ server' var)
|
|
||||||
|
|
||||||
writeServiceJS :: FilePath -> IO ()
|
|
||||||
writeServiceJS fp =
|
|
||||||
writeJSForAPI testApi
|
|
||||||
(angularServiceWith (NG.defAngularOptions { NG.serviceName = "counterSvc" })
|
|
||||||
(defCommonGeneratorOptions { SJS.moduleName = "counterApp" })
|
|
||||||
)
|
|
||||||
fp
|
|
||||||
|
|
||||||
main :: IO ()
|
|
||||||
main = do
|
|
||||||
-- write the JS code to www/api.js at startup
|
|
||||||
writeJSForAPI testApi jquery (www </> "jquery" </> "api.js")
|
|
||||||
|
|
||||||
writeJSForAPI testApi vanillaJS (www </> "vanilla" </> "api.js")
|
|
||||||
|
|
||||||
writeJSForAPI testApi (angular defAngularOptions) (www </> "angular" </> "api.js")
|
|
||||||
|
|
||||||
writeJSForAPI testApi axios (www </> "axios" </> "api.js")
|
|
||||||
|
|
||||||
writeServiceJS (www </> "angular" </> "api.service.js")
|
|
||||||
|
|
||||||
-- setup a shared counter
|
|
||||||
cnt <- newCounter
|
|
||||||
|
|
||||||
-- listen to requests on port 8080
|
|
||||||
runServer cnt 8080
|
|
|
@ -1,39 +0,0 @@
|
||||||
POST /counter
|
|
||||||
-------------
|
|
||||||
|
|
||||||
**Response**:
|
|
||||||
|
|
||||||
- Status code 201
|
|
||||||
- Response body as below.
|
|
||||||
|
|
||||||
``` javascript
|
|
||||||
{"value":0}
|
|
||||||
```
|
|
||||||
|
|
||||||
GET /doc
|
|
||||||
--------
|
|
||||||
|
|
||||||
**Response**:
|
|
||||||
|
|
||||||
- Status code 200
|
|
||||||
- No response body
|
|
||||||
|
|
||||||
GET /counter
|
|
||||||
------------
|
|
||||||
|
|
||||||
**Response**:
|
|
||||||
|
|
||||||
- Status code 200
|
|
||||||
- Response body as below.
|
|
||||||
|
|
||||||
``` javascript
|
|
||||||
{"value":0}
|
|
||||||
```
|
|
||||||
|
|
||||||
GET /
|
|
||||||
-----
|
|
||||||
|
|
||||||
**Response**:
|
|
||||||
|
|
||||||
- Status code 200
|
|
||||||
- No response body
|
|
290
servant-js/examples/www/angular/angular.min.js
vendored
290
servant-js/examples/www/angular/angular.min.js
vendored
|
@ -1,290 +0,0 @@
|
||||||
/*
|
|
||||||
AngularJS v1.4.2
|
|
||||||
(c) 2010-2015 Google, Inc. http://angularjs.org
|
|
||||||
License: MIT
|
|
||||||
*/
|
|
||||||
(function(O,U,t){'use strict';function J(b){return function(){var a=arguments[0],c;c="["+(b?b+":":"")+a+"] http://errors.angularjs.org/1.4.2/"+(b?b+"/":"")+a;for(a=1;a<arguments.length;a++){c=c+(1==a?"?":"&")+"p"+(a-1)+"=";var d=encodeURIComponent,e;e=arguments[a];e="function"==typeof e?e.toString().replace(/ \{[\s\S]*$/,""):"undefined"==typeof e?"undefined":"string"!=typeof e?JSON.stringify(e):e;c+=d(e)}return Error(c)}}function Ea(b){if(null==b||Wa(b))return!1;var a="length"in Object(b)&&b.length;
|
|
||||||
return b.nodeType===qa&&a?!0:L(b)||G(b)||0===a||"number"===typeof a&&0<a&&a-1 in b}function m(b,a,c){var d,e;if(b)if(z(b))for(d in b)"prototype"==d||"length"==d||"name"==d||b.hasOwnProperty&&!b.hasOwnProperty(d)||a.call(c,b[d],d,b);else if(G(b)||Ea(b)){var f="object"!==typeof b;d=0;for(e=b.length;d<e;d++)(f||d in b)&&a.call(c,b[d],d,b)}else if(b.forEach&&b.forEach!==m)b.forEach(a,c,b);else if(nc(b))for(d in b)a.call(c,b[d],d,b);else if("function"===typeof b.hasOwnProperty)for(d in b)b.hasOwnProperty(d)&&
|
|
||||||
a.call(c,b[d],d,b);else for(d in b)Xa.call(b,d)&&a.call(c,b[d],d,b);return b}function oc(b,a,c){for(var d=Object.keys(b).sort(),e=0;e<d.length;e++)a.call(c,b[d[e]],d[e]);return d}function pc(b){return function(a,c){b(c,a)}}function Ud(){return++nb}function qc(b,a){a?b.$$hashKey=a:delete b.$$hashKey}function Nb(b,a,c){for(var d=b.$$hashKey,e=0,f=a.length;e<f;++e){var g=a[e];if(H(g)||z(g))for(var h=Object.keys(g),l=0,k=h.length;l<k;l++){var n=h[l],r=g[n];c&&H(r)?aa(r)?b[n]=new Date(r.valueOf()):(H(b[n])||
|
|
||||||
(b[n]=G(r)?[]:{}),Nb(b[n],[r],!0)):b[n]=r}}qc(b,d);return b}function P(b){return Nb(b,za.call(arguments,1),!1)}function Vd(b){return Nb(b,za.call(arguments,1),!0)}function W(b){return parseInt(b,10)}function Ob(b,a){return P(Object.create(b),a)}function v(){}function Ya(b){return b}function ra(b){return function(){return b}}function rc(b){return z(b.toString)&&b.toString!==Object.prototype.toString}function A(b){return"undefined"===typeof b}function w(b){return"undefined"!==typeof b}function H(b){return null!==
|
|
||||||
b&&"object"===typeof b}function nc(b){return null!==b&&"object"===typeof b&&!sc(b)}function L(b){return"string"===typeof b}function V(b){return"number"===typeof b}function aa(b){return"[object Date]"===sa.call(b)}function z(b){return"function"===typeof b}function Za(b){return"[object RegExp]"===sa.call(b)}function Wa(b){return b&&b.window===b}function $a(b){return b&&b.$evalAsync&&b.$watch}function ab(b){return"boolean"===typeof b}function tc(b){return!(!b||!(b.nodeName||b.prop&&b.attr&&b.find))}
|
|
||||||
function Wd(b){var a={};b=b.split(",");var c;for(c=0;c<b.length;c++)a[b[c]]=!0;return a}function ta(b){return M(b.nodeName||b[0]&&b[0].nodeName)}function bb(b,a){var c=b.indexOf(a);0<=c&&b.splice(c,1);return c}function fa(b,a,c,d){if(Wa(b)||$a(b))throw Fa("cpws");if(uc.test(sa.call(a)))throw Fa("cpta");if(a){if(b===a)throw Fa("cpi");c=c||[];d=d||[];H(b)&&(c.push(b),d.push(a));var e;if(G(b))for(e=a.length=0;e<b.length;e++)a.push(fa(b[e],null,c,d));else{var f=a.$$hashKey;G(a)?a.length=0:m(a,function(b,
|
|
||||||
c){delete a[c]});if(nc(b))for(e in b)a[e]=fa(b[e],null,c,d);else if(b&&"function"===typeof b.hasOwnProperty)for(e in b)b.hasOwnProperty(e)&&(a[e]=fa(b[e],null,c,d));else for(e in b)Xa.call(b,e)&&(a[e]=fa(b[e],null,c,d));qc(a,f)}}else if(a=b,H(b)){if(c&&-1!==(f=c.indexOf(b)))return d[f];if(G(b))return fa(b,[],c,d);if(uc.test(sa.call(b)))a=new b.constructor(b);else if(aa(b))a=new Date(b.getTime());else if(Za(b))a=new RegExp(b.source,b.toString().match(/[^\/]*$/)[0]),a.lastIndex=b.lastIndex;else return e=
|
|
||||||
Object.create(sc(b)),fa(b,e,c,d);d&&(c.push(b),d.push(a))}return a}function ia(b,a){if(G(b)){a=a||[];for(var c=0,d=b.length;c<d;c++)a[c]=b[c]}else if(H(b))for(c in a=a||{},b)if("$"!==c.charAt(0)||"$"!==c.charAt(1))a[c]=b[c];return a||b}function ka(b,a){if(b===a)return!0;if(null===b||null===a)return!1;if(b!==b&&a!==a)return!0;var c=typeof b,d;if(c==typeof a&&"object"==c)if(G(b)){if(!G(a))return!1;if((c=b.length)==a.length){for(d=0;d<c;d++)if(!ka(b[d],a[d]))return!1;return!0}}else{if(aa(b))return aa(a)?
|
|
||||||
ka(b.getTime(),a.getTime()):!1;if(Za(b))return Za(a)?b.toString()==a.toString():!1;if($a(b)||$a(a)||Wa(b)||Wa(a)||G(a)||aa(a)||Za(a))return!1;c=ga();for(d in b)if("$"!==d.charAt(0)&&!z(b[d])){if(!ka(b[d],a[d]))return!1;c[d]=!0}for(d in a)if(!(d in c||"$"===d.charAt(0)||a[d]===t||z(a[d])))return!1;return!0}return!1}function cb(b,a,c){return b.concat(za.call(a,c))}function vc(b,a){var c=2<arguments.length?za.call(arguments,2):[];return!z(a)||a instanceof RegExp?a:c.length?function(){return arguments.length?
|
|
||||||
a.apply(b,cb(c,arguments,0)):a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}}function Xd(b,a){var c=a;"string"===typeof b&&"$"===b.charAt(0)&&"$"===b.charAt(1)?c=t:Wa(a)?c="$WINDOW":a&&U===a?c="$DOCUMENT":$a(a)&&(c="$SCOPE");return c}function db(b,a){if("undefined"===typeof b)return t;V(a)||(a=a?2:null);return JSON.stringify(b,Xd,a)}function wc(b){return L(b)?JSON.parse(b):b}function xc(b,a){var c=Date.parse("Jan 01, 1970 00:00:00 "+b)/6E4;return isNaN(c)?a:c}function Pb(b,
|
|
||||||
a,c){c=c?-1:1;var d=xc(a,b.getTimezoneOffset());a=b;b=c*(d-b.getTimezoneOffset());a=new Date(a.getTime());a.setMinutes(a.getMinutes()+b);return a}function ua(b){b=y(b).clone();try{b.empty()}catch(a){}var c=y("<div>").append(b).html();try{return b[0].nodeType===Na?M(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+M(b)})}catch(d){return M(c)}}function yc(b){try{return decodeURIComponent(b)}catch(a){}}function zc(b){var a={},c,d;m((b||"").split("&"),function(b){b&&(c=b.replace(/\+/g,
|
|
||||||
"%20").split("="),d=yc(c[0]),w(d)&&(b=w(c[1])?yc(c[1]):!0,Xa.call(a,d)?G(a[d])?a[d].push(b):a[d]=[a[d],b]:a[d]=b))});return a}function Qb(b){var a=[];m(b,function(b,d){G(b)?m(b,function(b){a.push(ma(d,!0)+(!0===b?"":"="+ma(b,!0)))}):a.push(ma(d,!0)+(!0===b?"":"="+ma(b,!0)))});return a.length?a.join("&"):""}function ob(b){return ma(b,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function ma(b,a){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,
|
|
||||||
"$").replace(/%2C/gi,",").replace(/%3B/gi,";").replace(/%20/g,a?"%20":"+")}function Yd(b,a){var c,d,e=Oa.length;for(d=0;d<e;++d)if(c=Oa[d]+a,L(c=b.getAttribute(c)))return c;return null}function Zd(b,a){var c,d,e={};m(Oa,function(a){a+="app";!c&&b.hasAttribute&&b.hasAttribute(a)&&(c=b,d=b.getAttribute(a))});m(Oa,function(a){a+="app";var e;!c&&(e=b.querySelector("["+a.replace(":","\\:")+"]"))&&(c=e,d=e.getAttribute(a))});c&&(e.strictDi=null!==Yd(c,"strict-di"),a(c,d?[d]:[],e))}function Ac(b,a,c){H(c)||
|
|
||||||
(c={});c=P({strictDi:!1},c);var d=function(){b=y(b);if(b.injector()){var d=b[0]===U?"document":ua(b);throw Fa("btstrpd",d.replace(/</,"<").replace(/>/,">"));}a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);c.debugInfoEnabled&&a.push(["$compileProvider",function(a){a.debugInfoEnabled(!0)}]);a.unshift("ng");d=eb(a,c.strictDi);d.invoke(["$rootScope","$rootElement","$compile","$injector",function(a,b,c,d){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return d},e=
|
|
||||||
/^NG_ENABLE_DEBUG_INFO!/,f=/^NG_DEFER_BOOTSTRAP!/;O&&e.test(O.name)&&(c.debugInfoEnabled=!0,O.name=O.name.replace(e,""));if(O&&!f.test(O.name))return d();O.name=O.name.replace(f,"");ca.resumeBootstrap=function(b){m(b,function(b){a.push(b)});return d()};z(ca.resumeDeferredBootstrap)&&ca.resumeDeferredBootstrap()}function $d(){O.name="NG_ENABLE_DEBUG_INFO!"+O.name;O.location.reload()}function ae(b){b=ca.element(b).injector();if(!b)throw Fa("test");return b.get("$$testability")}function Bc(b,a){a=a||
|
|
||||||
"_";return b.replace(be,function(b,d){return(d?a:"")+b.toLowerCase()})}function ce(){var b;if(!Cc){var a=pb();la=O.jQuery;w(a)&&(la=null===a?t:O[a]);la&&la.fn.on?(y=la,P(la.fn,{scope:Pa.scope,isolateScope:Pa.isolateScope,controller:Pa.controller,injector:Pa.injector,inheritedData:Pa.inheritedData}),b=la.cleanData,la.cleanData=function(a){var d;if(Rb)Rb=!1;else for(var e=0,f;null!=(f=a[e]);e++)(d=la._data(f,"events"))&&d.$destroy&&la(f).triggerHandler("$destroy");b(a)}):y=Q;ca.element=y;Cc=!0}}function Sb(b,
|
|
||||||
a,c){if(!b)throw Fa("areq",a||"?",c||"required");return b}function Qa(b,a,c){c&&G(b)&&(b=b[b.length-1]);Sb(z(b),a,"not a function, got "+(b&&"object"===typeof b?b.constructor.name||"Object":typeof b));return b}function Ra(b,a){if("hasOwnProperty"===b)throw Fa("badname",a);}function Dc(b,a,c){if(!a)return b;a=a.split(".");for(var d,e=b,f=a.length,g=0;g<f;g++)d=a[g],b&&(b=(e=b)[d]);return!c&&z(b)?vc(e,b):b}function qb(b){var a=b[0];b=b[b.length-1];var c=[a];do{a=a.nextSibling;if(!a)break;c.push(a)}while(a!==
|
|
||||||
b);return y(c)}function ga(){return Object.create(null)}function de(b){function a(a,b,c){return a[b]||(a[b]=c())}var c=J("$injector"),d=J("ng");b=a(b,"angular",Object);b.$$minErr=b.$$minErr||J;return a(b,"module",function(){var b={};return function(f,g,h){if("hasOwnProperty"===f)throw d("badname","module");g&&b.hasOwnProperty(f)&&(b[f]=null);return a(b,f,function(){function a(b,c,e,f){f||(f=d);return function(){f[e||"push"]([b,c,arguments]);return C}}function b(a,c){return function(b,e){e&&z(e)&&
|
|
||||||
(e.$$moduleName=f);d.push([a,c,arguments]);return C}}if(!g)throw c("nomod",f);var d=[],e=[],s=[],x=a("$injector","invoke","push",e),C={_invokeQueue:d,_configBlocks:e,_runBlocks:s,requires:g,name:f,provider:b("$provide","provider"),factory:b("$provide","factory"),service:b("$provide","service"),value:a("$provide","value"),constant:a("$provide","constant","unshift"),decorator:b("$provide","decorator"),animation:b("$animateProvider","register"),filter:b("$filterProvider","register"),controller:b("$controllerProvider",
|
|
||||||
"register"),directive:b("$compileProvider","directive"),config:x,run:function(a){s.push(a);return this}};h&&x(h);return C})}})}function ee(b){P(b,{bootstrap:Ac,copy:fa,extend:P,merge:Vd,equals:ka,element:y,forEach:m,injector:eb,noop:v,bind:vc,toJson:db,fromJson:wc,identity:Ya,isUndefined:A,isDefined:w,isString:L,isFunction:z,isObject:H,isNumber:V,isElement:tc,isArray:G,version:fe,isDate:aa,lowercase:M,uppercase:rb,callbacks:{counter:0},getTestability:ae,$$minErr:J,$$csp:fb,reloadWithDebugInfo:$d});
|
|
||||||
gb=de(O);try{gb("ngLocale")}catch(a){gb("ngLocale",[]).provider("$locale",ge)}gb("ng",["ngLocale"],["$provide",function(a){a.provider({$$sanitizeUri:he});a.provider("$compile",Ec).directive({a:ie,input:Fc,textarea:Fc,form:je,script:ke,select:le,style:me,option:ne,ngBind:oe,ngBindHtml:pe,ngBindTemplate:qe,ngClass:re,ngClassEven:se,ngClassOdd:te,ngCloak:ue,ngController:ve,ngForm:we,ngHide:xe,ngIf:ye,ngInclude:ze,ngInit:Ae,ngNonBindable:Be,ngPluralize:Ce,ngRepeat:De,ngShow:Ee,ngStyle:Fe,ngSwitch:Ge,
|
|
||||||
ngSwitchWhen:He,ngSwitchDefault:Ie,ngOptions:Je,ngTransclude:Ke,ngModel:Le,ngList:Me,ngChange:Ne,pattern:Gc,ngPattern:Gc,required:Hc,ngRequired:Hc,minlength:Ic,ngMinlength:Ic,maxlength:Jc,ngMaxlength:Jc,ngValue:Oe,ngModelOptions:Pe}).directive({ngInclude:Qe}).directive(sb).directive(Kc);a.provider({$anchorScroll:Re,$animate:Se,$$animateQueue:Te,$$AnimateRunner:Ue,$browser:Ve,$cacheFactory:We,$controller:Xe,$document:Ye,$exceptionHandler:Ze,$filter:Lc,$interpolate:$e,$interval:af,$http:bf,$httpParamSerializer:cf,
|
|
||||||
$httpParamSerializerJQLike:df,$httpBackend:ef,$location:ff,$log:gf,$parse:hf,$rootScope:jf,$q:kf,$$q:lf,$sce:mf,$sceDelegate:nf,$sniffer:of,$templateCache:pf,$templateRequest:qf,$$testability:rf,$timeout:sf,$window:tf,$$rAF:uf,$$asyncCallback:vf,$$jqLite:wf,$$HashMap:xf,$$cookieReader:yf})}])}function hb(b){return b.replace(zf,function(a,b,d,e){return e?d.toUpperCase():d}).replace(Af,"Moz$1")}function Mc(b){b=b.nodeType;return b===qa||!b||9===b}function Nc(b,a){var c,d,e=a.createDocumentFragment(),
|
|
||||||
f=[];if(Tb.test(b)){c=c||e.appendChild(a.createElement("div"));d=(Bf.exec(b)||["",""])[1].toLowerCase();d=na[d]||na._default;c.innerHTML=d[1]+b.replace(Cf,"<$1></$2>")+d[2];for(d=d[0];d--;)c=c.lastChild;f=cb(f,c.childNodes);c=e.firstChild;c.textContent=""}else f.push(a.createTextNode(b));e.textContent="";e.innerHTML="";m(f,function(a){e.appendChild(a)});return e}function Q(b){if(b instanceof Q)return b;var a;L(b)&&(b=R(b),a=!0);if(!(this instanceof Q)){if(a&&"<"!=b.charAt(0))throw Ub("nosel");return new Q(b)}if(a){a=
|
|
||||||
U;var c;b=(c=Df.exec(b))?[a.createElement(c[1])]:(c=Nc(b,a))?c.childNodes:[]}Oc(this,b)}function Vb(b){return b.cloneNode(!0)}function tb(b,a){a||ub(b);if(b.querySelectorAll)for(var c=b.querySelectorAll("*"),d=0,e=c.length;d<e;d++)ub(c[d])}function Pc(b,a,c,d){if(w(d))throw Ub("offargs");var e=(d=vb(b))&&d.events,f=d&&d.handle;if(f)if(a)m(a.split(" "),function(a){if(w(c)){var d=e[a];bb(d||[],c);if(d&&0<d.length)return}b.removeEventListener(a,f,!1);delete e[a]});else for(a in e)"$destroy"!==a&&b.removeEventListener(a,
|
|
||||||
f,!1),delete e[a]}function ub(b,a){var c=b.ng339,d=c&&ib[c];d&&(a?delete d.data[a]:(d.handle&&(d.events.$destroy&&d.handle({},"$destroy"),Pc(b)),delete ib[c],b.ng339=t))}function vb(b,a){var c=b.ng339,c=c&&ib[c];a&&!c&&(b.ng339=c=++Ef,c=ib[c]={events:{},data:{},handle:t});return c}function Wb(b,a,c){if(Mc(b)){var d=w(c),e=!d&&a&&!H(a),f=!a;b=(b=vb(b,!e))&&b.data;if(d)b[a]=c;else{if(f)return b;if(e)return b&&b[a];P(b,a)}}}function wb(b,a){return b.getAttribute?-1<(" "+(b.getAttribute("class")||"")+
|
|
||||||
" ").replace(/[\n\t]/g," ").indexOf(" "+a+" "):!1}function xb(b,a){a&&b.setAttribute&&m(a.split(" "),function(a){b.setAttribute("class",R((" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").replace(" "+R(a)+" "," ")))})}function yb(b,a){if(a&&b.setAttribute){var c=(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ");m(a.split(" "),function(a){a=R(a);-1===c.indexOf(" "+a+" ")&&(c+=a+" ")});b.setAttribute("class",R(c))}}function Oc(b,a){if(a)if(a.nodeType)b[b.length++]=a;else{var c=
|
|
||||||
a.length;if("number"===typeof c&&a.window!==a){if(c)for(var d=0;d<c;d++)b[b.length++]=a[d]}else b[b.length++]=a}}function Qc(b,a){return zb(b,"$"+(a||"ngController")+"Controller")}function zb(b,a,c){9==b.nodeType&&(b=b.documentElement);for(a=G(a)?a:[a];b;){for(var d=0,e=a.length;d<e;d++)if((c=y.data(b,a[d]))!==t)return c;b=b.parentNode||11===b.nodeType&&b.host}}function Rc(b){for(tb(b,!0);b.firstChild;)b.removeChild(b.firstChild)}function Xb(b,a){a||tb(b);var c=b.parentNode;c&&c.removeChild(b)}function Ff(b,
|
|
||||||
a){a=a||O;if("complete"===a.document.readyState)a.setTimeout(b);else y(a).on("load",b)}function Sc(b,a){var c=Ab[a.toLowerCase()];return c&&Tc[ta(b)]&&c}function Gf(b,a){var c=b.nodeName;return("INPUT"===c||"TEXTAREA"===c)&&Uc[a]}function Hf(b,a){var c=function(c,e){c.isDefaultPrevented=function(){return c.defaultPrevented};var f=a[e||c.type],g=f?f.length:0;if(g){if(A(c.immediatePropagationStopped)){var h=c.stopImmediatePropagation;c.stopImmediatePropagation=function(){c.immediatePropagationStopped=
|
|
||||||
!0;c.stopPropagation&&c.stopPropagation();h&&h.call(c)}}c.isImmediatePropagationStopped=function(){return!0===c.immediatePropagationStopped};1<g&&(f=ia(f));for(var l=0;l<g;l++)c.isImmediatePropagationStopped()||f[l].call(b,c)}};c.elem=b;return c}function wf(){this.$get=function(){return P(Q,{hasClass:function(b,a){b.attr&&(b=b[0]);return wb(b,a)},addClass:function(b,a){b.attr&&(b=b[0]);return yb(b,a)},removeClass:function(b,a){b.attr&&(b=b[0]);return xb(b,a)}})}}function Ga(b,a){var c=b&&b.$$hashKey;
|
|
||||||
if(c)return"function"===typeof c&&(c=b.$$hashKey()),c;c=typeof b;return c="function"==c||"object"==c&&null!==b?b.$$hashKey=c+":"+(a||Ud)():c+":"+b}function Sa(b,a){if(a){var c=0;this.nextUid=function(){return++c}}m(b,this.put,this)}function If(b){return(b=b.toString().replace(Vc,"").match(Wc))?"function("+(b[1]||"").replace(/[\s\r\n]+/," ")+")":"fn"}function eb(b,a){function c(a){return function(b,c){if(H(b))m(b,pc(a));else return a(b,c)}}function d(a,b){Ra(a,"service");if(z(b)||G(b))b=s.instantiate(b);
|
|
||||||
if(!b.$get)throw Ha("pget",a);return r[a+"Provider"]=b}function e(a,b){return function(){var c=C.invoke(b,this);if(A(c))throw Ha("undef",a);return c}}function f(a,b,c){return d(a,{$get:!1!==c?e(a,b):b})}function g(a){var b=[],c;m(a,function(a){function d(a){var b,c;b=0;for(c=a.length;b<c;b++){var e=a[b],f=s.get(e[0]);f[e[1]].apply(f,e[2])}}if(!n.get(a)){n.put(a,!0);try{L(a)?(c=gb(a),b=b.concat(g(c.requires)).concat(c._runBlocks),d(c._invokeQueue),d(c._configBlocks)):z(a)?b.push(s.invoke(a)):G(a)?
|
|
||||||
b.push(s.invoke(a)):Qa(a,"module")}catch(e){throw G(a)&&(a=a[a.length-1]),e.message&&e.stack&&-1==e.stack.indexOf(e.message)&&(e=e.message+"\n"+e.stack),Ha("modulerr",a,e.stack||e.message||e);}}});return b}function h(b,c){function d(a,e){if(b.hasOwnProperty(a)){if(b[a]===l)throw Ha("cdep",a+" <- "+k.join(" <- "));return b[a]}try{return k.unshift(a),b[a]=l,b[a]=c(a,e)}catch(f){throw b[a]===l&&delete b[a],f;}finally{k.shift()}}function e(b,c,f,g){"string"===typeof f&&(g=f,f=null);var h=[],k=eb.$$annotate(b,
|
|
||||||
a,g),l,s,n;s=0;for(l=k.length;s<l;s++){n=k[s];if("string"!==typeof n)throw Ha("itkn",n);h.push(f&&f.hasOwnProperty(n)?f[n]:d(n,g))}G(b)&&(b=b[l]);return b.apply(c,h)}return{invoke:e,instantiate:function(a,b,c){var d=Object.create((G(a)?a[a.length-1]:a).prototype||null);a=e(a,d,b,c);return H(a)||z(a)?a:d},get:d,annotate:eb.$$annotate,has:function(a){return r.hasOwnProperty(a+"Provider")||b.hasOwnProperty(a)}}}a=!0===a;var l={},k=[],n=new Sa([],!0),r={$provide:{provider:c(d),factory:c(f),service:c(function(a,
|
|
||||||
b){return f(a,["$injector",function(a){return a.instantiate(b)}])}),value:c(function(a,b){return f(a,ra(b),!1)}),constant:c(function(a,b){Ra(a,"constant");r[a]=b;x[a]=b}),decorator:function(a,b){var c=s.get(a+"Provider"),d=c.$get;c.$get=function(){var a=C.invoke(d,c);return C.invoke(b,null,{$delegate:a})}}}},s=r.$injector=h(r,function(a,b){ca.isString(b)&&k.push(b);throw Ha("unpr",k.join(" <- "));}),x={},C=x.$injector=h(x,function(a,b){var c=s.get(a+"Provider",b);return C.invoke(c.$get,c,t,a)});m(g(b),
|
|
||||||
function(a){a&&C.invoke(a)});return C}function Re(){var b=!0;this.disableAutoScrolling=function(){b=!1};this.$get=["$window","$location","$rootScope",function(a,c,d){function e(a){var b=null;Array.prototype.some.call(a,function(a){if("a"===ta(a))return b=a,!0});return b}function f(b){if(b){b.scrollIntoView();var c;c=g.yOffset;z(c)?c=c():tc(c)?(c=c[0],c="fixed"!==a.getComputedStyle(c).position?0:c.getBoundingClientRect().bottom):V(c)||(c=0);c&&(b=b.getBoundingClientRect().top,a.scrollBy(0,b-c))}else a.scrollTo(0,
|
|
||||||
0)}function g(a){a=L(a)?a:c.hash();var b;a?(b=h.getElementById(a))?f(b):(b=e(h.getElementsByName(a)))?f(b):"top"===a&&f(null):f(null)}var h=a.document;b&&d.$watch(function(){return c.hash()},function(a,b){a===b&&""===a||Ff(function(){d.$evalAsync(g)})});return g}]}function jb(b,a){if(!b&&!a)return"";if(!b)return a;if(!a)return b;G(b)&&(b=b.join(" "));G(a)&&(a=a.join(" "));return b+" "+a}function Jf(b){L(b)&&(b=b.split(" "));var a=ga();m(b,function(b){b.length&&(a[b]=!0)});return a}function Ia(b){return H(b)?
|
|
||||||
b:{}}function vf(){this.$get=["$$rAF","$timeout",function(b,a){return b.supported?function(a){return b(a)}:function(b){return a(b,0,!1)}}]}function Kf(b,a,c,d){function e(a){try{a.apply(null,za.call(arguments,1))}finally{if(C--,0===C)for(;F.length;)try{F.pop()()}catch(b){c.error(b)}}}function f(){g();h()}function g(){a:{try{u=n.state;break a}catch(a){}u=void 0}u=A(u)?null:u;ka(u,D)&&(u=D);D=u}function h(){if(K!==l.url()||p!==u)K=l.url(),p=u,m(B,function(a){a(l.url(),u)})}var l=this,k=b.location,n=
|
|
||||||
b.history,r=b.setTimeout,s=b.clearTimeout,x={};l.isMock=!1;var C=0,F=[];l.$$completeOutstandingRequest=e;l.$$incOutstandingRequestCount=function(){C++};l.notifyWhenNoOutstandingRequests=function(a){0===C?a():F.push(a)};var u,p,K=k.href,q=a.find("base"),I=null;g();p=u;l.url=function(a,c,e){A(e)&&(e=null);k!==b.location&&(k=b.location);n!==b.history&&(n=b.history);if(a){var f=p===e;if(K===a&&(!d.history||f))return l;var h=K&&Ja(K)===Ja(a);K=a;p=e;if(!d.history||h&&f){if(!h||I)I=a;c?k.replace(a):h?(c=
|
|
||||||
k,e=a.indexOf("#"),a=-1===e?"":a.substr(e),c.hash=a):k.href=a}else n[c?"replaceState":"pushState"](e,"",a),g(),p=u;return l}return I||k.href.replace(/%27/g,"'")};l.state=function(){return u};var B=[],N=!1,D=null;l.onUrlChange=function(a){if(!N){if(d.history)y(b).on("popstate",f);y(b).on("hashchange",f);N=!0}B.push(a);return a};l.$$applicationDestroyed=function(){y(b).off("hashchange popstate",f)};l.$$checkUrlChange=h;l.baseHref=function(){var a=q.attr("href");return a?a.replace(/^(https?\:)?\/\/[^\/]*/,
|
|
||||||
""):""};l.defer=function(a,b){var c;C++;c=r(function(){delete x[c];e(a)},b||0);x[c]=!0;return c};l.defer.cancel=function(a){return x[a]?(delete x[a],s(a),e(v),!0):!1}}function Ve(){this.$get=["$window","$log","$sniffer","$document",function(b,a,c,d){return new Kf(b,d,a,c)}]}function We(){this.$get=function(){function b(b,d){function e(a){a!=r&&(s?s==a&&(s=a.n):s=a,f(a.n,a.p),f(a,r),r=a,r.n=null)}function f(a,b){a!=b&&(a&&(a.p=b),b&&(b.n=a))}if(b in a)throw J("$cacheFactory")("iid",b);var g=0,h=P({},
|
|
||||||
d,{id:b}),l={},k=d&&d.capacity||Number.MAX_VALUE,n={},r=null,s=null;return a[b]={put:function(a,b){if(!A(b)){if(k<Number.MAX_VALUE){var c=n[a]||(n[a]={key:a});e(c)}a in l||g++;l[a]=b;g>k&&this.remove(s.key);return b}},get:function(a){if(k<Number.MAX_VALUE){var b=n[a];if(!b)return;e(b)}return l[a]},remove:function(a){if(k<Number.MAX_VALUE){var b=n[a];if(!b)return;b==r&&(r=b.p);b==s&&(s=b.n);f(b.n,b.p);delete n[a]}delete l[a];g--},removeAll:function(){l={};g=0;n={};r=s=null},destroy:function(){n=h=
|
|
||||||
l=null;delete a[b]},info:function(){return P({},h,{size:g})}}}var a={};b.info=function(){var b={};m(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return a[b]};return b}}function pf(){this.$get=["$cacheFactory",function(b){return b("templates")}]}function Ec(b,a){function c(a,b,c){var d=/^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/,e={};m(a,function(a,f){var g=a.match(d);if(!g)throw ea("iscp",b,f,a,c?"controller bindings definition":"isolate scope definition");e[f]={mode:g[1][0],collection:"*"===
|
|
||||||
g[2],optional:"?"===g[3],attrName:g[4]||f}});return e}function d(a){var b=a.charAt(0);if(!b||b!==M(b))throw ea("baddir",a);if(a!==a.trim())throw ea("baddir",a);}var e={},f=/^\s*directive\:\s*([\w\-]+)\s+(.*)$/,g=/(([\w\-]+)(?:\:([^;]+))?;?)/,h=Wd("ngSrc,ngSrcset,src,srcset"),l=/^(?:(\^\^?)?(\?)?(\^\^?)?)?/,k=/^(on[a-z]+|formaction)$/;this.directive=function s(a,f){Ra(a,"directive");L(a)?(d(a),Sb(f,"directiveFactory"),e.hasOwnProperty(a)||(e[a]=[],b.factory(a+"Directive",["$injector","$exceptionHandler",
|
|
||||||
function(b,d){var f=[];m(e[a],function(e,g){try{var h=b.invoke(e);z(h)?h={compile:ra(h)}:!h.compile&&h.link&&(h.compile=ra(h.link));h.priority=h.priority||0;h.index=g;h.name=h.name||a;h.require=h.require||h.controller&&h.name;h.restrict=h.restrict||"EA";var k=h,l=h,s=h.name,n={isolateScope:null,bindToController:null};H(l.scope)&&(!0===l.bindToController?(n.bindToController=c(l.scope,s,!0),n.isolateScope={}):n.isolateScope=c(l.scope,s,!1));H(l.bindToController)&&(n.bindToController=c(l.bindToController,
|
|
||||||
s,!0));if(H(n.bindToController)){var C=l.controller,$=l.controllerAs;if(!C)throw ea("noctrl",s);var ha;a:if($&&L($))ha=$;else{if(L(C)){var m=Xc.exec(C);if(m){ha=m[3];break a}}ha=void 0}if(!ha)throw ea("noident",s);}var q=k.$$bindings=n;H(q.isolateScope)&&(h.$$isolateBindings=q.isolateScope);h.$$moduleName=e.$$moduleName;f.push(h)}catch(t){d(t)}});return f}])),e[a].push(f)):m(a,pc(s));return this};this.aHrefSanitizationWhitelist=function(b){return w(b)?(a.aHrefSanitizationWhitelist(b),this):a.aHrefSanitizationWhitelist()};
|
|
||||||
this.imgSrcSanitizationWhitelist=function(b){return w(b)?(a.imgSrcSanitizationWhitelist(b),this):a.imgSrcSanitizationWhitelist()};var n=!0;this.debugInfoEnabled=function(a){return w(a)?(n=a,this):n};this.$get=["$injector","$interpolate","$exceptionHandler","$templateRequest","$parse","$controller","$rootScope","$document","$sce","$animate","$$sanitizeUri",function(a,b,c,d,u,p,K,q,I,B,N){function D(a,b){try{a.addClass(b)}catch(c){}}function Z(a,b,c,d,e){a instanceof y||(a=y(a));m(a,function(b,c){b.nodeType==
|
|
||||||
Na&&b.nodeValue.match(/\S+/)&&(a[c]=y(b).wrap("<span></span>").parent()[0])});var f=S(a,b,a,c,d,e);Z.$$addScopeClass(a);var g=null;return function(b,c,d){Sb(b,"scope");d=d||{};var e=d.parentBoundTranscludeFn,h=d.transcludeControllers;d=d.futureParentElement;e&&e.$$boundTransclude&&(e=e.$$boundTransclude);g||(g=(d=d&&d[0])?"foreignobject"!==ta(d)&&d.toString().match(/SVG/)?"svg":"html":"html");d="html"!==g?y(Yb(g,y("<div>").append(a).html())):c?Pa.clone.call(a):a;if(h)for(var k in h)d.data("$"+k+"Controller",
|
|
||||||
h[k].instance);Z.$$addScopeInfo(d,b);c&&c(d,b);f&&f(b,d,d,e);return d}}function S(a,b,c,d,e,f){function g(a,c,d,e){var f,k,l,s,n,B,C;if(p)for(C=Array(c.length),s=0;s<h.length;s+=3)f=h[s],C[f]=c[f];else C=c;s=0;for(n=h.length;s<n;)if(k=C[h[s++]],c=h[s++],f=h[s++],c){if(c.scope){if(l=a.$new(),Z.$$addScopeInfo(y(k),l),B=c.$$destroyBindings)c.$$destroyBindings=null,l.$on("$destroyed",B)}else l=a;B=c.transcludeOnThisElement?$(a,c.transclude,e):!c.templateOnThisElement&&e?e:!e&&b?$(a,b):null;c(f,l,k,d,
|
|
||||||
B,c)}else f&&f(a,k.childNodes,t,e)}for(var h=[],k,l,s,n,p,B=0;B<a.length;B++){k=new aa;l=ha(a[B],[],k,0===B?d:t,e);(f=l.length?E(l,a[B],k,b,c,null,[],[],f):null)&&f.scope&&Z.$$addScopeClass(k.$$element);k=f&&f.terminal||!(s=a[B].childNodes)||!s.length?null:S(s,f?(f.transcludeOnThisElement||!f.templateOnThisElement)&&f.transclude:b);if(f||k)h.push(B,f,k),n=!0,p=p||f;f=null}return n?g:null}function $(a,b,c){return function(d,e,f,g,h){d||(d=a.$new(!1,h),d.$$transcluded=!0);return b(d,e,{parentBoundTranscludeFn:c,
|
|
||||||
transcludeControllers:f,futureParentElement:g})}}function ha(a,b,c,d,e){var h=c.$attr,k;switch(a.nodeType){case qa:w(b,wa(ta(a)),"E",d,e);for(var l,s,n,p=a.attributes,B=0,C=p&&p.length;B<C;B++){var x=!1,S=!1;l=p[B];k=l.name;s=R(l.value);l=wa(k);if(n=ia.test(l))k=k.replace(Zc,"").substr(8).replace(/_(.)/g,function(a,b){return b.toUpperCase()});var F=l.replace(/(Start|End)$/,"");A(F)&&l===F+"Start"&&(x=k,S=k.substr(0,k.length-5)+"end",k=k.substr(0,k.length-6));l=wa(k.toLowerCase());h[l]=k;if(n||!c.hasOwnProperty(l))c[l]=
|
|
||||||
s,Sc(a,l)&&(c[l]=!0);V(a,b,s,l,n);w(b,l,"A",d,e,x,S)}a=a.className;H(a)&&(a=a.animVal);if(L(a)&&""!==a)for(;k=g.exec(a);)l=wa(k[2]),w(b,l,"C",d,e)&&(c[l]=R(k[3])),a=a.substr(k.index+k[0].length);break;case Na:if(11===Ua)for(;a.parentNode&&a.nextSibling&&a.nextSibling.nodeType===Na;)a.nodeValue+=a.nextSibling.nodeValue,a.parentNode.removeChild(a.nextSibling);xa(b,a.nodeValue);break;case 8:try{if(k=f.exec(a.nodeValue))l=wa(k[1]),w(b,l,"M",d,e)&&(c[l]=R(k[2]))}catch($){}}b.sort(Aa);return b}function va(a,
|
|
||||||
b,c){var d=[],e=0;if(b&&a.hasAttribute&&a.hasAttribute(b)){do{if(!a)throw ea("uterdir",b,c);a.nodeType==qa&&(a.hasAttribute(b)&&e++,a.hasAttribute(c)&&e--);d.push(a);a=a.nextSibling}while(0<e)}else d.push(a);return y(d)}function Yc(a,b,c){return function(d,e,f,g,h){e=va(e[0],b,c);return a(d,e,f,g,h)}}function E(a,b,d,e,f,g,h,k,s){function n(a,b,c,d){if(a){c&&(a=Yc(a,c,d));a.require=E.require;a.directiveName=w;if(u===E||E.$$isolateScope)a=X(a,{isolateScope:!0});h.push(a)}if(b){c&&(b=Yc(b,c,d));b.require=
|
|
||||||
E.require;b.directiveName=w;if(u===E||E.$$isolateScope)b=X(b,{isolateScope:!0});k.push(b)}}function B(a,b,c,d){var e;if(L(b)){var f=b.match(l);b=b.substring(f[0].length);var g=f[1]||f[3],f="?"===f[2];"^^"===g?c=c.parent():e=(e=d&&d[b])&&e.instance;e||(d="$"+b+"Controller",e=g?c.inheritedData(d):c.data(d));if(!e&&!f)throw ea("ctreq",b,a);}else if(G(b))for(e=[],g=0,f=b.length;g<f;g++)e[g]=B(a,b[g],c,d);return e||null}function x(a,b,c,d,e,f){var g=ga(),h;for(h in d){var k=d[h],l={$scope:k===u||k.$$isolateScope?
|
|
||||||
e:f,$element:a,$attrs:b,$transclude:c},s=k.controller;"@"==s&&(s=b[k.name]);l=p(s,l,!0,k.controllerAs);g[k.name]=l;q||a.data("$"+k.name+"Controller",l.instance)}return g}function S(a,c,e,f,g,l){function s(a,b,c){var d;$a(a)||(c=b,b=a,a=t);q&&(d=m);c||(c=q?ja.parent():ja);return g(a,b,d,c,va)}var n,p,C,F,m,ha,ja;b===e?(f=d,ja=d.$$element):(ja=y(e),f=new aa(ja,d));u&&(F=c.$new(!0));g&&(ha=s,ha.$$boundTransclude=g);N&&(m=x(ja,f,ha,N,F,c));u&&(Z.$$addScopeInfo(ja,F,!0,!(D&&(D===u||D===u.$$originalDirective))),
|
|
||||||
Z.$$addScopeClass(ja,!0),F.$$isolateBindings=u.$$isolateBindings,W(c,f,F,F.$$isolateBindings,u,F));if(m){var K=u||$,I;K&&m[K.name]&&(p=K.$$bindings.bindToController,(C=m[K.name])&&C.identifier&&p&&(I=C,l.$$destroyBindings=W(c,f,C.instance,p,K)));for(n in m){C=m[n];var E=C();E!==C.instance&&(C.instance=E,ja.data("$"+n+"Controller",E),C===I&&(l.$$destroyBindings(),l.$$destroyBindings=W(c,f,E,p,K)))}}n=0;for(l=h.length;n<l;n++)p=h[n],Y(p,p.isolateScope?F:c,ja,f,p.require&&B(p.directiveName,p.require,
|
|
||||||
ja,m),ha);var va=c;u&&(u.template||null===u.templateUrl)&&(va=F);a&&a(va,e.childNodes,t,g);for(n=k.length-1;0<=n;n--)p=k[n],Y(p,p.isolateScope?F:c,ja,f,p.require&&B(p.directiveName,p.require,ja,m),ha)}s=s||{};for(var F=-Number.MAX_VALUE,$=s.newScopeDirective,N=s.controllerDirectives,u=s.newIsolateScopeDirective,D=s.templateDirective,m=s.nonTlbTranscludeDirective,K=!1,I=!1,q=s.hasElementTranscludeDirective,ba=d.$$element=y(b),E,w,v,A=e,Aa,xa=0,Ta=a.length;xa<Ta;xa++){E=a[xa];var M=E.$$start,P=E.$$end;
|
|
||||||
M&&(ba=va(b,M,P));v=t;if(F>E.priority)break;if(v=E.scope)E.templateUrl||(H(v)?(O("new/isolated scope",u||$,E,ba),u=E):O("new/isolated scope",u,E,ba)),$=$||E;w=E.name;!E.templateUrl&&E.controller&&(v=E.controller,N=N||ga(),O("'"+w+"' controller",N[w],E,ba),N[w]=E);if(v=E.transclude)K=!0,E.$$tlb||(O("transclusion",m,E,ba),m=E),"element"==v?(q=!0,F=E.priority,v=ba,ba=d.$$element=y(U.createComment(" "+w+": "+d[w]+" ")),b=ba[0],T(f,za.call(v,0),b),A=Z(v,e,F,g&&g.name,{nonTlbTranscludeDirective:m})):(v=
|
|
||||||
y(Vb(b)).contents(),ba.empty(),A=Z(v,e));if(E.template)if(I=!0,O("template",D,E,ba),D=E,v=z(E.template)?E.template(ba,d):E.template,v=fa(v),E.replace){g=E;v=Tb.test(v)?$c(Yb(E.templateNamespace,R(v))):[];b=v[0];if(1!=v.length||b.nodeType!==qa)throw ea("tplrt",w,"");T(f,ba,b);Ta={$attr:{}};v=ha(b,[],Ta);var Q=a.splice(xa+1,a.length-(xa+1));u&&ad(v);a=a.concat(v).concat(Q);J(d,Ta);Ta=a.length}else ba.html(v);if(E.templateUrl)I=!0,O("template",D,E,ba),D=E,E.replace&&(g=E),S=Mf(a.splice(xa,a.length-xa),
|
|
||||||
ba,d,f,K&&A,h,k,{controllerDirectives:N,newScopeDirective:$!==E&&$,newIsolateScopeDirective:u,templateDirective:D,nonTlbTranscludeDirective:m}),Ta=a.length;else if(E.compile)try{Aa=E.compile(ba,d,A),z(Aa)?n(null,Aa,M,P):Aa&&n(Aa.pre,Aa.post,M,P)}catch(Lf){c(Lf,ua(ba))}E.terminal&&(S.terminal=!0,F=Math.max(F,E.priority))}S.scope=$&&!0===$.scope;S.transcludeOnThisElement=K;S.templateOnThisElement=I;S.transclude=A;s.hasElementTranscludeDirective=q;return S}function ad(a){for(var b=0,c=a.length;b<c;b++)a[b]=
|
|
||||||
Ob(a[b],{$$isolateScope:!0})}function w(b,d,f,g,h,k,l){if(d===h)return null;h=null;if(e.hasOwnProperty(d)){var n;d=a.get(d+"Directive");for(var p=0,B=d.length;p<B;p++)try{n=d[p],(g===t||g>n.priority)&&-1!=n.restrict.indexOf(f)&&(k&&(n=Ob(n,{$$start:k,$$end:l})),b.push(n),h=n)}catch(x){c(x)}}return h}function A(b){if(e.hasOwnProperty(b))for(var c=a.get(b+"Directive"),d=0,f=c.length;d<f;d++)if(b=c[d],b.multiElement)return!0;return!1}function J(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;m(a,function(d,
|
|
||||||
e){"$"!=e.charAt(0)&&(b[e]&&b[e]!==d&&(d+=("style"===e?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});m(b,function(b,f){"class"==f?(D(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):"style"==f?(e.attr("style",e.attr("style")+";"+b),a.style=(a.style?a.style+";":"")+b):"$"==f.charAt(0)||a.hasOwnProperty(f)||(a[f]=b,d[f]=c[f])})}function Mf(a,b,c,e,f,g,h,k){var l=[],s,n,p=b[0],B=a.shift(),C=Ob(B,{templateUrl:null,transclude:null,replace:null,$$originalDirective:B}),x=z(B.templateUrl)?B.templateUrl(b,c):B.templateUrl,
|
|
||||||
N=B.templateNamespace;b.empty();d(x).then(function(d){var F,u;d=fa(d);if(B.replace){d=Tb.test(d)?$c(Yb(N,R(d))):[];F=d[0];if(1!=d.length||F.nodeType!==qa)throw ea("tplrt",B.name,x);d={$attr:{}};T(e,b,F);var K=ha(F,[],d);H(B.scope)&&ad(K);a=K.concat(a);J(c,d)}else F=p,b.html(d);a.unshift(C);s=E(a,F,c,f,b,B,g,h,k);m(e,function(a,c){a==F&&(e[c]=b[0])});for(n=S(b[0].childNodes,f);l.length;){d=l.shift();u=l.shift();var I=l.shift(),va=l.shift(),K=b[0];if(!d.$$destroyed){if(u!==p){var Z=u.className;k.hasElementTranscludeDirective&&
|
|
||||||
B.replace||(K=Vb(F));T(I,y(u),K);D(y(K),Z)}u=s.transcludeOnThisElement?$(d,s.transclude,va):va;s(n,d,K,e,u,s)}}l=null});return function(a,b,c,d,e){a=e;b.$$destroyed||(l?l.push(b,c,d,a):(s.transcludeOnThisElement&&(a=$(b,s.transclude,e)),s(n,b,c,d,a,s)))}}function Aa(a,b){var c=b.priority-a.priority;return 0!==c?c:a.name!==b.name?a.name<b.name?-1:1:a.index-b.index}function O(a,b,c,d){function e(a){return a?" (module: "+a+")":""}if(b)throw ea("multidir",b.name,e(b.$$moduleName),c.name,e(c.$$moduleName),
|
|
||||||
a,ua(d));}function xa(a,c){var d=b(c,!0);d&&a.push({priority:0,compile:function(a){a=a.parent();var b=!!a.length;b&&Z.$$addBindingClass(a);return function(a,c){var e=c.parent();b||Z.$$addBindingClass(e);Z.$$addBindingInfo(e,d.expressions);a.$watch(d,function(a){c[0].nodeValue=a})}}})}function Yb(a,b){a=M(a||"html");switch(a){case "svg":case "math":var c=U.createElement("div");c.innerHTML="<"+a+">"+b+"</"+a+">";return c.childNodes[0].childNodes;default:return b}}function Q(a,b){if("srcdoc"==b)return I.HTML;
|
|
||||||
var c=ta(a);if("xlinkHref"==b||"form"==c&&"action"==b||"img"!=c&&("src"==b||"ngSrc"==b))return I.RESOURCE_URL}function V(a,c,d,e,f){var g=Q(a,e);f=h[e]||f;var l=b(d,!0,g,f);if(l){if("multiple"===e&&"select"===ta(a))throw ea("selmulti",ua(a));c.push({priority:100,compile:function(){return{pre:function(a,c,h){c=h.$$observers||(h.$$observers={});if(k.test(e))throw ea("nodomevents");var s=h[e];s!==d&&(l=s&&b(s,!0,g,f),d=s);l&&(h[e]=l(a),(c[e]||(c[e]=[])).$$inter=!0,(h.$$observers&&h.$$observers[e].$$scope||
|
|
||||||
a).$watch(l,function(a,b){"class"===e&&a!=b?h.$updateClass(a,b):h.$set(e,a)}))}}}})}}function T(a,b,c){var d=b[0],e=b.length,f=d.parentNode,g,h;if(a)for(g=0,h=a.length;g<h;g++)if(a[g]==d){a[g++]=c;h=g+e-1;for(var k=a.length;g<k;g++,h++)h<k?a[g]=a[h]:delete a[g];a.length-=e-1;a.context===d&&(a.context=c);break}f&&f.replaceChild(c,d);a=U.createDocumentFragment();a.appendChild(d);y.hasData(d)&&(y(c).data(y(d).data()),la?(Rb=!0,la.cleanData([d])):delete y.cache[d[y.expando]]);d=1;for(e=b.length;d<e;d++)f=
|
|
||||||
b[d],y(f).remove(),a.appendChild(f),delete b[d];b[0]=c;b.length=1}function X(a,b){return P(function(){return a.apply(null,arguments)},a,b)}function Y(a,b,d,e,f,g){try{a(b,d,e,f,g)}catch(h){c(h,ua(d))}}function W(a,c,d,e,f,g){var h;m(e,function(e,g){var k=e.attrName,l=e.optional,s=e.mode,n,p,B,C;Xa.call(c,k)||(c[k]=t);switch(s){case "@":c[k]||l||(d[g]=t);c.$observe(k,function(a){d[g]=a});c.$$observers[k].$$scope=a;c[k]&&(d[g]=b(c[k])(a));break;case "=":if(l&&!c[k])break;p=u(c[k]);C=p.literal?ka:function(a,
|
|
||||||
b){return a===b||a!==a&&b!==b};B=p.assign||function(){n=d[g]=p(a);throw ea("nonassign",c[k],f.name);};n=d[g]=p(a);l=function(b){C(b,d[g])||(C(b,n)?B(a,b=d[g]):d[g]=b);return n=b};l.$stateful=!0;l=e.collection?a.$watchCollection(c[k],l):a.$watch(u(c[k],l),null,p.literal);h=h||[];h.push(l);break;case "&":p=u(c[k]);if(p===v&&l)break;d[g]=function(b){return p(a,b)}}});e=h?function(){for(var a=0,b=h.length;a<b;++a)h[a]()}:v;return g&&e!==v?(g.$on("$destroy",e),v):e}var aa=function(a,b){if(b){var c=Object.keys(b),
|
|
||||||
d,e,f;d=0;for(e=c.length;d<e;d++)f=c[d],this[f]=b[f]}else this.$attr={};this.$$element=a};aa.prototype={$normalize:wa,$addClass:function(a){a&&0<a.length&&B.addClass(this.$$element,a)},$removeClass:function(a){a&&0<a.length&&B.removeClass(this.$$element,a)},$updateClass:function(a,b){var c=bd(a,b);c&&c.length&&B.addClass(this.$$element,c);(c=bd(b,a))&&c.length&&B.removeClass(this.$$element,c)},$set:function(a,b,d,e){var f=this.$$element[0],g=Sc(f,a),h=Gf(f,a),f=a;g?(this.$$element.prop(a,b),e=g):
|
|
||||||
h&&(this[h]=b,f=h);this[a]=b;e?this.$attr[a]=e:(e=this.$attr[a])||(this.$attr[a]=e=Bc(a,"-"));g=ta(this.$$element);if("a"===g&&"href"===a||"img"===g&&"src"===a)this[a]=b=N(b,"src"===a);else if("img"===g&&"srcset"===a){for(var g="",h=R(b),k=/(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/,k=/\s/.test(h)?k:/(,)/,h=h.split(k),k=Math.floor(h.length/2),l=0;l<k;l++)var s=2*l,g=g+N(R(h[s]),!0),g=g+(" "+R(h[s+1]));h=R(h[2*l]).split(/\s/);g+=N(R(h[0]),!0);2===h.length&&(g+=" "+R(h[1]));this[a]=b=g}!1!==d&&(null===b||
|
|
||||||
b===t?this.$$element.removeAttr(e):this.$$element.attr(e,b));(a=this.$$observers)&&m(a[f],function(a){try{a(b)}catch(d){c(d)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers=ga()),e=d[a]||(d[a]=[]);e.push(b);K.$evalAsync(function(){!e.$$inter&&c.hasOwnProperty(a)&&b(c[a])});return function(){bb(e,b)}}};var ca=b.startSymbol(),da=b.endSymbol(),fa="{{"==ca||"}}"==da?Ya:function(a){return a.replace(/\{\{/g,ca).replace(/}}/g,da)},ia=/^ngAttr[A-Z]/;Z.$$addBindingInfo=n?function(a,b){var c=
|
|
||||||
a.data("$binding")||[];G(b)?c=c.concat(b):c.push(b);a.data("$binding",c)}:v;Z.$$addBindingClass=n?function(a){D(a,"ng-binding")}:v;Z.$$addScopeInfo=n?function(a,b,c,d){a.data(c?d?"$isolateScopeNoTemplate":"$isolateScope":"$scope",b)}:v;Z.$$addScopeClass=n?function(a,b){D(a,b?"ng-isolate-scope":"ng-scope")}:v;return Z}]}function wa(b){return hb(b.replace(Zc,""))}function bd(b,a){var c="",d=b.split(/\s+/),e=a.split(/\s+/),f=0;a:for(;f<d.length;f++){for(var g=d[f],h=0;h<e.length;h++)if(g==e[h])continue a;
|
|
||||||
c+=(0<c.length?" ":"")+g}return c}function $c(b){b=y(b);var a=b.length;if(1>=a)return b;for(;a--;)8===b[a].nodeType&&Nf.call(b,a,1);return b}function Xe(){var b={},a=!1;this.register=function(a,d){Ra(a,"controller");H(a)?P(b,a):b[a]=d};this.allowGlobals=function(){a=!0};this.$get=["$injector","$window",function(c,d){function e(a,b,c,d){if(!a||!H(a.$scope))throw J("$controller")("noscp",d,b);a.$scope[b]=c}return function(f,g,h,l){var k,n,r;h=!0===h;l&&L(l)&&(r=l);if(L(f)){l=f.match(Xc);if(!l)throw Of("ctrlfmt",
|
|
||||||
f);n=l[1];r=r||l[3];f=b.hasOwnProperty(n)?b[n]:Dc(g.$scope,n,!0)||(a?Dc(d,n,!0):t);Qa(f,n,!0)}if(h)return h=(G(f)?f[f.length-1]:f).prototype,k=Object.create(h||null),r&&e(g,r,k,n||f.name),P(function(){var a=c.invoke(f,k,g,n);a!==k&&(H(a)||z(a))&&(k=a,r&&e(g,r,k,n||f.name));return k},{instance:k,identifier:r});k=c.instantiate(f,g,n);r&&e(g,r,k,n||f.name);return k}}]}function Ye(){this.$get=["$window",function(b){return y(b.document)}]}function Ze(){this.$get=["$log",function(b){return function(a,c){b.error.apply(b,
|
|
||||||
arguments)}}]}function Zb(b){return H(b)?aa(b)?b.toISOString():db(b):b}function cf(){this.$get=function(){return function(b){if(!b)return"";var a=[];oc(b,function(b,d){null===b||A(b)||(G(b)?m(b,function(b,c){a.push(ma(d)+"="+ma(Zb(b)))}):a.push(ma(d)+"="+ma(Zb(b))))});return a.join("&")}}}function df(){this.$get=function(){return function(b){function a(b,e,f){null===b||A(b)||(G(b)?m(b,function(b){a(b,e+"[]")}):H(b)&&!aa(b)?oc(b,function(b,c){a(b,e+(f?"":"[")+c+(f?"":"]"))}):c.push(ma(e)+"="+ma(Zb(b))))}
|
|
||||||
if(!b)return"";var c=[];a(b,"",!0);return c.join("&")}}}function $b(b,a){if(L(b)){var c=b.replace(Pf,"").trim();if(c){var d=a("Content-Type");(d=d&&0===d.indexOf(cd))||(d=(d=c.match(Qf))&&Rf[d[0]].test(c));d&&(b=wc(c))}}return b}function dd(b){var a=ga(),c;L(b)?m(b.split("\n"),function(b){c=b.indexOf(":");var e=M(R(b.substr(0,c)));b=R(b.substr(c+1));e&&(a[e]=a[e]?a[e]+", "+b:b)}):H(b)&&m(b,function(b,c){var f=M(c),g=R(b);f&&(a[f]=a[f]?a[f]+", "+g:g)});return a}function ed(b){var a;return function(c){a||
|
|
||||||
(a=dd(b));return c?(c=a[M(c)],void 0===c&&(c=null),c):a}}function fd(b,a,c,d){if(z(d))return d(b,a,c);m(d,function(d){b=d(b,a,c)});return b}function bf(){var b=this.defaults={transformResponse:[$b],transformRequest:[function(a){return H(a)&&"[object File]"!==sa.call(a)&&"[object Blob]"!==sa.call(a)&&"[object FormData]"!==sa.call(a)?db(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},post:ia(ac),put:ia(ac),patch:ia(ac)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",paramSerializer:"$httpParamSerializer"},
|
|
||||||
a=!1;this.useApplyAsync=function(b){return w(b)?(a=!!b,this):a};var c=this.interceptors=[];this.$get=["$httpBackend","$$cookieReader","$cacheFactory","$rootScope","$q","$injector",function(d,e,f,g,h,l){function k(a){function c(a){var b=P({},a);b.data=a.data?fd(a.data,a.headers,a.status,e.transformResponse):a.data;a=a.status;return 200<=a&&300>a?b:h.reject(b)}function d(a,b){var c,e={};m(a,function(a,d){z(a)?(c=a(b),null!=c&&(e[d]=c)):e[d]=a});return e}if(!ca.isObject(a))throw J("$http")("badreq",
|
|
||||||
a);var e=P({method:"get",transformRequest:b.transformRequest,transformResponse:b.transformResponse,paramSerializer:b.paramSerializer},a);e.headers=function(a){var c=b.headers,e=P({},a.headers),f,g,h,c=P({},c.common,c[M(a.method)]);a:for(f in c){g=M(f);for(h in e)if(M(h)===g)continue a;e[f]=c[f]}return d(e,ia(a))}(a);e.method=rb(e.method);e.paramSerializer=L(e.paramSerializer)?l.get(e.paramSerializer):e.paramSerializer;var f=[function(a){var d=a.headers,e=fd(a.data,ed(d),t,a.transformRequest);A(e)&&
|
|
||||||
m(d,function(a,b){"content-type"===M(b)&&delete d[b]});A(a.withCredentials)&&!A(b.withCredentials)&&(a.withCredentials=b.withCredentials);return n(a,e).then(c,c)},t],g=h.when(e);for(m(x,function(a){(a.request||a.requestError)&&f.unshift(a.request,a.requestError);(a.response||a.responseError)&&f.push(a.response,a.responseError)});f.length;){a=f.shift();var k=f.shift(),g=g.then(a,k)}g.success=function(a){Qa(a,"fn");g.then(function(b){a(b.data,b.status,b.headers,e)});return g};g.error=function(a){Qa(a,
|
|
||||||
"fn");g.then(null,function(b){a(b.data,b.status,b.headers,e)});return g};return g}function n(c,f){function l(b,c,d,e){function f(){n(c,b,d,e)}N&&(200<=b&&300>b?N.put(S,[b,c,dd(d),e]):N.remove(S));a?g.$applyAsync(f):(f(),g.$$phase||g.$apply())}function n(a,b,d,e){b=Math.max(b,0);(200<=b&&300>b?I.resolve:I.reject)({data:a,status:b,headers:ed(d),config:c,statusText:e})}function x(a){n(a.data,a.status,ia(a.headers()),a.statusText)}function m(){var a=k.pendingRequests.indexOf(c);-1!==a&&k.pendingRequests.splice(a,
|
|
||||||
1)}var I=h.defer(),B=I.promise,N,D,q=c.headers,S=r(c.url,c.paramSerializer(c.params));k.pendingRequests.push(c);B.then(m,m);!c.cache&&!b.cache||!1===c.cache||"GET"!==c.method&&"JSONP"!==c.method||(N=H(c.cache)?c.cache:H(b.cache)?b.cache:s);N&&(D=N.get(S),w(D)?D&&z(D.then)?D.then(x,x):G(D)?n(D[1],D[0],ia(D[2]),D[3]):n(D,200,{},"OK"):N.put(S,B));A(D)&&((D=gd(c.url)?e()[c.xsrfCookieName||b.xsrfCookieName]:t)&&(q[c.xsrfHeaderName||b.xsrfHeaderName]=D),d(c.method,S,f,l,q,c.timeout,c.withCredentials,c.responseType));
|
|
||||||
return B}function r(a,b){0<b.length&&(a+=(-1==a.indexOf("?")?"?":"&")+b);return a}var s=f("$http");b.paramSerializer=L(b.paramSerializer)?l.get(b.paramSerializer):b.paramSerializer;var x=[];m(c,function(a){x.unshift(L(a)?l.get(a):l.invoke(a))});k.pendingRequests=[];(function(a){m(arguments,function(a){k[a]=function(b,c){return k(P({},c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){m(arguments,function(a){k[a]=function(b,c,d){return k(P({},d||{},{method:a,url:b,data:c}))}})})("post",
|
|
||||||
"put","patch");k.defaults=b;return k}]}function Sf(){return new O.XMLHttpRequest}function ef(){this.$get=["$browser","$window","$document",function(b,a,c){return Tf(b,Sf,b.defer,a.angular.callbacks,c[0])}]}function Tf(b,a,c,d,e){function f(a,b,c){var f=e.createElement("script"),n=null;f.type="text/javascript";f.src=a;f.async=!0;n=function(a){f.removeEventListener("load",n,!1);f.removeEventListener("error",n,!1);e.body.removeChild(f);f=null;var g=-1,x="unknown";a&&("load"!==a.type||d[b].called||(a=
|
|
||||||
{type:"error"}),x=a.type,g="error"===a.type?404:200);c&&c(g,x)};f.addEventListener("load",n,!1);f.addEventListener("error",n,!1);e.body.appendChild(f);return n}return function(e,h,l,k,n,r,s,x){function C(){p&&p();K&&K.abort()}function F(a,d,e,f,g){I!==t&&c.cancel(I);p=K=null;a(d,e,f,g);b.$$completeOutstandingRequest(v)}b.$$incOutstandingRequestCount();h=h||b.url();if("jsonp"==M(e)){var u="_"+(d.counter++).toString(36);d[u]=function(a){d[u].data=a;d[u].called=!0};var p=f(h.replace("JSON_CALLBACK",
|
|
||||||
"angular.callbacks."+u),u,function(a,b){F(k,a,d[u].data,"",b);d[u]=v})}else{var K=a();K.open(e,h,!0);m(n,function(a,b){w(a)&&K.setRequestHeader(b,a)});K.onload=function(){var a=K.statusText||"",b="response"in K?K.response:K.responseText,c=1223===K.status?204:K.status;0===c&&(c=b?200:"file"==Ba(h).protocol?404:0);F(k,c,b,K.getAllResponseHeaders(),a)};e=function(){F(k,-1,null,null,"")};K.onerror=e;K.onabort=e;s&&(K.withCredentials=!0);if(x)try{K.responseType=x}catch(q){if("json"!==x)throw q;}K.send(l)}if(0<
|
|
||||||
r)var I=c(C,r);else r&&z(r.then)&&r.then(C)}}function $e(){var b="{{",a="}}";this.startSymbol=function(a){return a?(b=a,this):b};this.endSymbol=function(b){return b?(a=b,this):a};this.$get=["$parse","$exceptionHandler","$sce",function(c,d,e){function f(a){return"\\\\\\"+a}function g(c){return c.replace(n,b).replace(r,a)}function h(f,h,n,r){function u(a){try{var b=a;a=n?e.getTrusted(n,b):e.valueOf(b);var c;if(r&&!w(a))c=a;else if(null==a)c="";else{switch(typeof a){case "string":break;case "number":a=
|
|
||||||
""+a;break;default:a=db(a)}c=a}return c}catch(g){d(Ka.interr(f,g))}}r=!!r;for(var p,m,q=0,I=[],B=[],N=f.length,D=[],t=[];q<N;)if(-1!=(p=f.indexOf(b,q))&&-1!=(m=f.indexOf(a,p+l)))q!==p&&D.push(g(f.substring(q,p))),q=f.substring(p+l,m),I.push(q),B.push(c(q,u)),q=m+k,t.push(D.length),D.push("");else{q!==N&&D.push(g(f.substring(q)));break}n&&1<D.length&&Ka.throwNoconcat(f);if(!h||I.length){var S=function(a){for(var b=0,c=I.length;b<c;b++){if(r&&A(a[b]))return;D[t[b]]=a[b]}return D.join("")};return P(function(a){var b=
|
|
||||||
0,c=I.length,e=Array(c);try{for(;b<c;b++)e[b]=B[b](a);return S(e)}catch(g){d(Ka.interr(f,g))}},{exp:f,expressions:I,$$watchDelegate:function(a,b){var c;return a.$watchGroup(B,function(d,e){var f=S(d);z(b)&&b.call(this,f,d!==e?c:f,a);c=f})}})}}var l=b.length,k=a.length,n=new RegExp(b.replace(/./g,f),"g"),r=new RegExp(a.replace(/./g,f),"g");h.startSymbol=function(){return b};h.endSymbol=function(){return a};return h}]}function af(){this.$get=["$rootScope","$window","$q","$$q",function(b,a,c,d){function e(e,
|
|
||||||
h,l,k){var n=4<arguments.length,r=n?za.call(arguments,4):[],s=a.setInterval,x=a.clearInterval,C=0,F=w(k)&&!k,u=(F?d:c).defer(),p=u.promise;l=w(l)?l:0;p.then(null,null,n?function(){e.apply(null,r)}:e);p.$$intervalId=s(function(){u.notify(C++);0<l&&C>=l&&(u.resolve(C),x(p.$$intervalId),delete f[p.$$intervalId]);F||b.$apply()},h);f[p.$$intervalId]=u;return p}var f={};e.cancel=function(b){return b&&b.$$intervalId in f?(f[b.$$intervalId].reject("canceled"),a.clearInterval(b.$$intervalId),delete f[b.$$intervalId],
|
|
||||||
!0):!1};return e}]}function ge(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0,maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",posSuf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},DATETIME_FORMATS:{MONTH:"January February March April May June July August September October November December".split(" "),SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),
|
|
||||||
DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss a",shortTime:"h:mm a",ERANAMES:["Before Christ","Anno Domini"],ERAS:["BC","AD"]},pluralCat:function(b){return 1===b?"one":"other"}}}}function bc(b){b=b.split("/");for(var a=b.length;a--;)b[a]=ob(b[a]);
|
|
||||||
return b.join("/")}function hd(b,a){var c=Ba(b);a.$$protocol=c.protocol;a.$$host=c.hostname;a.$$port=W(c.port)||Uf[c.protocol]||null}function id(b,a){var c="/"!==b.charAt(0);c&&(b="/"+b);var d=Ba(b);a.$$path=decodeURIComponent(c&&"/"===d.pathname.charAt(0)?d.pathname.substring(1):d.pathname);a.$$search=zc(d.search);a.$$hash=decodeURIComponent(d.hash);a.$$path&&"/"!=a.$$path.charAt(0)&&(a.$$path="/"+a.$$path)}function ya(b,a){if(0===a.indexOf(b))return a.substr(b.length)}function Ja(b){var a=b.indexOf("#");
|
|
||||||
return-1==a?b:b.substr(0,a)}function Bb(b){return b.replace(/(#.+)|#$/,"$1")}function cc(b){return b.substr(0,Ja(b).lastIndexOf("/")+1)}function dc(b,a){this.$$html5=!0;a=a||"";var c=cc(b);hd(b,this);this.$$parse=function(a){var b=ya(c,a);if(!L(b))throw Cb("ipthprfx",a,c);id(b,this);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=Qb(this.$$search),b=this.$$hash?"#"+ob(this.$$hash):"";this.$$url=bc(this.$$path)+(a?"?"+a:"")+b;this.$$absUrl=c+this.$$url.substr(1)};this.$$parseLinkUrl=
|
|
||||||
function(d,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;(f=ya(b,d))!==t?(g=f,g=(f=ya(a,f))!==t?c+(ya("/",f)||f):b+g):(f=ya(c,d))!==t?g=c+f:c==d+"/"&&(g=c);g&&this.$$parse(g);return!!g}}function ec(b,a){var c=cc(b);hd(b,this);this.$$parse=function(d){var e=ya(b,d)||ya(c,d),f;A(e)||"#"!==e.charAt(0)?this.$$html5?f=e:(f="",A(e)&&(b=d,this.replace())):(f=ya(a,e),A(f)&&(f=e));id(f,this);d=this.$$path;var e=b,g=/^\/[A-Z]:(\/.*)/;0===f.indexOf(e)&&(f=f.replace(e,""));g.exec(f)||(d=(f=g.exec(d))?
|
|
||||||
f[1]:d);this.$$path=d;this.$$compose()};this.$$compose=function(){var c=Qb(this.$$search),e=this.$$hash?"#"+ob(this.$$hash):"";this.$$url=bc(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=b+(this.$$url?a+this.$$url:"")};this.$$parseLinkUrl=function(a,c){return Ja(b)==Ja(a)?(this.$$parse(a),!0):!1}}function jd(b,a){this.$$html5=!0;ec.apply(this,arguments);var c=cc(b);this.$$parseLinkUrl=function(d,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;b==Ja(d)?f=d:(g=ya(c,d))?f=b+a+g:c===d+"/"&&(f=
|
|
||||||
c);f&&this.$$parse(f);return!!f};this.$$compose=function(){var c=Qb(this.$$search),e=this.$$hash?"#"+ob(this.$$hash):"";this.$$url=bc(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=b+a+this.$$url}}function Db(b){return function(){return this[b]}}function kd(b,a){return function(c){if(A(c))return this[b];this[b]=a(c);this.$$compose();return this}}function ff(){var b="",a={enabled:!1,requireBase:!0,rewriteLinks:!0};this.hashPrefix=function(a){return w(a)?(b=a,this):b};this.html5Mode=function(b){return ab(b)?
|
|
||||||
(a.enabled=b,this):H(b)?(ab(b.enabled)&&(a.enabled=b.enabled),ab(b.requireBase)&&(a.requireBase=b.requireBase),ab(b.rewriteLinks)&&(a.rewriteLinks=b.rewriteLinks),this):a};this.$get=["$rootScope","$browser","$sniffer","$rootElement","$window",function(c,d,e,f,g){function h(a,b,c){var e=k.url(),f=k.$$state;try{d.url(a,b,c),k.$$state=d.state()}catch(g){throw k.url(e),k.$$state=f,g;}}function l(a,b){c.$broadcast("$locationChangeSuccess",k.absUrl(),a,k.$$state,b)}var k,n;n=d.baseHref();var r=d.url(),
|
|
||||||
s;if(a.enabled){if(!n&&a.requireBase)throw Cb("nobase");s=r.substring(0,r.indexOf("/",r.indexOf("//")+2))+(n||"/");n=e.history?dc:jd}else s=Ja(r),n=ec;k=new n(s,"#"+b);k.$$parseLinkUrl(r,r);k.$$state=d.state();var x=/^\s*(javascript|mailto):/i;f.on("click",function(b){if(a.rewriteLinks&&!b.ctrlKey&&!b.metaKey&&!b.shiftKey&&2!=b.which&&2!=b.button){for(var e=y(b.target);"a"!==ta(e[0]);)if(e[0]===f[0]||!(e=e.parent())[0])return;var h=e.prop("href"),l=e.attr("href")||e.attr("xlink:href");H(h)&&"[object SVGAnimatedString]"===
|
|
||||||
h.toString()&&(h=Ba(h.animVal).href);x.test(h)||!h||e.attr("target")||b.isDefaultPrevented()||!k.$$parseLinkUrl(h,l)||(b.preventDefault(),k.absUrl()!=d.url()&&(c.$apply(),g.angular["ff-684208-preventDefault"]=!0))}});Bb(k.absUrl())!=Bb(r)&&d.url(k.absUrl(),!0);var C=!0;d.onUrlChange(function(a,b){c.$evalAsync(function(){var d=k.absUrl(),e=k.$$state,f;k.$$parse(a);k.$$state=b;f=c.$broadcast("$locationChangeStart",a,d,b,e).defaultPrevented;k.absUrl()===a&&(f?(k.$$parse(d),k.$$state=e,h(d,!1,e)):(C=
|
|
||||||
!1,l(d,e)))});c.$$phase||c.$digest()});c.$watch(function(){var a=Bb(d.url()),b=Bb(k.absUrl()),f=d.state(),g=k.$$replace,n=a!==b||k.$$html5&&e.history&&f!==k.$$state;if(C||n)C=!1,c.$evalAsync(function(){var b=k.absUrl(),d=c.$broadcast("$locationChangeStart",b,a,k.$$state,f).defaultPrevented;k.absUrl()===b&&(d?(k.$$parse(a),k.$$state=f):(n&&h(b,g,f===k.$$state?null:k.$$state),l(a,f)))});k.$$replace=!1});return k}]}function gf(){var b=!0,a=this;this.debugEnabled=function(a){return w(a)?(b=a,this):b};
|
|
||||||
this.$get=["$window",function(c){function d(a){a instanceof Error&&(a.stack?a=a.message&&-1===a.stack.indexOf(a.message)?"Error: "+a.message+"\n"+a.stack:a.stack:a.sourceURL&&(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function e(a){var b=c.console||{},e=b[a]||b.log||v;a=!1;try{a=!!e.apply}catch(l){}return a?function(){var a=[];m(arguments,function(b){a.push(d(b))});return e.apply(b,a)}:function(a,b){e(a,null==b?"":b)}}return{log:e("log"),info:e("info"),warn:e("warn"),error:e("error"),debug:function(){var c=
|
|
||||||
e("debug");return function(){b&&c.apply(a,arguments)}}()}}]}function Ca(b,a){if("__defineGetter__"===b||"__defineSetter__"===b||"__lookupGetter__"===b||"__lookupSetter__"===b||"__proto__"===b)throw da("isecfld",a);return b}function oa(b,a){if(b){if(b.constructor===b)throw da("isecfn",a);if(b.window===b)throw da("isecwindow",a);if(b.children&&(b.nodeName||b.prop&&b.attr&&b.find))throw da("isecdom",a);if(b===Object)throw da("isecobj",a);}return b}function ld(b,a){if(b){if(b.constructor===b)throw da("isecfn",
|
|
||||||
a);if(b===Vf||b===Wf||b===Xf)throw da("isecff",a);}}function Yf(b,a){return"undefined"!==typeof b?b:a}function md(b,a){return"undefined"===typeof b?a:"undefined"===typeof a?b:b+a}function T(b,a){var c,d;switch(b.type){case q.Program:c=!0;m(b.body,function(b){T(b.expression,a);c=c&&b.expression.constant});b.constant=c;break;case q.Literal:b.constant=!0;b.toWatch=[];break;case q.UnaryExpression:T(b.argument,a);b.constant=b.argument.constant;b.toWatch=b.argument.toWatch;break;case q.BinaryExpression:T(b.left,
|
|
||||||
a);T(b.right,a);b.constant=b.left.constant&&b.right.constant;b.toWatch=b.left.toWatch.concat(b.right.toWatch);break;case q.LogicalExpression:T(b.left,a);T(b.right,a);b.constant=b.left.constant&&b.right.constant;b.toWatch=b.constant?[]:[b];break;case q.ConditionalExpression:T(b.test,a);T(b.alternate,a);T(b.consequent,a);b.constant=b.test.constant&&b.alternate.constant&&b.consequent.constant;b.toWatch=b.constant?[]:[b];break;case q.Identifier:b.constant=!1;b.toWatch=[b];break;case q.MemberExpression:T(b.object,
|
|
||||||
a);b.computed&&T(b.property,a);b.constant=b.object.constant&&(!b.computed||b.property.constant);b.toWatch=[b];break;case q.CallExpression:c=b.filter?!a(b.callee.name).$stateful:!1;d=[];m(b.arguments,function(b){T(b,a);c=c&&b.constant;b.constant||d.push.apply(d,b.toWatch)});b.constant=c;b.toWatch=b.filter&&!a(b.callee.name).$stateful?d:[b];break;case q.AssignmentExpression:T(b.left,a);T(b.right,a);b.constant=b.left.constant&&b.right.constant;b.toWatch=[b];break;case q.ArrayExpression:c=!0;d=[];m(b.elements,
|
|
||||||
function(b){T(b,a);c=c&&b.constant;b.constant||d.push.apply(d,b.toWatch)});b.constant=c;b.toWatch=d;break;case q.ObjectExpression:c=!0;d=[];m(b.properties,function(b){T(b.value,a);c=c&&b.value.constant;b.value.constant||d.push.apply(d,b.value.toWatch)});b.constant=c;b.toWatch=d;break;case q.ThisExpression:b.constant=!1,b.toWatch=[]}}function nd(b){if(1==b.length){b=b[0].expression;var a=b.toWatch;return 1!==a.length?a:a[0]!==b?a:t}}function od(b){return b.type===q.Identifier||b.type===q.MemberExpression}
|
|
||||||
function pd(b){if(1===b.body.length&&od(b.body[0].expression))return{type:q.AssignmentExpression,left:b.body[0].expression,right:{type:q.NGValueParameter},operator:"="}}function qd(b){return 0===b.body.length||1===b.body.length&&(b.body[0].expression.type===q.Literal||b.body[0].expression.type===q.ArrayExpression||b.body[0].expression.type===q.ObjectExpression)}function rd(b,a){this.astBuilder=b;this.$filter=a}function sd(b,a){this.astBuilder=b;this.$filter=a}function Eb(b,a,c,d){oa(b,d);a=a.split(".");
|
|
||||||
for(var e,f=0;1<a.length;f++){e=Ca(a.shift(),d);var g=oa(b[e],d);g||(g={},b[e]=g);b=g}e=Ca(a.shift(),d);oa(b[e],d);return b[e]=c}function Fb(b){return"constructor"==b}function fc(b){return z(b.valueOf)?b.valueOf():Zf.call(b)}function hf(){var b=ga(),a=ga();this.$get=["$filter","$sniffer",function(c,d){function e(a,b){return null==a||null==b?a===b:"object"===typeof a&&(a=fc(a),"object"===typeof a)?!1:a===b||a!==a&&b!==b}function f(a,b,c,d,f){var g=d.inputs,h;if(1===g.length){var k=e,g=g[0];return a.$watch(function(a){var b=
|
|
||||||
g(a);e(b,k)||(h=d(a,t,t,[b]),k=b&&fc(b));return h},b,c,f)}for(var l=[],n=[],r=0,m=g.length;r<m;r++)l[r]=e,n[r]=null;return a.$watch(function(a){for(var b=!1,c=0,f=g.length;c<f;c++){var k=g[c](a);if(b||(b=!e(k,l[c])))n[c]=k,l[c]=k&&fc(k)}b&&(h=d(a,t,t,n));return h},b,c,f)}function g(a,b,c,d){var e,f;return e=a.$watch(function(a){return d(a)},function(a,c,d){f=a;z(b)&&b.apply(this,arguments);w(a)&&d.$$postDigest(function(){w(f)&&e()})},c)}function h(a,b,c,d){function e(a){var b=!0;m(a,function(a){w(a)||
|
|
||||||
(b=!1)});return b}var f,g;return f=a.$watch(function(a){return d(a)},function(a,c,d){g=a;z(b)&&b.call(this,a,c,d);e(a)&&d.$$postDigest(function(){e(g)&&f()})},c)}function l(a,b,c,d){var e;return e=a.$watch(function(a){return d(a)},function(a,c,d){z(b)&&b.apply(this,arguments);e()},c)}function k(a,b){if(!b)return a;var c=a.$$watchDelegate,c=c!==h&&c!==g?function(c,d,e,f){e=a(c,d,e,f);return b(e,c,d)}:function(c,d,e,f){e=a(c,d,e,f);c=b(e,c,d);return w(e)?c:e};a.$$watchDelegate&&a.$$watchDelegate!==
|
|
||||||
f?c.$$watchDelegate=a.$$watchDelegate:b.$stateful||(c.$$watchDelegate=f,c.inputs=a.inputs?a.inputs:[a]);return c}var n={csp:d.csp,expensiveChecks:!1},r={csp:d.csp,expensiveChecks:!0};return function(d,e,C){var m,u,p;switch(typeof d){case "string":p=d=d.trim();var q=C?a:b;m=q[p];m||(":"===d.charAt(0)&&":"===d.charAt(1)&&(u=!0,d=d.substring(2)),C=C?r:n,m=new gc(C),m=(new hc(m,c,C)).parse(d),m.constant?m.$$watchDelegate=l:u?m.$$watchDelegate=m.literal?h:g:m.inputs&&(m.$$watchDelegate=f),q[p]=m);return k(m,
|
|
||||||
e);case "function":return k(d,e);default:return v}}}]}function kf(){this.$get=["$rootScope","$exceptionHandler",function(b,a){return td(function(a){b.$evalAsync(a)},a)}]}function lf(){this.$get=["$browser","$exceptionHandler",function(b,a){return td(function(a){b.defer(a)},a)}]}function td(b,a){function c(a,b,c){function d(b){return function(c){e||(e=!0,b.call(a,c))}}var e=!1;return[d(b),d(c)]}function d(){this.$$state={status:0}}function e(a,b){return function(c){b.call(a,c)}}function f(c){!c.processScheduled&&
|
|
||||||
c.pending&&(c.processScheduled=!0,b(function(){var b,d,e;e=c.pending;c.processScheduled=!1;c.pending=t;for(var f=0,g=e.length;f<g;++f){d=e[f][0];b=e[f][c.status];try{z(b)?d.resolve(b(c.value)):1===c.status?d.resolve(c.value):d.reject(c.value)}catch(h){d.reject(h),a(h)}}}))}function g(){this.promise=new d;this.resolve=e(this,this.resolve);this.reject=e(this,this.reject);this.notify=e(this,this.notify)}var h=J("$q",TypeError);d.prototype={then:function(a,b,c){var d=new g;this.$$state.pending=this.$$state.pending||
|
|
||||||
[];this.$$state.pending.push([d,a,b,c]);0<this.$$state.status&&f(this.$$state);return d.promise},"catch":function(a){return this.then(null,a)},"finally":function(a,b){return this.then(function(b){return k(b,!0,a)},function(b){return k(b,!1,a)},b)}};g.prototype={resolve:function(a){this.promise.$$state.status||(a===this.promise?this.$$reject(h("qcycle",a)):this.$$resolve(a))},$$resolve:function(b){var d,e;e=c(this,this.$$resolve,this.$$reject);try{if(H(b)||z(b))d=b&&b.then;z(d)?(this.promise.$$state.status=
|
|
||||||
-1,d.call(b,e[0],e[1],this.notify)):(this.promise.$$state.value=b,this.promise.$$state.status=1,f(this.promise.$$state))}catch(g){e[1](g),a(g)}},reject:function(a){this.promise.$$state.status||this.$$reject(a)},$$reject:function(a){this.promise.$$state.value=a;this.promise.$$state.status=2;f(this.promise.$$state)},notify:function(c){var d=this.promise.$$state.pending;0>=this.promise.$$state.status&&d&&d.length&&b(function(){for(var b,e,f=0,g=d.length;f<g;f++){e=d[f][0];b=d[f][3];try{e.notify(z(b)?
|
|
||||||
b(c):c)}catch(h){a(h)}}})}};var l=function(a,b){var c=new g;b?c.resolve(a):c.reject(a);return c.promise},k=function(a,b,c){var d=null;try{z(c)&&(d=c())}catch(e){return l(e,!1)}return d&&z(d.then)?d.then(function(){return l(a,b)},function(a){return l(a,!1)}):l(a,b)},n=function(a,b,c,d){var e=new g;e.resolve(a);return e.promise.then(b,c,d)},r=function x(a){if(!z(a))throw h("norslvr",a);if(!(this instanceof x))return new x(a);var b=new g;a(function(a){b.resolve(a)},function(a){b.reject(a)});return b.promise};
|
|
||||||
r.defer=function(){return new g};r.reject=function(a){var b=new g;b.reject(a);return b.promise};r.when=n;r.resolve=n;r.all=function(a){var b=new g,c=0,d=G(a)?[]:{};m(a,function(a,e){c++;n(a).then(function(a){d.hasOwnProperty(e)||(d[e]=a,--c||b.resolve(d))},function(a){d.hasOwnProperty(e)||b.reject(a)})});0===c&&b.resolve(d);return b.promise};return r}function uf(){this.$get=["$window","$timeout",function(b,a){function c(){for(var a=0;a<n.length;a++){var b=n[a];b&&(n[a]=null,b())}k=n.length=0}function d(a){var b=
|
|
||||||
n.length;k++;n.push(a);0===b&&(l=h(c));return function(){0<=b&&(b=n[b]=null,0===--k&&l&&(l(),l=null,n.length=0))}}var e=b.requestAnimationFrame||b.webkitRequestAnimationFrame,f=b.cancelAnimationFrame||b.webkitCancelAnimationFrame||b.webkitCancelRequestAnimationFrame,g=!!e,h=g?function(a){var b=e(a);return function(){f(b)}}:function(b){var c=a(b,16.66,!1);return function(){a.cancel(c)}};d.supported=g;var l,k=0,n=[];return d}]}function jf(){function b(a){function b(){this.$$watchers=this.$$nextSibling=
|
|
||||||
this.$$childHead=this.$$childTail=null;this.$$listeners={};this.$$listenerCount={};this.$$watchersCount=0;this.$id=++nb;this.$$ChildScope=null}b.prototype=a;return b}var a=10,c=J("$rootScope"),d=null,e=null;this.digestTtl=function(b){arguments.length&&(a=b);return a};this.$get=["$injector","$exceptionHandler","$parse","$browser",function(f,g,h,l){function k(a){a.currentScope.$$destroyed=!0}function n(){this.$id=++nb;this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=this.$$prevSibling=this.$$childHead=
|
|
||||||
this.$$childTail=null;this.$root=this;this.$$destroyed=!1;this.$$listeners={};this.$$listenerCount={};this.$$watchersCount=0;this.$$isolateBindings=null}function r(a){if(p.$$phase)throw c("inprog",p.$$phase);p.$$phase=a}function s(a,b){do a.$$watchersCount+=b;while(a=a.$parent)}function x(a,b,c){do a.$$listenerCount[c]-=b,0===a.$$listenerCount[c]&&delete a.$$listenerCount[c];while(a=a.$parent)}function q(){}function F(){for(;I.length;)try{I.shift()()}catch(a){g(a)}e=null}function u(){null===e&&(e=
|
|
||||||
l.defer(function(){p.$apply(F)}))}n.prototype={constructor:n,$new:function(a,c){var d;c=c||this;a?(d=new n,d.$root=this.$root):(this.$$ChildScope||(this.$$ChildScope=b(this)),d=new this.$$ChildScope);d.$parent=c;d.$$prevSibling=c.$$childTail;c.$$childHead?(c.$$childTail.$$nextSibling=d,c.$$childTail=d):c.$$childHead=c.$$childTail=d;(a||c!=this)&&d.$on("$destroy",k);return d},$watch:function(a,b,c,e){var f=h(a);if(f.$$watchDelegate)return f.$$watchDelegate(this,b,c,f,a);var g=this,k=g.$$watchers,l=
|
|
||||||
{fn:b,last:q,get:f,exp:e||a,eq:!!c};d=null;z(b)||(l.fn=v);k||(k=g.$$watchers=[]);k.unshift(l);s(this,1);return function(){0<=bb(k,l)&&s(g,-1);d=null}},$watchGroup:function(a,b){function c(){h=!1;k?(k=!1,b(e,e,g)):b(e,d,g)}var d=Array(a.length),e=Array(a.length),f=[],g=this,h=!1,k=!0;if(!a.length){var l=!0;g.$evalAsync(function(){l&&b(e,e,g)});return function(){l=!1}}if(1===a.length)return this.$watch(a[0],function(a,c,f){e[0]=a;d[0]=c;b(e,a===c?e:d,f)});m(a,function(a,b){var k=g.$watch(a,function(a,
|
|
||||||
f){e[b]=a;d[b]=f;h||(h=!0,g.$evalAsync(c))});f.push(k)});return function(){for(;f.length;)f.shift()()}},$watchCollection:function(a,b){function c(a){e=a;var b,d,g,h;if(!A(e)){if(H(e))if(Ea(e))for(f!==r&&(f=r,m=f.length=0,l++),a=e.length,m!==a&&(l++,f.length=m=a),b=0;b<a;b++)h=f[b],g=e[b],d=h!==h&&g!==g,d||h===g||(l++,f[b]=g);else{f!==s&&(f=s={},m=0,l++);a=0;for(b in e)e.hasOwnProperty(b)&&(a++,g=e[b],h=f[b],b in f?(d=h!==h&&g!==g,d||h===g||(l++,f[b]=g)):(m++,f[b]=g,l++));if(m>a)for(b in l++,f)e.hasOwnProperty(b)||
|
|
||||||
(m--,delete f[b])}else f!==e&&(f=e,l++);return l}}c.$stateful=!0;var d=this,e,f,g,k=1<b.length,l=0,n=h(a,c),r=[],s={},p=!0,m=0;return this.$watch(n,function(){p?(p=!1,b(e,e,d)):b(e,g,d);if(k)if(H(e))if(Ea(e)){g=Array(e.length);for(var a=0;a<e.length;a++)g[a]=e[a]}else for(a in g={},e)Xa.call(e,a)&&(g[a]=e[a]);else g=e})},$digest:function(){var b,f,h,k,n,s,m=a,x,u=[],E,I;r("$digest");l.$$checkUrlChange();this===p&&null!==e&&(l.defer.cancel(e),F());d=null;do{s=!1;for(x=this;t.length;){try{I=t.shift(),
|
|
||||||
I.scope.$eval(I.expression,I.locals)}catch(v){g(v)}d=null}a:do{if(k=x.$$watchers)for(n=k.length;n--;)try{if(b=k[n])if((f=b.get(x))!==(h=b.last)&&!(b.eq?ka(f,h):"number"===typeof f&&"number"===typeof h&&isNaN(f)&&isNaN(h)))s=!0,d=b,b.last=b.eq?fa(f,null):f,b.fn(f,h===q?f:h,x),5>m&&(E=4-m,u[E]||(u[E]=[]),u[E].push({msg:z(b.exp)?"fn: "+(b.exp.name||b.exp.toString()):b.exp,newVal:f,oldVal:h}));else if(b===d){s=!1;break a}}catch(A){g(A)}if(!(k=x.$$watchersCount&&x.$$childHead||x!==this&&x.$$nextSibling))for(;x!==
|
|
||||||
this&&!(k=x.$$nextSibling);)x=x.$parent}while(x=k);if((s||t.length)&&!m--)throw p.$$phase=null,c("infdig",a,u);}while(s||t.length);for(p.$$phase=null;w.length;)try{w.shift()()}catch(y){g(y)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;this===p&&l.$$applicationDestroyed();s(this,-this.$$watchersCount);for(var b in this.$$listenerCount)x(this,this.$$listenerCount[b],b);a&&a.$$childHead==this&&(a.$$childHead=this.$$nextSibling);a&&a.$$childTail==
|
|
||||||
this&&(a.$$childTail=this.$$prevSibling);this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=this.$$nextSibling);this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling);this.$destroy=this.$digest=this.$apply=this.$evalAsync=this.$applyAsync=v;this.$on=this.$watch=this.$watchGroup=function(){return v};this.$$listeners={};this.$parent=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=this.$root=this.$$watchers=null}},$eval:function(a,b){return h(a)(this,b)},
|
|
||||||
$evalAsync:function(a,b){p.$$phase||t.length||l.defer(function(){t.length&&p.$digest()});t.push({scope:this,expression:a,locals:b})},$$postDigest:function(a){w.push(a)},$apply:function(a){try{return r("$apply"),this.$eval(a)}catch(b){g(b)}finally{p.$$phase=null;try{p.$digest()}catch(c){throw g(c),c;}}},$applyAsync:function(a){function b(){c.$eval(a)}var c=this;a&&I.push(b);u()},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);var d=this;do d.$$listenerCount[a]||
|
|
||||||
(d.$$listenerCount[a]=0),d.$$listenerCount[a]++;while(d=d.$parent);var e=this;return function(){var d=c.indexOf(b);-1!==d&&(c[d]=null,x(e,1,a))}},$emit:function(a,b){var c=[],d,e=this,f=!1,h={name:a,targetScope:e,stopPropagation:function(){f=!0},preventDefault:function(){h.defaultPrevented=!0},defaultPrevented:!1},k=cb([h],arguments,1),l,n;do{d=e.$$listeners[a]||c;h.currentScope=e;l=0;for(n=d.length;l<n;l++)if(d[l])try{d[l].apply(null,k)}catch(r){g(r)}else d.splice(l,1),l--,n--;if(f)return h.currentScope=
|
|
||||||
null,h;e=e.$parent}while(e);h.currentScope=null;return h},$broadcast:function(a,b){var c=this,d=this,e={name:a,targetScope:this,preventDefault:function(){e.defaultPrevented=!0},defaultPrevented:!1};if(!this.$$listenerCount[a])return e;for(var f=cb([e],arguments,1),h,k;c=d;){e.currentScope=c;d=c.$$listeners[a]||[];h=0;for(k=d.length;h<k;h++)if(d[h])try{d[h].apply(null,f)}catch(l){g(l)}else d.splice(h,1),h--,k--;if(!(d=c.$$listenerCount[a]&&c.$$childHead||c!==this&&c.$$nextSibling))for(;c!==this&&!(d=
|
|
||||||
c.$$nextSibling);)c=c.$parent}e.currentScope=null;return e}};var p=new n,t=p.$$asyncQueue=[],w=p.$$postDigestQueue=[],I=p.$$applyAsyncQueue=[];return p}]}function he(){var b=/^\s*(https?|ftp|mailto|tel|file):/,a=/^\s*((https?|ftp|file|blob):|data:image\/)/;this.aHrefSanitizationWhitelist=function(a){return w(a)?(b=a,this):b};this.imgSrcSanitizationWhitelist=function(b){return w(b)?(a=b,this):a};this.$get=function(){return function(c,d){var e=d?a:b,f;f=Ba(c).href;return""===f||f.match(e)?c:"unsafe:"+
|
|
||||||
f}}}function $f(b){if("self"===b)return b;if(L(b)){if(-1<b.indexOf("***"))throw Da("iwcard",b);b=ud(b).replace("\\*\\*",".*").replace("\\*","[^:/.?&;]*");return new RegExp("^"+b+"$")}if(Za(b))return new RegExp("^"+b.source+"$");throw Da("imatcher");}function vd(b){var a=[];w(b)&&m(b,function(b){a.push($f(b))});return a}function nf(){this.SCE_CONTEXTS=pa;var b=["self"],a=[];this.resourceUrlWhitelist=function(a){arguments.length&&(b=vd(a));return b};this.resourceUrlBlacklist=function(b){arguments.length&&
|
|
||||||
(a=vd(b));return a};this.$get=["$injector",function(c){function d(a,b){return"self"===a?gd(b):!!a.exec(b.href)}function e(a){var b=function(a){this.$$unwrapTrustedValue=function(){return a}};a&&(b.prototype=new a);b.prototype.valueOf=function(){return this.$$unwrapTrustedValue()};b.prototype.toString=function(){return this.$$unwrapTrustedValue().toString()};return b}var f=function(a){throw Da("unsafe");};c.has("$sanitize")&&(f=c.get("$sanitize"));var g=e(),h={};h[pa.HTML]=e(g);h[pa.CSS]=e(g);h[pa.URL]=
|
|
||||||
e(g);h[pa.JS]=e(g);h[pa.RESOURCE_URL]=e(h[pa.URL]);return{trustAs:function(a,b){var c=h.hasOwnProperty(a)?h[a]:null;if(!c)throw Da("icontext",a,b);if(null===b||b===t||""===b)return b;if("string"!==typeof b)throw Da("itype",a);return new c(b)},getTrusted:function(c,e){if(null===e||e===t||""===e)return e;var g=h.hasOwnProperty(c)?h[c]:null;if(g&&e instanceof g)return e.$$unwrapTrustedValue();if(c===pa.RESOURCE_URL){var g=Ba(e.toString()),r,s,m=!1;r=0;for(s=b.length;r<s;r++)if(d(b[r],g)){m=!0;break}if(m)for(r=
|
|
||||||
0,s=a.length;r<s;r++)if(d(a[r],g)){m=!1;break}if(m)return e;throw Da("insecurl",e.toString());}if(c===pa.HTML)return f(e);throw Da("unsafe");},valueOf:function(a){return a instanceof g?a.$$unwrapTrustedValue():a}}}]}function mf(){var b=!0;this.enabled=function(a){arguments.length&&(b=!!a);return b};this.$get=["$parse","$sceDelegate",function(a,c){if(b&&8>Ua)throw Da("iequirks");var d=ia(pa);d.isEnabled=function(){return b};d.trustAs=c.trustAs;d.getTrusted=c.getTrusted;d.valueOf=c.valueOf;b||(d.trustAs=
|
|
||||||
d.getTrusted=function(a,b){return b},d.valueOf=Ya);d.parseAs=function(b,c){var e=a(c);return e.literal&&e.constant?e:a(c,function(a){return d.getTrusted(b,a)})};var e=d.parseAs,f=d.getTrusted,g=d.trustAs;m(pa,function(a,b){var c=M(b);d[hb("parse_as_"+c)]=function(b){return e(a,b)};d[hb("get_trusted_"+c)]=function(b){return f(a,b)};d[hb("trust_as_"+c)]=function(b){return g(a,b)}});return d}]}function of(){this.$get=["$window","$document",function(b,a){var c={},d=W((/android (\d+)/.exec(M((b.navigator||
|
|
||||||
{}).userAgent))||[])[1]),e=/Boxee/i.test((b.navigator||{}).userAgent),f=a[0]||{},g,h=/^(Moz|webkit|ms)(?=[A-Z])/,l=f.body&&f.body.style,k=!1,n=!1;if(l){for(var r in l)if(k=h.exec(r)){g=k[0];g=g.substr(0,1).toUpperCase()+g.substr(1);break}g||(g="WebkitOpacity"in l&&"webkit");k=!!("transition"in l||g+"Transition"in l);n=!!("animation"in l||g+"Animation"in l);!d||k&&n||(k=L(l.webkitTransition),n=L(l.webkitAnimation))}return{history:!(!b.history||!b.history.pushState||4>d||e),hasEvent:function(a){if("input"===
|
|
||||||
a&&11>=Ua)return!1;if(A(c[a])){var b=f.createElement("div");c[a]="on"+a in b}return c[a]},csp:fb(),vendorPrefix:g,transitions:k,animations:n,android:d}}]}function qf(){this.$get=["$templateCache","$http","$q","$sce",function(b,a,c,d){function e(f,g){e.totalPendingRequests++;L(f)&&b.get(f)||(f=d.getTrustedResourceUrl(f));var h=a.defaults&&a.defaults.transformResponse;G(h)?h=h.filter(function(a){return a!==$b}):h===$b&&(h=null);return a.get(f,{cache:b,transformResponse:h})["finally"](function(){e.totalPendingRequests--}).then(function(a){b.put(f,
|
|
||||||
a.data);return a.data},function(a){if(!g)throw ea("tpload",f,a.status,a.statusText);return c.reject(a)})}e.totalPendingRequests=0;return e}]}function rf(){this.$get=["$rootScope","$browser","$location",function(b,a,c){return{findBindings:function(a,b,c){a=a.getElementsByClassName("ng-binding");var g=[];m(a,function(a){var d=ca.element(a).data("$binding");d&&m(d,function(d){c?(new RegExp("(^|\\s)"+ud(b)+"(\\s|\\||$)")).test(d)&&g.push(a):-1!=d.indexOf(b)&&g.push(a)})});return g},findModels:function(a,
|
|
||||||
b,c){for(var g=["ng-","data-ng-","ng\\:"],h=0;h<g.length;++h){var l=a.querySelectorAll("["+g[h]+"model"+(c?"=":"*=")+'"'+b+'"]');if(l.length)return l}},getLocation:function(){return c.url()},setLocation:function(a){a!==c.url()&&(c.url(a),b.$digest())},whenStable:function(b){a.notifyWhenNoOutstandingRequests(b)}}}]}function sf(){this.$get=["$rootScope","$browser","$q","$$q","$exceptionHandler",function(b,a,c,d,e){function f(f,l,k){z(f)||(k=l,l=f,f=v);var n=za.call(arguments,3),r=w(k)&&!k,s=(r?d:c).defer(),
|
|
||||||
m=s.promise,q;q=a.defer(function(){try{s.resolve(f.apply(null,n))}catch(a){s.reject(a),e(a)}finally{delete g[m.$$timeoutId]}r||b.$apply()},l);m.$$timeoutId=q;g[q]=s;return m}var g={};f.cancel=function(b){return b&&b.$$timeoutId in g?(g[b.$$timeoutId].reject("canceled"),delete g[b.$$timeoutId],a.defer.cancel(b.$$timeoutId)):!1};return f}]}function Ba(b){Ua&&(X.setAttribute("href",b),b=X.href);X.setAttribute("href",b);return{href:X.href,protocol:X.protocol?X.protocol.replace(/:$/,""):"",host:X.host,
|
|
||||||
search:X.search?X.search.replace(/^\?/,""):"",hash:X.hash?X.hash.replace(/^#/,""):"",hostname:X.hostname,port:X.port,pathname:"/"===X.pathname.charAt(0)?X.pathname:"/"+X.pathname}}function gd(b){b=L(b)?Ba(b):b;return b.protocol===wd.protocol&&b.host===wd.host}function tf(){this.$get=ra(O)}function xd(b){function a(a){try{return decodeURIComponent(a)}catch(b){return a}}var c=b[0]||{},d={},e="";return function(){var b,g,h,l,k;b=c.cookie||"";if(b!==e)for(e=b,b=e.split("; "),d={},h=0;h<b.length;h++)g=
|
|
||||||
b[h],l=g.indexOf("="),0<l&&(k=a(g.substring(0,l)),d[k]===t&&(d[k]=a(g.substring(l+1))));return d}}function yf(){this.$get=xd}function Lc(b){function a(c,d){if(H(c)){var e={};m(c,function(b,c){e[c]=a(c,b)});return e}return b.factory(c+"Filter",d)}this.register=a;this.$get=["$injector",function(a){return function(b){return a.get(b+"Filter")}}];a("currency",yd);a("date",zd);a("filter",ag);a("json",bg);a("limitTo",cg);a("lowercase",dg);a("number",Ad);a("orderBy",Bd);a("uppercase",eg)}function ag(){return function(b,
|
|
||||||
a,c){if(!Ea(b)){if(null==b)return b;throw J("filter")("notarray",b);}var d;switch(ic(a)){case "function":break;case "boolean":case "null":case "number":case "string":d=!0;case "object":a=fg(a,c,d);break;default:return b}return Array.prototype.filter.call(b,a)}}function fg(b,a,c){var d=H(b)&&"$"in b;!0===a?a=ka:z(a)||(a=function(a,b){if(A(a))return!1;if(null===a||null===b)return a===b;if(H(b)||H(a)&&!rc(a))return!1;a=M(""+a);b=M(""+b);return-1!==a.indexOf(b)});return function(e){return d&&!H(e)?La(e,
|
|
||||||
b.$,a,!1):La(e,b,a,c)}}function La(b,a,c,d,e){var f=ic(b),g=ic(a);if("string"===g&&"!"===a.charAt(0))return!La(b,a.substring(1),c,d);if(G(b))return b.some(function(b){return La(b,a,c,d)});switch(f){case "object":var h;if(d){for(h in b)if("$"!==h.charAt(0)&&La(b[h],a,c,!0))return!0;return e?!1:La(b,a,c,!1)}if("object"===g){for(h in a)if(e=a[h],!z(e)&&!A(e)&&(f="$"===h,!La(f?b:b[h],e,c,f,f)))return!1;return!0}return c(b,a);case "function":return!1;default:return c(b,a)}}function ic(b){return null===
|
|
||||||
b?"null":typeof b}function yd(b){var a=b.NUMBER_FORMATS;return function(b,d,e){A(d)&&(d=a.CURRENCY_SYM);A(e)&&(e=a.PATTERNS[1].maxFrac);return null==b?b:Cd(b,a.PATTERNS[1],a.GROUP_SEP,a.DECIMAL_SEP,e).replace(/\u00A4/g,d)}}function Ad(b){var a=b.NUMBER_FORMATS;return function(b,d){return null==b?b:Cd(b,a.PATTERNS[0],a.GROUP_SEP,a.DECIMAL_SEP,d)}}function Cd(b,a,c,d,e){if(H(b))return"";var f=0>b;b=Math.abs(b);var g=Infinity===b;if(!g&&!isFinite(b))return"";var h=b+"",l="",k=!1,n=[];g&&(l="\u221e");
|
|
||||||
if(!g&&-1!==h.indexOf("e")){var r=h.match(/([\d\.]+)e(-?)(\d+)/);r&&"-"==r[2]&&r[3]>e+1?b=0:(l=h,k=!0)}if(g||k)0<e&&1>b&&(l=b.toFixed(e),b=parseFloat(l));else{g=(h.split(Dd)[1]||"").length;A(e)&&(e=Math.min(Math.max(a.minFrac,g),a.maxFrac));b=+(Math.round(+(b.toString()+"e"+e)).toString()+"e"+-e);var g=(""+b).split(Dd),h=g[0],g=g[1]||"",r=0,s=a.lgSize,m=a.gSize;if(h.length>=s+m)for(r=h.length-s,k=0;k<r;k++)0===(r-k)%m&&0!==k&&(l+=c),l+=h.charAt(k);for(k=r;k<h.length;k++)0===(h.length-k)%s&&0!==k&&
|
|
||||||
(l+=c),l+=h.charAt(k);for(;g.length<e;)g+="0";e&&"0"!==e&&(l+=d+g.substr(0,e))}0===b&&(f=!1);n.push(f?a.negPre:a.posPre,l,f?a.negSuf:a.posSuf);return n.join("")}function Gb(b,a,c){var d="";0>b&&(d="-",b=-b);for(b=""+b;b.length<a;)b="0"+b;c&&(b=b.substr(b.length-a));return d+b}function Y(b,a,c,d){c=c||0;return function(e){e=e["get"+b]();if(0<c||e>-c)e+=c;0===e&&-12==c&&(e=12);return Gb(e,a,d)}}function Hb(b,a){return function(c,d){var e=c["get"+b](),f=rb(a?"SHORT"+b:b);return d[f][e]}}function Ed(b){var a=
|
|
||||||
(new Date(b,0,1)).getDay();return new Date(b,0,(4>=a?5:12)-a)}function Fd(b){return function(a){var c=Ed(a.getFullYear());a=+new Date(a.getFullYear(),a.getMonth(),a.getDate()+(4-a.getDay()))-+c;a=1+Math.round(a/6048E5);return Gb(a,b)}}function jc(b,a){return 0>=b.getFullYear()?a.ERAS[0]:a.ERAS[1]}function zd(b){function a(a){var b;if(b=a.match(c)){a=new Date(0);var f=0,g=0,h=b[8]?a.setUTCFullYear:a.setFullYear,l=b[8]?a.setUTCHours:a.setHours;b[9]&&(f=W(b[9]+b[10]),g=W(b[9]+b[11]));h.call(a,W(b[1]),
|
|
||||||
W(b[2])-1,W(b[3]));f=W(b[4]||0)-f;g=W(b[5]||0)-g;h=W(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));l.call(a,f,g,h,b)}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,e,f){var g="",h=[],l,k;e=e||"mediumDate";e=b.DATETIME_FORMATS[e]||e;L(c)&&(c=gg.test(c)?W(c):a(c));V(c)&&(c=new Date(c));if(!aa(c)||!isFinite(c.getTime()))return c;for(;e;)(k=hg.exec(e))?(h=cb(h,k,1),e=h.pop()):(h.push(e),e=null);var n=c.getTimezoneOffset();
|
|
||||||
f&&(n=xc(f,c.getTimezoneOffset()),c=Pb(c,f,!0));m(h,function(a){l=ig[a];g+=l?l(c,b.DATETIME_FORMATS,n):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function bg(){return function(b,a){A(a)&&(a=2);return db(b,a)}}function cg(){return function(b,a,c){a=Infinity===Math.abs(Number(a))?Number(a):W(a);if(isNaN(a))return b;V(b)&&(b=b.toString());if(!G(b)&&!L(b))return b;c=!c||isNaN(c)?0:W(c);c=0>c&&c>=-b.length?b.length+c:c;return 0<=a?b.slice(c,c+a):0===c?b.slice(a,b.length):b.slice(Math.max(0,
|
|
||||||
c+a),c)}}function Bd(b){function a(a,c){c=c?-1:1;return a.map(function(a){var d=1,h=Ya;if(z(a))h=a;else if(L(a)){if("+"==a.charAt(0)||"-"==a.charAt(0))d="-"==a.charAt(0)?-1:1,a=a.substring(1);if(""!==a&&(h=b(a),h.constant))var l=h(),h=function(a){return a[l]}}return{get:h,descending:d*c}})}function c(a){switch(typeof a){case "number":case "boolean":case "string":return!0;default:return!1}}return function(b,e,f){if(!Ea(b))return b;G(e)||(e=[e]);0===e.length&&(e=["+"]);var g=a(e,f);b=Array.prototype.map.call(b,
|
|
||||||
function(a,b){return{value:a,predicateValues:g.map(function(d){var e=d.get(a);d=typeof e;if(null===e)d="string",e="null";else if("string"===d)e=e.toLowerCase();else if("object"===d)a:{if("function"===typeof e.valueOf&&(e=e.valueOf(),c(e)))break a;if(rc(e)&&(e=e.toString(),c(e)))break a;e=b}return{value:e,type:d}})}});b.sort(function(a,b){for(var c=0,d=0,e=g.length;d<e;++d){var c=a.predicateValues[d],f=b.predicateValues[d],m=0;c.type===f.type?c.value!==f.value&&(m=c.value<f.value?-1:1):m=c.type<f.type?
|
|
||||||
-1:1;if(c=m*g[d].descending)break}return c});return b=b.map(function(a){return a.value})}}function Ma(b){z(b)&&(b={link:b});b.restrict=b.restrict||"AC";return ra(b)}function Gd(b,a,c,d,e){var f=this,g=[],h=f.$$parentForm=b.parent().controller("form")||Ib;f.$error={};f.$$success={};f.$pending=t;f.$name=e(a.name||a.ngForm||"")(c);f.$dirty=!1;f.$pristine=!0;f.$valid=!0;f.$invalid=!1;f.$submitted=!1;h.$addControl(f);f.$rollbackViewValue=function(){m(g,function(a){a.$rollbackViewValue()})};f.$commitViewValue=
|
|
||||||
function(){m(g,function(a){a.$commitViewValue()})};f.$addControl=function(a){Ra(a.$name,"input");g.push(a);a.$name&&(f[a.$name]=a)};f.$$renameControl=function(a,b){var c=a.$name;f[c]===a&&delete f[c];f[b]=a;a.$name=b};f.$removeControl=function(a){a.$name&&f[a.$name]===a&&delete f[a.$name];m(f.$pending,function(b,c){f.$setValidity(c,null,a)});m(f.$error,function(b,c){f.$setValidity(c,null,a)});m(f.$$success,function(b,c){f.$setValidity(c,null,a)});bb(g,a)};Hd({ctrl:this,$element:b,set:function(a,b,
|
|
||||||
c){var d=a[b];d?-1===d.indexOf(c)&&d.push(c):a[b]=[c]},unset:function(a,b,c){var d=a[b];d&&(bb(d,c),0===d.length&&delete a[b])},parentForm:h,$animate:d});f.$setDirty=function(){d.removeClass(b,Va);d.addClass(b,Jb);f.$dirty=!0;f.$pristine=!1;h.$setDirty()};f.$setPristine=function(){d.setClass(b,Va,Jb+" ng-submitted");f.$dirty=!1;f.$pristine=!0;f.$submitted=!1;m(g,function(a){a.$setPristine()})};f.$setUntouched=function(){m(g,function(a){a.$setUntouched()})};f.$setSubmitted=function(){d.addClass(b,
|
|
||||||
"ng-submitted");f.$submitted=!0;h.$setSubmitted()}}function kc(b){b.$formatters.push(function(a){return b.$isEmpty(a)?a:a.toString()})}function kb(b,a,c,d,e,f){var g=M(a[0].type);if(!e.android){var h=!1;a.on("compositionstart",function(a){h=!0});a.on("compositionend",function(){h=!1;l()})}var l=function(b){k&&(f.defer.cancel(k),k=null);if(!h){var e=a.val();b=b&&b.type;"password"===g||c.ngTrim&&"false"===c.ngTrim||(e=R(e));(d.$viewValue!==e||""===e&&d.$$hasNativeValidators)&&d.$setViewValue(e,b)}};
|
|
||||||
if(e.hasEvent("input"))a.on("input",l);else{var k,n=function(a,b,c){k||(k=f.defer(function(){k=null;b&&b.value===c||l(a)}))};a.on("keydown",function(a){var b=a.keyCode;91===b||15<b&&19>b||37<=b&&40>=b||n(a,this,this.value)});if(e.hasEvent("paste"))a.on("paste cut",n)}a.on("change",l);d.$render=function(){a.val(d.$isEmpty(d.$viewValue)?"":d.$viewValue)}}function Kb(b,a){return function(c,d){var e,f;if(aa(c))return c;if(L(c)){'"'==c.charAt(0)&&'"'==c.charAt(c.length-1)&&(c=c.substring(1,c.length-1));
|
|
||||||
if(jg.test(c))return new Date(c);b.lastIndex=0;if(e=b.exec(c))return e.shift(),f=d?{yyyy:d.getFullYear(),MM:d.getMonth()+1,dd:d.getDate(),HH:d.getHours(),mm:d.getMinutes(),ss:d.getSeconds(),sss:d.getMilliseconds()/1E3}:{yyyy:1970,MM:1,dd:1,HH:0,mm:0,ss:0,sss:0},m(e,function(b,c){c<a.length&&(f[a[c]]=+b)}),new Date(f.yyyy,f.MM-1,f.dd,f.HH,f.mm,f.ss||0,1E3*f.sss||0)}return NaN}}function lb(b,a,c,d){return function(e,f,g,h,l,k,n){function r(a){return a&&!(a.getTime&&a.getTime()!==a.getTime())}function s(a){return w(a)?
|
|
||||||
aa(a)?a:c(a):t}Id(e,f,g,h);kb(e,f,g,h,l,k);var m=h&&h.$options&&h.$options.timezone,q;h.$$parserName=b;h.$parsers.push(function(b){return h.$isEmpty(b)?null:a.test(b)?(b=c(b,q),m&&(b=Pb(b,m)),b):t});h.$formatters.push(function(a){if(a&&!aa(a))throw Lb("datefmt",a);if(r(a))return(q=a)&&m&&(q=Pb(q,m,!0)),n("date")(a,d,m);q=null;return""});if(w(g.min)||g.ngMin){var F;h.$validators.min=function(a){return!r(a)||A(F)||c(a)>=F};g.$observe("min",function(a){F=s(a);h.$validate()})}if(w(g.max)||g.ngMax){var u;
|
|
||||||
h.$validators.max=function(a){return!r(a)||A(u)||c(a)<=u};g.$observe("max",function(a){u=s(a);h.$validate()})}}}function Id(b,a,c,d){(d.$$hasNativeValidators=H(a[0].validity))&&d.$parsers.push(function(b){var c=a.prop("validity")||{};return c.badInput&&!c.typeMismatch?t:b})}function Jd(b,a,c,d,e){if(w(d)){b=b(d);if(!b.constant)throw J("ngModel")("constexpr",c,d);return b(a)}return e}function lc(b,a){b="ngClass"+b;return["$animate",function(c){function d(a,b){var c=[],d=0;a:for(;d<a.length;d++){for(var e=
|
|
||||||
a[d],n=0;n<b.length;n++)if(e==b[n])continue a;c.push(e)}return c}function e(a){var b=[];return G(a)?(m(a,function(a){b=b.concat(e(a))}),b):L(a)?a.split(" "):H(a)?(m(a,function(a,c){a&&(b=b.concat(c.split(" ")))}),b):a}return{restrict:"AC",link:function(f,g,h){function l(a,b){var c=g.data("$classCounts")||ga(),d=[];m(a,function(a){if(0<b||c[a])c[a]=(c[a]||0)+b,c[a]===+(0<b)&&d.push(a)});g.data("$classCounts",c);return d.join(" ")}function k(b){if(!0===a||f.$index%2===a){var k=e(b||[]);if(!n){var m=
|
|
||||||
l(k,1);h.$addClass(m)}else if(!ka(b,n)){var q=e(n),m=d(k,q),k=d(q,k),m=l(m,1),k=l(k,-1);m&&m.length&&c.addClass(g,m);k&&k.length&&c.removeClass(g,k)}}n=ia(b)}var n;f.$watch(h[b],k,!0);h.$observe("class",function(a){k(f.$eval(h[b]))});"ngClass"!==b&&f.$watch("$index",function(c,d){var g=c&1;if(g!==(d&1)){var k=e(f.$eval(h[b]));g===a?(g=l(k,1),h.$addClass(g)):(g=l(k,-1),h.$removeClass(g))}})}}}]}function Hd(b){function a(a,b){b&&!f[a]?(k.addClass(e,a),f[a]=!0):!b&&f[a]&&(k.removeClass(e,a),f[a]=!1)}
|
|
||||||
function c(b,c){b=b?"-"+Bc(b,"-"):"";a(mb+b,!0===c);a(Kd+b,!1===c)}var d=b.ctrl,e=b.$element,f={},g=b.set,h=b.unset,l=b.parentForm,k=b.$animate;f[Kd]=!(f[mb]=e.hasClass(mb));d.$setValidity=function(b,e,f){e===t?(d.$pending||(d.$pending={}),g(d.$pending,b,f)):(d.$pending&&h(d.$pending,b,f),Ld(d.$pending)&&(d.$pending=t));ab(e)?e?(h(d.$error,b,f),g(d.$$success,b,f)):(g(d.$error,b,f),h(d.$$success,b,f)):(h(d.$error,b,f),h(d.$$success,b,f));d.$pending?(a(Md,!0),d.$valid=d.$invalid=t,c("",null)):(a(Md,
|
|
||||||
!1),d.$valid=Ld(d.$error),d.$invalid=!d.$valid,c("",d.$valid));e=d.$pending&&d.$pending[b]?t:d.$error[b]?!1:d.$$success[b]?!0:null;c(b,e);l.$setValidity(b,e,d)}}function Ld(b){if(b)for(var a in b)if(b.hasOwnProperty(a))return!1;return!0}var kg=/^\/(.+)\/([a-z]*)$/,M=function(b){return L(b)?b.toLowerCase():b},Xa=Object.prototype.hasOwnProperty,rb=function(b){return L(b)?b.toUpperCase():b},Ua,y,la,za=[].slice,Nf=[].splice,lg=[].push,sa=Object.prototype.toString,sc=Object.getPrototypeOf,Fa=J("ng"),ca=
|
|
||||||
O.angular||(O.angular={}),gb,nb=0;Ua=U.documentMode;v.$inject=[];Ya.$inject=[];var G=Array.isArray,uc=/^\[object (Uint8(Clamped)?)|(Uint16)|(Uint32)|(Int8)|(Int16)|(Int32)|(Float(32)|(64))Array\]$/,R=function(b){return L(b)?b.trim():b},ud=function(b){return b.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08")},fb=function(){if(w(fb.isActive_))return fb.isActive_;var b=!(!U.querySelector("[ng-csp]")&&!U.querySelector("[data-ng-csp]"));if(!b)try{new Function("")}catch(a){b=!0}return fb.isActive_=
|
|
||||||
b},pb=function(){if(w(pb.name_))return pb.name_;var b,a,c=Oa.length,d,e;for(a=0;a<c;++a)if(d=Oa[a],b=U.querySelector("["+d.replace(":","\\:")+"jq]")){e=b.getAttribute(d+"jq");break}return pb.name_=e},Oa=["ng-","data-ng-","ng:","x-ng-"],be=/[A-Z]/g,Cc=!1,Rb,qa=1,Na=3,fe={full:"1.4.2",major:1,minor:4,dot:2,codeName:"nebular-readjustment"};Q.expando="ng339";var ib=Q.cache={},Ef=1;Q._data=function(b){return this.cache[b[this.expando]]||{}};var zf=/([\:\-\_]+(.))/g,Af=/^moz([A-Z])/,mg={mouseleave:"mouseout",
|
|
||||||
mouseenter:"mouseover"},Ub=J("jqLite"),Df=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,Tb=/<|&#?\w+;/,Bf=/<([\w:]+)/,Cf=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,na={option:[1,'<select multiple="multiple">',"</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};na.optgroup=na.option;na.tbody=na.tfoot=na.colgroup=na.caption=na.thead;
|
|
||||||
na.th=na.td;var Pa=Q.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=!1;"complete"===U.readyState?setTimeout(a):(this.on("DOMContentLoaded",a),Q(O).on("load",a))},toString:function(){var b=[];m(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return 0<=b?y(this[b]):y(this[this.length+b])},length:0,push:lg,sort:[].sort,splice:[].splice},Ab={};m("multiple selected checked disabled readOnly required open".split(" "),function(b){Ab[M(b)]=b});var Tc={};m("input select option textarea button form details".split(" "),
|
|
||||||
function(b){Tc[b]=!0});var Uc={ngMinlength:"minlength",ngMaxlength:"maxlength",ngMin:"min",ngMax:"max",ngPattern:"pattern"};m({data:Wb,removeData:ub,hasData:function(b){for(var a in ib[b.ng339])return!0;return!1}},function(b,a){Q[a]=b});m({data:Wb,inheritedData:zb,scope:function(b){return y.data(b,"$scope")||zb(b.parentNode||b,["$isolateScope","$scope"])},isolateScope:function(b){return y.data(b,"$isolateScope")||y.data(b,"$isolateScopeNoTemplate")},controller:Qc,injector:function(b){return zb(b,
|
|
||||||
"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:wb,css:function(b,a,c){a=hb(a);if(w(c))b.style[a]=c;else return b.style[a]},attr:function(b,a,c){var d=b.nodeType;if(d!==Na&&2!==d&&8!==d)if(d=M(a),Ab[d])if(w(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||v).specified?d:t;else if(w(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),null===b?t:b},prop:function(b,a,c){if(w(c))b[a]=c;else return b[a]},
|
|
||||||
text:function(){function b(a,b){if(A(b)){var d=a.nodeType;return d===qa||d===Na?a.textContent:""}a.textContent=b}b.$dv="";return b}(),val:function(b,a){if(A(a)){if(b.multiple&&"select"===ta(b)){var c=[];m(b.options,function(a){a.selected&&c.push(a.value||a.text)});return 0===c.length?null:c}return b.value}b.value=a},html:function(b,a){if(A(a))return b.innerHTML;tb(b,!0);b.innerHTML=a},empty:Rc},function(b,a){Q.prototype[a]=function(a,d){var e,f,g=this.length;if(b!==Rc&&(2==b.length&&b!==wb&&b!==Qc?
|
|
||||||
a:d)===t){if(H(a)){for(e=0;e<g;e++)if(b===Wb)b(this[e],a);else for(f in a)b(this[e],f,a[f]);return this}e=b.$dv;g=e===t?Math.min(g,1):g;for(f=0;f<g;f++){var h=b(this[f],a,d);e=e?e+h:h}return e}for(e=0;e<g;e++)b(this[e],a,d);return this}});m({removeData:ub,on:function a(c,d,e,f){if(w(f))throw Ub("onargs");if(Mc(c)){var g=vb(c,!0);f=g.events;var h=g.handle;h||(h=g.handle=Hf(c,f));for(var g=0<=d.indexOf(" ")?d.split(" "):[d],l=g.length;l--;){d=g[l];var k=f[d];k||(f[d]=[],"mouseenter"===d||"mouseleave"===
|
|
||||||
d?a(c,mg[d],function(a){var c=a.relatedTarget;c&&(c===this||this.contains(c))||h(a,d)}):"$destroy"!==d&&c.addEventListener(d,h,!1),k=f[d]);k.push(e)}}},off:Pc,one:function(a,c,d){a=y(a);a.on(c,function f(){a.off(c,d);a.off(c,f)});a.on(c,d)},replaceWith:function(a,c){var d,e=a.parentNode;tb(a);m(new Q(c),function(c){d?e.insertBefore(c,d.nextSibling):e.replaceChild(c,a);d=c})},children:function(a){var c=[];m(a.childNodes,function(a){a.nodeType===qa&&c.push(a)});return c},contents:function(a){return a.contentDocument||
|
|
||||||
a.childNodes||[]},append:function(a,c){var d=a.nodeType;if(d===qa||11===d){c=new Q(c);for(var d=0,e=c.length;d<e;d++)a.appendChild(c[d])}},prepend:function(a,c){if(a.nodeType===qa){var d=a.firstChild;m(new Q(c),function(c){a.insertBefore(c,d)})}},wrap:function(a,c){c=y(c).eq(0).clone()[0];var d=a.parentNode;d&&d.replaceChild(c,a);c.appendChild(a)},remove:Xb,detach:function(a){Xb(a,!0)},after:function(a,c){var d=a,e=a.parentNode;c=new Q(c);for(var f=0,g=c.length;f<g;f++){var h=c[f];e.insertBefore(h,
|
|
||||||
d.nextSibling);d=h}},addClass:yb,removeClass:xb,toggleClass:function(a,c,d){c&&m(c.split(" "),function(c){var f=d;A(f)&&(f=!wb(a,c));(f?yb:xb)(a,c)})},parent:function(a){return(a=a.parentNode)&&11!==a.nodeType?a:null},next:function(a){return a.nextElementSibling},find:function(a,c){return a.getElementsByTagName?a.getElementsByTagName(c):[]},clone:Vb,triggerHandler:function(a,c,d){var e,f,g=c.type||c,h=vb(a);if(h=(h=h&&h.events)&&h[g])e={preventDefault:function(){this.defaultPrevented=!0},isDefaultPrevented:function(){return!0===
|
|
||||||
this.defaultPrevented},stopImmediatePropagation:function(){this.immediatePropagationStopped=!0},isImmediatePropagationStopped:function(){return!0===this.immediatePropagationStopped},stopPropagation:v,type:g,target:a},c.type&&(e=P(e,c)),c=ia(h),f=d?[e].concat(d):[e],m(c,function(c){e.isImmediatePropagationStopped()||c.apply(a,f)})}},function(a,c){Q.prototype[c]=function(c,e,f){for(var g,h=0,l=this.length;h<l;h++)A(g)?(g=a(this[h],c,e,f),w(g)&&(g=y(g))):Oc(g,a(this[h],c,e,f));return w(g)?g:this};Q.prototype.bind=
|
|
||||||
Q.prototype.on;Q.prototype.unbind=Q.prototype.off});Sa.prototype={put:function(a,c){this[Ga(a,this.nextUid)]=c},get:function(a){return this[Ga(a,this.nextUid)]},remove:function(a){var c=this[a=Ga(a,this.nextUid)];delete this[a];return c}};var xf=[function(){this.$get=[function(){return Sa}]}],Wc=/^function\s*[^\(]*\(\s*([^\)]*)\)/m,ng=/,/,og=/^\s*(_?)(\S+?)\1\s*$/,Vc=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Ha=J("$injector");eb.$$annotate=function(a,c,d){var e;if("function"===typeof a){if(!(e=a.$inject)){e=
|
|
||||||
[];if(a.length){if(c)throw L(d)&&d||(d=a.name||If(a)),Ha("strictdi",d);c=a.toString().replace(Vc,"");c=c.match(Wc);m(c[1].split(ng),function(a){a.replace(og,function(a,c,d){e.push(d)})})}a.$inject=e}}else G(a)?(c=a.length-1,Qa(a[c],"fn"),e=a.slice(0,c)):Qa(a,"fn",!0);return e};var Nd=J("$animate"),Ue=function(){this.$get=["$q","$$rAF",function(a,c){function d(){}d.all=v;d.chain=v;d.prototype={end:v,cancel:v,resume:v,pause:v,complete:v,then:function(d,f){return a(function(a){c(function(){a()})}).then(d,
|
|
||||||
f)}};return d}]},Te=function(){var a=new Sa,c=[];this.$get=["$$AnimateRunner","$rootScope",function(d,e){function f(d,f,l){var k=a.get(d);k||(a.put(d,k={}),c.push(d));f&&m(f.split(" "),function(a){a&&(k[a]=!0)});l&&m(l.split(" "),function(a){a&&(k[a]=!1)});1<c.length||e.$$postDigest(function(){m(c,function(c){var d=a.get(c);if(d){var e=Jf(c.attr("class")),f="",g="";m(d,function(a,c){a!==!!e[c]&&(a?f+=(f.length?" ":"")+c:g+=(g.length?" ":"")+c)});m(c,function(a){f&&yb(a,f);g&&xb(a,g)});a.remove(c)}});
|
|
||||||
c.length=0})}return{enabled:v,on:v,off:v,pin:v,push:function(a,c,e,k){k&&k();e=e||{};e.from&&a.css(e.from);e.to&&a.css(e.to);(e.addClass||e.removeClass)&&f(a,e.addClass,e.removeClass);return new d}}}]},Se=["$provide",function(a){var c=this;this.$$registeredAnimations=Object.create(null);this.register=function(d,e){if(d&&"."!==d.charAt(0))throw Nd("notcsel",d);var f=d+"-animation";c.$$registeredAnimations[d.substr(1)]=f;a.factory(f,e)};this.classNameFilter=function(a){if(1===arguments.length&&(this.$$classNameFilter=
|
|
||||||
a instanceof RegExp?a:null)&&/(\s+|\/)ng-animate(\s+|\/)/.test(this.$$classNameFilter.toString()))throw Nd("nongcls","ng-animate");return this.$$classNameFilter};this.$get=["$$animateQueue",function(a){function c(a,d,e){if(e){var l;a:{for(l=0;l<e.length;l++){var k=e[l];if(1===k.nodeType){l=k;break a}}l=void 0}!l||l.parentNode||l.previousElementSibling||(e=null)}e?e.after(a):d.prepend(a)}return{on:a.on,off:a.off,pin:a.pin,enabled:a.enabled,cancel:function(a){a.end&&a.end()},enter:function(f,g,h,l){g=
|
|
||||||
g&&y(g);h=h&&y(h);g=g||h.parent();c(f,g,h);return a.push(f,"enter",Ia(l))},move:function(f,g,h,l){g=g&&y(g);h=h&&y(h);g=g||h.parent();c(f,g,h);return a.push(f,"move",Ia(l))},leave:function(c,e){return a.push(c,"leave",Ia(e),function(){c.remove()})},addClass:function(c,e,h){h=Ia(h);h.addClass=jb(h.addclass,e);return a.push(c,"addClass",h)},removeClass:function(c,e,h){h=Ia(h);h.removeClass=jb(h.removeClass,e);return a.push(c,"removeClass",h)},setClass:function(c,e,h,l){l=Ia(l);l.addClass=jb(l.addClass,
|
|
||||||
e);l.removeClass=jb(l.removeClass,h);return a.push(c,"setClass",l)},animate:function(c,e,h,l,k){k=Ia(k);k.from=k.from?P(k.from,e):e;k.to=k.to?P(k.to,h):h;k.tempClasses=jb(k.tempClasses,l||"ng-inline-animate");return a.push(c,"animate",k)}}}]}],ea=J("$compile");Ec.$inject=["$provide","$$sanitizeUriProvider"];var Zc=/^((?:x|data)[\:\-_])/i,Of=J("$controller"),Xc=/^(\S+)(\s+as\s+(\w+))?$/,cd="application/json",ac={"Content-Type":cd+";charset=utf-8"},Qf=/^\[|^\{(?!\{)/,Rf={"[":/]$/,"{":/}$/},Pf=/^\)\]\}',?\n/,
|
|
||||||
Ka=ca.$interpolateMinErr=J("$interpolate");Ka.throwNoconcat=function(a){throw Ka("noconcat",a);};Ka.interr=function(a,c){return Ka("interr",a,c.toString())};var pg=/^([^\?#]*)(\?([^#]*))?(#(.*))?$/,Uf={http:80,https:443,ftp:21},Cb=J("$location"),qg={$$html5:!1,$$replace:!1,absUrl:Db("$$absUrl"),url:function(a){if(A(a))return this.$$url;var c=pg.exec(a);(c[1]||""===a)&&this.path(decodeURIComponent(c[1]));(c[2]||c[1]||""===a)&&this.search(c[3]||"");this.hash(c[5]||"");return this},protocol:Db("$$protocol"),
|
|
||||||
host:Db("$$host"),port:Db("$$port"),path:kd("$$path",function(a){a=null!==a?a.toString():"";return"/"==a.charAt(0)?a:"/"+a}),search:function(a,c){switch(arguments.length){case 0:return this.$$search;case 1:if(L(a)||V(a))a=a.toString(),this.$$search=zc(a);else if(H(a))a=fa(a,{}),m(a,function(c,e){null==c&&delete a[e]}),this.$$search=a;else throw Cb("isrcharg");break;default:A(c)||null===c?delete this.$$search[a]:this.$$search[a]=c}this.$$compose();return this},hash:kd("$$hash",function(a){return null!==
|
|
||||||
a?a.toString():""}),replace:function(){this.$$replace=!0;return this}};m([jd,ec,dc],function(a){a.prototype=Object.create(qg);a.prototype.state=function(c){if(!arguments.length)return this.$$state;if(a!==dc||!this.$$html5)throw Cb("nostate");this.$$state=A(c)?null:c;return this}});var da=J("$parse"),Vf=Function.prototype.call,Wf=Function.prototype.apply,Xf=Function.prototype.bind,Mb=ga();m("+ - * / % === !== == != < > <= >= && || ! = |".split(" "),function(a){Mb[a]=!0});var rg={n:"\n",f:"\f",r:"\r",
|
|
||||||
t:"\t",v:"\v","'":"'",'"':'"'},gc=function(a){this.options=a};gc.prototype={constructor:gc,lex:function(a){this.text=a;this.index=0;for(this.tokens=[];this.index<this.text.length;)if(a=this.text.charAt(this.index),'"'===a||"'"===a)this.readString(a);else if(this.isNumber(a)||"."===a&&this.isNumber(this.peek()))this.readNumber();else if(this.isIdent(a))this.readIdent();else if(this.is(a,"(){}[].,;:?"))this.tokens.push({index:this.index,text:a}),this.index++;else if(this.isWhitespace(a))this.index++;
|
|
||||||
else{var c=a+this.peek(),d=c+this.peek(2),e=Mb[c],f=Mb[d];Mb[a]||e||f?(a=f?d:e?c:a,this.tokens.push({index:this.index,text:a,operator:!0}),this.index+=a.length):this.throwError("Unexpected next character ",this.index,this.index+1)}return this.tokens},is:function(a,c){return-1!==c.indexOf(a)},peek:function(a){a=a||1;return this.index+a<this.text.length?this.text.charAt(this.index+a):!1},isNumber:function(a){return"0"<=a&&"9">=a&&"string"===typeof a},isWhitespace:function(a){return" "===a||"\r"===a||
|
|
||||||
"\t"===a||"\n"===a||"\v"===a||"\u00a0"===a},isIdent:function(a){return"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},throwError:function(a,c,d){d=d||this.index;c=w(c)?"s "+c+"-"+this.index+" ["+this.text.substring(c,d)+"]":" "+d;throw da("lexerr",a,c,this.text);},readNumber:function(){for(var a="",c=this.index;this.index<this.text.length;){var d=M(this.text.charAt(this.index));if("."==d||this.isNumber(d))a+=d;else{var e=this.peek();
|
|
||||||
if("e"==d&&this.isExpOperator(e))a+=d;else if(this.isExpOperator(d)&&e&&this.isNumber(e)&&"e"==a.charAt(a.length-1))a+=d;else if(!this.isExpOperator(d)||e&&this.isNumber(e)||"e"!=a.charAt(a.length-1))break;else this.throwError("Invalid exponent")}this.index++}this.tokens.push({index:c,text:a,constant:!0,value:Number(a)})},readIdent:function(){for(var a=this.index;this.index<this.text.length;){var c=this.text.charAt(this.index);if(!this.isIdent(c)&&!this.isNumber(c))break;this.index++}this.tokens.push({index:a,
|
|
||||||
text:this.text.slice(a,this.index),identifier:!0})},readString:function(a){var c=this.index;this.index++;for(var d="",e=a,f=!1;this.index<this.text.length;){var g=this.text.charAt(this.index),e=e+g;if(f)"u"===g?(f=this.text.substring(this.index+1,this.index+5),f.match(/[\da-f]{4}/i)||this.throwError("Invalid unicode escape [\\u"+f+"]"),this.index+=4,d+=String.fromCharCode(parseInt(f,16))):d+=rg[g]||g,f=!1;else if("\\"===g)f=!0;else{if(g===a){this.index++;this.tokens.push({index:c,text:e,constant:!0,
|
|
||||||
value:d});return}d+=g}this.index++}this.throwError("Unterminated quote",c)}};var q=function(a,c){this.lexer=a;this.options=c};q.Program="Program";q.ExpressionStatement="ExpressionStatement";q.AssignmentExpression="AssignmentExpression";q.ConditionalExpression="ConditionalExpression";q.LogicalExpression="LogicalExpression";q.BinaryExpression="BinaryExpression";q.UnaryExpression="UnaryExpression";q.CallExpression="CallExpression";q.MemberExpression="MemberExpression";q.Identifier="Identifier";q.Literal=
|
|
||||||
"Literal";q.ArrayExpression="ArrayExpression";q.Property="Property";q.ObjectExpression="ObjectExpression";q.ThisExpression="ThisExpression";q.NGValueParameter="NGValueParameter";q.prototype={ast:function(a){this.text=a;this.tokens=this.lexer.lex(a);a=this.program();0!==this.tokens.length&&this.throwError("is an unexpected token",this.tokens[0]);return a},program:function(){for(var a=[];;)if(0<this.tokens.length&&!this.peek("}",")",";","]")&&a.push(this.expressionStatement()),!this.expect(";"))return{type:q.Program,
|
|
||||||
body:a}},expressionStatement:function(){return{type:q.ExpressionStatement,expression:this.filterChain()}},filterChain:function(){for(var a=this.expression();this.expect("|");)a=this.filter(a);return a},expression:function(){return this.assignment()},assignment:function(){var a=this.ternary();this.expect("=")&&(a={type:q.AssignmentExpression,left:a,right:this.assignment(),operator:"="});return a},ternary:function(){var a=this.logicalOR(),c,d;return this.expect("?")&&(c=this.expression(),this.consume(":"))?
|
|
||||||
(d=this.expression(),{type:q.ConditionalExpression,test:a,alternate:c,consequent:d}):a},logicalOR:function(){for(var a=this.logicalAND();this.expect("||");)a={type:q.LogicalExpression,operator:"||",left:a,right:this.logicalAND()};return a},logicalAND:function(){for(var a=this.equality();this.expect("&&");)a={type:q.LogicalExpression,operator:"&&",left:a,right:this.equality()};return a},equality:function(){for(var a=this.relational(),c;c=this.expect("==","!=","===","!==");)a={type:q.BinaryExpression,
|
|
||||||
operator:c.text,left:a,right:this.relational()};return a},relational:function(){for(var a=this.additive(),c;c=this.expect("<",">","<=",">=");)a={type:q.BinaryExpression,operator:c.text,left:a,right:this.additive()};return a},additive:function(){for(var a=this.multiplicative(),c;c=this.expect("+","-");)a={type:q.BinaryExpression,operator:c.text,left:a,right:this.multiplicative()};return a},multiplicative:function(){for(var a=this.unary(),c;c=this.expect("*","/","%");)a={type:q.BinaryExpression,operator:c.text,
|
|
||||||
left:a,right:this.unary()};return a},unary:function(){var a;return(a=this.expect("+","-","!"))?{type:q.UnaryExpression,operator:a.text,prefix:!0,argument:this.unary()}:this.primary()},primary:function(){var a;this.expect("(")?(a=this.filterChain(),this.consume(")")):this.expect("[")?a=this.arrayDeclaration():this.expect("{")?a=this.object():this.constants.hasOwnProperty(this.peek().text)?a=fa(this.constants[this.consume().text]):this.peek().identifier?a=this.identifier():this.peek().constant?a=this.constant():
|
|
||||||
this.throwError("not a primary expression",this.peek());for(var c;c=this.expect("(","[",".");)"("===c.text?(a={type:q.CallExpression,callee:a,arguments:this.parseArguments()},this.consume(")")):"["===c.text?(a={type:q.MemberExpression,object:a,property:this.expression(),computed:!0},this.consume("]")):"."===c.text?a={type:q.MemberExpression,object:a,property:this.identifier(),computed:!1}:this.throwError("IMPOSSIBLE");return a},filter:function(a){a=[a];for(var c={type:q.CallExpression,callee:this.identifier(),
|
|
||||||
arguments:a,filter:!0};this.expect(":");)a.push(this.expression());return c},parseArguments:function(){var a=[];if(")"!==this.peekToken().text){do a.push(this.expression());while(this.expect(","))}return a},identifier:function(){var a=this.consume();a.identifier||this.throwError("is not a valid identifier",a);return{type:q.Identifier,name:a.text}},constant:function(){return{type:q.Literal,value:this.consume().value}},arrayDeclaration:function(){var a=[];if("]"!==this.peekToken().text){do{if(this.peek("]"))break;
|
|
||||||
a.push(this.expression())}while(this.expect(","))}this.consume("]");return{type:q.ArrayExpression,elements:a}},object:function(){var a=[],c;if("}"!==this.peekToken().text){do{if(this.peek("}"))break;c={type:q.Property,kind:"init"};this.peek().constant?c.key=this.constant():this.peek().identifier?c.key=this.identifier():this.throwError("invalid key",this.peek());this.consume(":");c.value=this.expression();a.push(c)}while(this.expect(","))}this.consume("}");return{type:q.ObjectExpression,properties:a}},
|
|
||||||
throwError:function(a,c){throw da("syntax",c.text,a,c.index+1,this.text,this.text.substring(c.index));},consume:function(a){if(0===this.tokens.length)throw da("ueoe",this.text);var c=this.expect(a);c||this.throwError("is unexpected, expecting ["+a+"]",this.peek());return c},peekToken:function(){if(0===this.tokens.length)throw da("ueoe",this.text);return this.tokens[0]},peek:function(a,c,d,e){return this.peekAhead(0,a,c,d,e)},peekAhead:function(a,c,d,e,f){if(this.tokens.length>a){a=this.tokens[a];
|
|
||||||
var g=a.text;if(g===c||g===d||g===e||g===f||!(c||d||e||f))return a}return!1},expect:function(a,c,d,e){return(a=this.peek(a,c,d,e))?(this.tokens.shift(),a):!1},constants:{"true":{type:q.Literal,value:!0},"false":{type:q.Literal,value:!1},"null":{type:q.Literal,value:null},undefined:{type:q.Literal,value:t},"this":{type:q.ThisExpression}}};rd.prototype={compile:function(a,c){var d=this,e=this.astBuilder.ast(a);this.state={nextId:0,filters:{},expensiveChecks:c,fn:{vars:[],body:[],own:{}},assign:{vars:[],
|
|
||||||
body:[],own:{}},inputs:[]};T(e,d.$filter);var f="",g;this.stage="assign";if(g=pd(e))this.state.computing="assign",f=this.nextId(),this.recurse(g,f),f="fn.assign="+this.generateFunction("assign","s,v,l");g=nd(e.body);d.stage="inputs";m(g,function(a,c){var e="fn"+c;d.state[e]={vars:[],body:[],own:{}};d.state.computing=e;var f=d.nextId();d.recurse(a,f);d.return_(f);d.state.inputs.push(e);a.watchId=c});this.state.computing="fn";this.stage="main";this.recurse(e);f='"'+this.USE+" "+this.STRICT+'";\n'+this.filterPrefix()+
|
|
||||||
"var fn="+this.generateFunction("fn","s,l,a,i")+f+this.watchFns()+"return fn;";f=(new Function("$filter","ensureSafeMemberName","ensureSafeObject","ensureSafeFunction","ifDefined","plus","text",f))(this.$filter,Ca,oa,ld,Yf,md,a);this.state=this.stage=t;f.literal=qd(e);f.constant=e.constant;return f},USE:"use",STRICT:"strict",watchFns:function(){var a=[],c=this.state.inputs,d=this;m(c,function(c){a.push("var "+c+"="+d.generateFunction(c,"s"))});c.length&&a.push("fn.inputs=["+c.join(",")+"];");return a.join("")},
|
|
||||||
generateFunction:function(a,c){return"function("+c+"){"+this.varsPrefix(a)+this.body(a)+"};"},filterPrefix:function(){var a=[],c=this;m(this.state.filters,function(d,e){a.push(d+"=$filter("+c.escape(e)+")")});return a.length?"var "+a.join(",")+";":""},varsPrefix:function(a){return this.state[a].vars.length?"var "+this.state[a].vars.join(",")+";":""},body:function(a){return this.state[a].body.join("")},recurse:function(a,c,d,e,f,g){var h,l,k=this,n,r;e=e||v;if(!g&&w(a.watchId))c=c||this.nextId(),this.if_("i",
|
|
||||||
this.lazyAssign(c,this.computedMember("i",a.watchId)),this.lazyRecurse(a,c,d,e,f,!0));else switch(a.type){case q.Program:m(a.body,function(c,d){k.recurse(c.expression,t,t,function(a){l=a});d!==a.body.length-1?k.current().body.push(l,";"):k.return_(l)});break;case q.Literal:r=this.escape(a.value);this.assign(c,r);e(r);break;case q.UnaryExpression:this.recurse(a.argument,t,t,function(a){l=a});r=a.operator+"("+this.ifDefined(l,0)+")";this.assign(c,r);e(r);break;case q.BinaryExpression:this.recurse(a.left,
|
|
||||||
t,t,function(a){h=a});this.recurse(a.right,t,t,function(a){l=a});r="+"===a.operator?this.plus(h,l):"-"===a.operator?this.ifDefined(h,0)+a.operator+this.ifDefined(l,0):"("+h+")"+a.operator+"("+l+")";this.assign(c,r);e(r);break;case q.LogicalExpression:c=c||this.nextId();k.recurse(a.left,c);k.if_("&&"===a.operator?c:k.not(c),k.lazyRecurse(a.right,c));e(c);break;case q.ConditionalExpression:c=c||this.nextId();k.recurse(a.test,c);k.if_(c,k.lazyRecurse(a.alternate,c),k.lazyRecurse(a.consequent,c));e(c);
|
|
||||||
break;case q.Identifier:c=c||this.nextId();d&&(d.context="inputs"===k.stage?"s":this.assign(this.nextId(),this.getHasOwnProperty("l",a.name)+"?l:s"),d.computed=!1,d.name=a.name);Ca(a.name);k.if_("inputs"===k.stage||k.not(k.getHasOwnProperty("l",a.name)),function(){k.if_("inputs"===k.stage||"s",function(){f&&1!==f&&k.if_(k.not(k.nonComputedMember("s",a.name)),k.lazyAssign(k.nonComputedMember("s",a.name),"{}"));k.assign(c,k.nonComputedMember("s",a.name))})},c&&k.lazyAssign(c,k.nonComputedMember("l",
|
|
||||||
a.name)));(k.state.expensiveChecks||Fb(a.name))&&k.addEnsureSafeObject(c);e(c);break;case q.MemberExpression:h=d&&(d.context=this.nextId())||this.nextId();c=c||this.nextId();k.recurse(a.object,h,t,function(){k.if_(k.notNull(h),function(){if(a.computed)l=k.nextId(),k.recurse(a.property,l),k.addEnsureSafeMemberName(l),f&&1!==f&&k.if_(k.not(k.computedMember(h,l)),k.lazyAssign(k.computedMember(h,l),"{}")),r=k.ensureSafeObject(k.computedMember(h,l)),k.assign(c,r),d&&(d.computed=!0,d.name=l);else{Ca(a.property.name);
|
|
||||||
f&&1!==f&&k.if_(k.not(k.nonComputedMember(h,a.property.name)),k.lazyAssign(k.nonComputedMember(h,a.property.name),"{}"));r=k.nonComputedMember(h,a.property.name);if(k.state.expensiveChecks||Fb(a.property.name))r=k.ensureSafeObject(r);k.assign(c,r);d&&(d.computed=!1,d.name=a.property.name)}},function(){k.assign(c,"undefined")});e(c)},!!f);break;case q.CallExpression:c=c||this.nextId();a.filter?(l=k.filter(a.callee.name),n=[],m(a.arguments,function(a){var c=k.nextId();k.recurse(a,c);n.push(c)}),r=l+
|
|
||||||
"("+n.join(",")+")",k.assign(c,r),e(c)):(l=k.nextId(),h={},n=[],k.recurse(a.callee,l,h,function(){k.if_(k.notNull(l),function(){k.addEnsureSafeFunction(l);m(a.arguments,function(a){k.recurse(a,k.nextId(),t,function(a){n.push(k.ensureSafeObject(a))})});h.name?(k.state.expensiveChecks||k.addEnsureSafeObject(h.context),r=k.member(h.context,h.name,h.computed)+"("+n.join(",")+")"):r=l+"("+n.join(",")+")";r=k.ensureSafeObject(r);k.assign(c,r)},function(){k.assign(c,"undefined")});e(c)}));break;case q.AssignmentExpression:l=
|
|
||||||
this.nextId();h={};if(!od(a.left))throw da("lval");this.recurse(a.left,t,h,function(){k.if_(k.notNull(h.context),function(){k.recurse(a.right,l);k.addEnsureSafeObject(k.member(h.context,h.name,h.computed));r=k.member(h.context,h.name,h.computed)+a.operator+l;k.assign(c,r);e(c||r)})},1);break;case q.ArrayExpression:n=[];m(a.elements,function(a){k.recurse(a,k.nextId(),t,function(a){n.push(a)})});r="["+n.join(",")+"]";this.assign(c,r);e(r);break;case q.ObjectExpression:n=[];m(a.properties,function(a){k.recurse(a.value,
|
|
||||||
k.nextId(),t,function(c){n.push(k.escape(a.key.type===q.Identifier?a.key.name:""+a.key.value)+":"+c)})});r="{"+n.join(",")+"}";this.assign(c,r);e(r);break;case q.ThisExpression:this.assign(c,"s");e("s");break;case q.NGValueParameter:this.assign(c,"v"),e("v")}},getHasOwnProperty:function(a,c){var d=a+"."+c,e=this.current().own;e.hasOwnProperty(d)||(e[d]=this.nextId(!1,a+"&&("+this.escape(c)+" in "+a+")"));return e[d]},assign:function(a,c){if(a)return this.current().body.push(a,"=",c,";"),a},filter:function(a){this.state.filters.hasOwnProperty(a)||
|
|
||||||
(this.state.filters[a]=this.nextId(!0));return this.state.filters[a]},ifDefined:function(a,c){return"ifDefined("+a+","+this.escape(c)+")"},plus:function(a,c){return"plus("+a+","+c+")"},return_:function(a){this.current().body.push("return ",a,";")},if_:function(a,c,d){if(!0===a)c();else{var e=this.current().body;e.push("if(",a,"){");c();e.push("}");d&&(e.push("else{"),d(),e.push("}"))}},not:function(a){return"!("+a+")"},notNull:function(a){return a+"!=null"},nonComputedMember:function(a,c){return a+
|
|
||||||
"."+c},computedMember:function(a,c){return a+"["+c+"]"},member:function(a,c,d){return d?this.computedMember(a,c):this.nonComputedMember(a,c)},addEnsureSafeObject:function(a){this.current().body.push(this.ensureSafeObject(a),";")},addEnsureSafeMemberName:function(a){this.current().body.push(this.ensureSafeMemberName(a),";")},addEnsureSafeFunction:function(a){this.current().body.push(this.ensureSafeFunction(a),";")},ensureSafeObject:function(a){return"ensureSafeObject("+a+",text)"},ensureSafeMemberName:function(a){return"ensureSafeMemberName("+
|
|
||||||
a+",text)"},ensureSafeFunction:function(a){return"ensureSafeFunction("+a+",text)"},lazyRecurse:function(a,c,d,e,f,g){var h=this;return function(){h.recurse(a,c,d,e,f,g)}},lazyAssign:function(a,c){var d=this;return function(){d.assign(a,c)}},stringEscapeRegex:/[^ a-zA-Z0-9]/g,stringEscapeFn:function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)},escape:function(a){if(L(a))return"'"+a.replace(this.stringEscapeRegex,this.stringEscapeFn)+"'";if(V(a))return a.toString();if(!0===a)return"true";
|
|
||||||
if(!1===a)return"false";if(null===a)return"null";if("undefined"===typeof a)return"undefined";throw da("esc");},nextId:function(a,c){var d="v"+this.state.nextId++;a||this.current().vars.push(d+(c?"="+c:""));return d},current:function(){return this.state[this.state.computing]}};sd.prototype={compile:function(a,c){var d=this,e=this.astBuilder.ast(a);this.expression=a;this.expensiveChecks=c;T(e,d.$filter);var f,g;if(f=pd(e))g=this.recurse(f);f=nd(e.body);var h;f&&(h=[],m(f,function(a,c){var e=d.recurse(a);
|
|
||||||
a.input=e;h.push(e);a.watchId=c}));var l=[];m(e.body,function(a){l.push(d.recurse(a.expression))});f=0===e.body.length?function(){}:1===e.body.length?l[0]:function(a,c){var d;m(l,function(e){d=e(a,c)});return d};g&&(f.assign=function(a,c,d){return g(a,d,c)});h&&(f.inputs=h);f.literal=qd(e);f.constant=e.constant;return f},recurse:function(a,c,d){var e,f,g=this,h;if(a.input)return this.inputs(a.input,a.watchId);switch(a.type){case q.Literal:return this.value(a.value,c);case q.UnaryExpression:return f=
|
|
||||||
this.recurse(a.argument),this["unary"+a.operator](f,c);case q.BinaryExpression:return e=this.recurse(a.left),f=this.recurse(a.right),this["binary"+a.operator](e,f,c);case q.LogicalExpression:return e=this.recurse(a.left),f=this.recurse(a.right),this["binary"+a.operator](e,f,c);case q.ConditionalExpression:return this["ternary?:"](this.recurse(a.test),this.recurse(a.alternate),this.recurse(a.consequent),c);case q.Identifier:return Ca(a.name,g.expression),g.identifier(a.name,g.expensiveChecks||Fb(a.name),
|
|
||||||
c,d,g.expression);case q.MemberExpression:return e=this.recurse(a.object,!1,!!d),a.computed||(Ca(a.property.name,g.expression),f=a.property.name),a.computed&&(f=this.recurse(a.property)),a.computed?this.computedMember(e,f,c,d,g.expression):this.nonComputedMember(e,f,g.expensiveChecks,c,d,g.expression);case q.CallExpression:return h=[],m(a.arguments,function(a){h.push(g.recurse(a))}),a.filter&&(f=this.$filter(a.callee.name)),a.filter||(f=this.recurse(a.callee,!0)),a.filter?function(a,d,e,g){for(var m=
|
|
||||||
[],q=0;q<h.length;++q)m.push(h[q](a,d,e,g));a=f.apply(t,m,g);return c?{context:t,name:t,value:a}:a}:function(a,d,e,r){var m=f(a,d,e,r),q;if(null!=m.value){oa(m.context,g.expression);ld(m.value,g.expression);q=[];for(var t=0;t<h.length;++t)q.push(oa(h[t](a,d,e,r),g.expression));q=oa(m.value.apply(m.context,q),g.expression)}return c?{value:q}:q};case q.AssignmentExpression:return e=this.recurse(a.left,!0,1),f=this.recurse(a.right),function(a,d,h,r){var m=e(a,d,h,r);a=f(a,d,h,r);oa(m.value,g.expression);
|
|
||||||
m.context[m.name]=a;return c?{value:a}:a};case q.ArrayExpression:return h=[],m(a.elements,function(a){h.push(g.recurse(a))}),function(a,d,e,f){for(var g=[],m=0;m<h.length;++m)g.push(h[m](a,d,e,f));return c?{value:g}:g};case q.ObjectExpression:return h=[],m(a.properties,function(a){h.push({key:a.key.type===q.Identifier?a.key.name:""+a.key.value,value:g.recurse(a.value)})}),function(a,d,e,f){for(var g={},m=0;m<h.length;++m)g[h[m].key]=h[m].value(a,d,e,f);return c?{value:g}:g};case q.ThisExpression:return function(a){return c?
|
|
||||||
{value:a}:a};case q.NGValueParameter:return function(a,d,e,f){return c?{value:e}:e}}},"unary+":function(a,c){return function(d,e,f,g){d=a(d,e,f,g);d=w(d)?+d:0;return c?{value:d}:d}},"unary-":function(a,c){return function(d,e,f,g){d=a(d,e,f,g);d=w(d)?-d:0;return c?{value:d}:d}},"unary!":function(a,c){return function(d,e,f,g){d=!a(d,e,f,g);return c?{value:d}:d}},"binary+":function(a,c,d){return function(e,f,g,h){var l=a(e,f,g,h);e=c(e,f,g,h);l=md(l,e);return d?{value:l}:l}},"binary-":function(a,c,d){return function(e,
|
|
||||||
f,g,h){var l=a(e,f,g,h);e=c(e,f,g,h);l=(w(l)?l:0)-(w(e)?e:0);return d?{value:l}:l}},"binary*":function(a,c,d){return function(e,f,g,h){e=a(e,f,g,h)*c(e,f,g,h);return d?{value:e}:e}},"binary/":function(a,c,d){return function(e,f,g,h){e=a(e,f,g,h)/c(e,f,g,h);return d?{value:e}:e}},"binary%":function(a,c,d){return function(e,f,g,h){e=a(e,f,g,h)%c(e,f,g,h);return d?{value:e}:e}},"binary===":function(a,c,d){return function(e,f,g,h){e=a(e,f,g,h)===c(e,f,g,h);return d?{value:e}:e}},"binary!==":function(a,
|
|
||||||
c,d){return function(e,f,g,h){e=a(e,f,g,h)!==c(e,f,g,h);return d?{value:e}:e}},"binary==":function(a,c,d){return function(e,f,g,h){e=a(e,f,g,h)==c(e,f,g,h);return d?{value:e}:e}},"binary!=":function(a,c,d){return function(e,f,g,h){e=a(e,f,g,h)!=c(e,f,g,h);return d?{value:e}:e}},"binary<":function(a,c,d){return function(e,f,g,h){e=a(e,f,g,h)<c(e,f,g,h);return d?{value:e}:e}},"binary>":function(a,c,d){return function(e,f,g,h){e=a(e,f,g,h)>c(e,f,g,h);return d?{value:e}:e}},"binary<=":function(a,c,d){return function(e,
|
|
||||||
f,g,h){e=a(e,f,g,h)<=c(e,f,g,h);return d?{value:e}:e}},"binary>=":function(a,c,d){return function(e,f,g,h){e=a(e,f,g,h)>=c(e,f,g,h);return d?{value:e}:e}},"binary&&":function(a,c,d){return function(e,f,g,h){e=a(e,f,g,h)&&c(e,f,g,h);return d?{value:e}:e}},"binary||":function(a,c,d){return function(e,f,g,h){e=a(e,f,g,h)||c(e,f,g,h);return d?{value:e}:e}},"ternary?:":function(a,c,d,e){return function(f,g,h,l){f=a(f,g,h,l)?c(f,g,h,l):d(f,g,h,l);return e?{value:f}:f}},value:function(a,c){return function(){return c?
|
|
||||||
{context:t,name:t,value:a}:a}},identifier:function(a,c,d,e,f){return function(g,h,l,k){g=h&&a in h?h:g;e&&1!==e&&g&&!g[a]&&(g[a]={});h=g?g[a]:t;c&&oa(h,f);return d?{context:g,name:a,value:h}:h}},computedMember:function(a,c,d,e,f){return function(g,h,l,k){var n=a(g,h,l,k),m,s;null!=n&&(m=c(g,h,l,k),Ca(m,f),e&&1!==e&&n&&!n[m]&&(n[m]={}),s=n[m],oa(s,f));return d?{context:n,name:m,value:s}:s}},nonComputedMember:function(a,c,d,e,f,g){return function(h,l,k,n){h=a(h,l,k,n);f&&1!==f&&h&&!h[c]&&(h[c]={});
|
|
||||||
l=null!=h?h[c]:t;(d||Fb(c))&&oa(l,g);return e?{context:h,name:c,value:l}:l}},inputs:function(a,c){return function(d,e,f,g){return g?g[c]:a(d,e,f)}}};var hc=function(a,c,d){this.lexer=a;this.$filter=c;this.options=d;this.ast=new q(this.lexer);this.astCompiler=d.csp?new sd(this.ast,c):new rd(this.ast,c)};hc.prototype={constructor:hc,parse:function(a){return this.astCompiler.compile(a,this.options.expensiveChecks)}};ga();ga();var Zf=Object.prototype.valueOf,Da=J("$sce"),pa={HTML:"html",CSS:"css",URL:"url",
|
|
||||||
RESOURCE_URL:"resourceUrl",JS:"js"},ea=J("$compile"),X=U.createElement("a"),wd=Ba(O.location.href);xd.$inject=["$document"];Lc.$inject=["$provide"];yd.$inject=["$locale"];Ad.$inject=["$locale"];var Dd=".",ig={yyyy:Y("FullYear",4),yy:Y("FullYear",2,0,!0),y:Y("FullYear",1),MMMM:Hb("Month"),MMM:Hb("Month",!0),MM:Y("Month",2,1),M:Y("Month",1,1),dd:Y("Date",2),d:Y("Date",1),HH:Y("Hours",2),H:Y("Hours",1),hh:Y("Hours",2,-12),h:Y("Hours",1,-12),mm:Y("Minutes",2),m:Y("Minutes",1),ss:Y("Seconds",2),s:Y("Seconds",
|
|
||||||
1),sss:Y("Milliseconds",3),EEEE:Hb("Day"),EEE:Hb("Day",!0),a:function(a,c){return 12>a.getHours()?c.AMPMS[0]:c.AMPMS[1]},Z:function(a,c,d){a=-1*d;return a=(0<=a?"+":"")+(Gb(Math[0<a?"floor":"ceil"](a/60),2)+Gb(Math.abs(a%60),2))},ww:Fd(2),w:Fd(1),G:jc,GG:jc,GGG:jc,GGGG:function(a,c){return 0>=a.getFullYear()?c.ERANAMES[0]:c.ERANAMES[1]}},hg=/((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,gg=/^\-?\d+$/;zd.$inject=["$locale"];var dg=ra(M),eg=ra(rb);Bd.$inject=
|
|
||||||
["$parse"];var ie=ra({restrict:"E",compile:function(a,c){if(!c.href&&!c.xlinkHref)return function(a,c){if("a"===c[0].nodeName.toLowerCase()){var f="[object SVGAnimatedString]"===sa.call(c.prop("href"))?"xlink:href":"href";c.on("click",function(a){c.attr(f)||a.preventDefault()})}}}}),sb={};m(Ab,function(a,c){function d(a,d,f){a.$watch(f[e],function(a){f.$set(c,!!a)})}if("multiple"!=a){var e=wa("ng-"+c),f=d;"checked"===a&&(f=function(a,c,f){f.ngModel!==f[e]&&d(a,c,f)});sb[e]=function(){return{restrict:"A",
|
|
||||||
priority:100,link:f}}}});m(Uc,function(a,c){sb[c]=function(){return{priority:100,link:function(a,e,f){if("ngPattern"===c&&"/"==f.ngPattern.charAt(0)&&(e=f.ngPattern.match(kg))){f.$set("ngPattern",new RegExp(e[1],e[2]));return}a.$watch(f[c],function(a){f.$set(c,a)})}}}});m(["src","srcset","href"],function(a){var c=wa("ng-"+a);sb[c]=function(){return{priority:99,link:function(d,e,f){var g=a,h=a;"href"===a&&"[object SVGAnimatedString]"===sa.call(e.prop("href"))&&(h="xlinkHref",f.$attr[h]="xlink:href",
|
|
||||||
g=null);f.$observe(c,function(c){c?(f.$set(h,c),Ua&&g&&e.prop(g,f[h])):"href"===a&&f.$set(h,null)})}}}});var Ib={$addControl:v,$$renameControl:function(a,c){a.$name=c},$removeControl:v,$setValidity:v,$setDirty:v,$setPristine:v,$setSubmitted:v};Gd.$inject=["$element","$attrs","$scope","$animate","$interpolate"];var Od=function(a){return["$timeout",function(c){return{name:"form",restrict:a?"EAC":"E",controller:Gd,compile:function(d,e){d.addClass(Va).addClass(mb);var f=e.name?"name":a&&e.ngForm?"ngForm":
|
|
||||||
!1;return{pre:function(a,d,e,k){if(!("action"in e)){var n=function(c){a.$apply(function(){k.$commitViewValue();k.$setSubmitted()});c.preventDefault()};d[0].addEventListener("submit",n,!1);d.on("$destroy",function(){c(function(){d[0].removeEventListener("submit",n,!1)},0,!1)})}var m=k.$$parentForm;f&&(Eb(a,k.$name,k,k.$name),e.$observe(f,function(c){k.$name!==c&&(Eb(a,k.$name,t,k.$name),m.$$renameControl(k,c),Eb(a,k.$name,k,k.$name))}));d.on("$destroy",function(){m.$removeControl(k);f&&Eb(a,e[f],t,
|
|
||||||
k.$name);P(k,Ib)})}}}}}]},je=Od(),we=Od(!0),jg=/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/,sg=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,tg=/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,ug=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/,Pd=/^(\d{4})-(\d{2})-(\d{2})$/,Qd=/^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,mc=/^(\d{4})-W(\d\d)$/,Rd=/^(\d{4})-(\d\d)$/,
|
|
||||||
Sd=/^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,Td={text:function(a,c,d,e,f,g){kb(a,c,d,e,f,g);kc(e)},date:lb("date",Pd,Kb(Pd,["yyyy","MM","dd"]),"yyyy-MM-dd"),"datetime-local":lb("datetimelocal",Qd,Kb(Qd,"yyyy MM dd HH mm ss sss".split(" ")),"yyyy-MM-ddTHH:mm:ss.sss"),time:lb("time",Sd,Kb(Sd,["HH","mm","ss","sss"]),"HH:mm:ss.sss"),week:lb("week",mc,function(a,c){if(aa(a))return a;if(L(a)){mc.lastIndex=0;var d=mc.exec(a);if(d){var e=+d[1],f=+d[2],g=d=0,h=0,l=0,k=Ed(e),f=7*(f-1);c&&(d=c.getHours(),g=
|
|
||||||
c.getMinutes(),h=c.getSeconds(),l=c.getMilliseconds());return new Date(e,0,k.getDate()+f,d,g,h,l)}}return NaN},"yyyy-Www"),month:lb("month",Rd,Kb(Rd,["yyyy","MM"]),"yyyy-MM"),number:function(a,c,d,e,f,g){Id(a,c,d,e);kb(a,c,d,e,f,g);e.$$parserName="number";e.$parsers.push(function(a){return e.$isEmpty(a)?null:ug.test(a)?parseFloat(a):t});e.$formatters.push(function(a){if(!e.$isEmpty(a)){if(!V(a))throw Lb("numfmt",a);a=a.toString()}return a});if(w(d.min)||d.ngMin){var h;e.$validators.min=function(a){return e.$isEmpty(a)||
|
|
||||||
A(h)||a>=h};d.$observe("min",function(a){w(a)&&!V(a)&&(a=parseFloat(a,10));h=V(a)&&!isNaN(a)?a:t;e.$validate()})}if(w(d.max)||d.ngMax){var l;e.$validators.max=function(a){return e.$isEmpty(a)||A(l)||a<=l};d.$observe("max",function(a){w(a)&&!V(a)&&(a=parseFloat(a,10));l=V(a)&&!isNaN(a)?a:t;e.$validate()})}},url:function(a,c,d,e,f,g){kb(a,c,d,e,f,g);kc(e);e.$$parserName="url";e.$validators.url=function(a,c){var d=a||c;return e.$isEmpty(d)||sg.test(d)}},email:function(a,c,d,e,f,g){kb(a,c,d,e,f,g);kc(e);
|
|
||||||
e.$$parserName="email";e.$validators.email=function(a,c){var d=a||c;return e.$isEmpty(d)||tg.test(d)}},radio:function(a,c,d,e){A(d.name)&&c.attr("name",++nb);c.on("click",function(a){c[0].checked&&e.$setViewValue(d.value,a&&a.type)});e.$render=function(){c[0].checked=d.value==e.$viewValue};d.$observe("value",e.$render)},checkbox:function(a,c,d,e,f,g,h,l){var k=Jd(l,a,"ngTrueValue",d.ngTrueValue,!0),n=Jd(l,a,"ngFalseValue",d.ngFalseValue,!1);c.on("click",function(a){e.$setViewValue(c[0].checked,a&&
|
|
||||||
a.type)});e.$render=function(){c[0].checked=e.$viewValue};e.$isEmpty=function(a){return!1===a};e.$formatters.push(function(a){return ka(a,k)});e.$parsers.push(function(a){return a?k:n})},hidden:v,button:v,submit:v,reset:v,file:v},Fc=["$browser","$sniffer","$filter","$parse",function(a,c,d,e){return{restrict:"E",require:["?ngModel"],link:{pre:function(f,g,h,l){l[0]&&(Td[M(h.type)]||Td.text)(f,g,h,l[0],c,a,d,e)}}}}],vg=/^(true|false|\d+)$/,Oe=function(){return{restrict:"A",priority:100,compile:function(a,
|
|
||||||
c){return vg.test(c.ngValue)?function(a,c,f){f.$set("value",a.$eval(f.ngValue))}:function(a,c,f){a.$watch(f.ngValue,function(a){f.$set("value",a)})}}}},oe=["$compile",function(a){return{restrict:"AC",compile:function(c){a.$$addBindingClass(c);return function(c,e,f){a.$$addBindingInfo(e,f.ngBind);e=e[0];c.$watch(f.ngBind,function(a){e.textContent=a===t?"":a})}}}}],qe=["$interpolate","$compile",function(a,c){return{compile:function(d){c.$$addBindingClass(d);return function(d,f,g){d=a(f.attr(g.$attr.ngBindTemplate));
|
|
||||||
c.$$addBindingInfo(f,d.expressions);f=f[0];g.$observe("ngBindTemplate",function(a){f.textContent=a===t?"":a})}}}}],pe=["$sce","$parse","$compile",function(a,c,d){return{restrict:"A",compile:function(e,f){var g=c(f.ngBindHtml),h=c(f.ngBindHtml,function(a){return(a||"").toString()});d.$$addBindingClass(e);return function(c,e,f){d.$$addBindingInfo(e,f.ngBindHtml);c.$watch(h,function(){e.html(a.getTrustedHtml(g(c))||"")})}}}}],Ne=ra({restrict:"A",require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),
|
|
||||||
re=lc("",!0),te=lc("Odd",0),se=lc("Even",1),ue=Ma({compile:function(a,c){c.$set("ngCloak",t);a.removeClass("ng-cloak")}}),ve=[function(){return{restrict:"A",scope:!0,controller:"@",priority:500}}],Kc={},wg={blur:!0,focus:!0};m("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),function(a){var c=wa("ng-"+a);Kc[c]=["$parse","$rootScope",function(d,e){return{restrict:"A",compile:function(f,g){var h=
|
|
||||||
d(g[c],null,!0);return function(c,d){d.on(a,function(d){var f=function(){h(c,{$event:d})};wg[a]&&e.$$phase?c.$evalAsync(f):c.$apply(f)})}}}}]});var ye=["$animate",function(a){return{multiElement:!0,transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(c,d,e,f,g){var h,l,k;c.$watch(e.ngIf,function(c){c?l||g(function(c,f){l=f;c[c.length++]=U.createComment(" end ngIf: "+e.ngIf+" ");h={clone:c};a.enter(c,d.parent(),d)}):(k&&(k.remove(),k=null),l&&(l.$destroy(),l=null),h&&(k=
|
|
||||||
qb(h.clone),a.leave(k).then(function(){k=null}),h=null))})}}}],ze=["$templateRequest","$anchorScroll","$animate",function(a,c,d){return{restrict:"ECA",priority:400,terminal:!0,transclude:"element",controller:ca.noop,compile:function(e,f){var g=f.ngInclude||f.src,h=f.onload||"",l=f.autoscroll;return function(e,f,m,s,q){var t=0,F,u,p,v=function(){u&&(u.remove(),u=null);F&&(F.$destroy(),F=null);p&&(d.leave(p).then(function(){u=null}),u=p,p=null)};e.$watch(g,function(g){var m=function(){!w(l)||l&&!e.$eval(l)||
|
|
||||||
c()},r=++t;g?(a(g,!0).then(function(a){if(r===t){var c=e.$new();s.template=a;a=q(c,function(a){v();d.enter(a,null,f).then(m)});F=c;p=a;F.$emit("$includeContentLoaded",g);e.$eval(h)}},function(){r===t&&(v(),e.$emit("$includeContentError",g))}),e.$emit("$includeContentRequested",g)):(v(),s.template=null)})}}}}],Qe=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(c,d,e,f){/SVG/.test(d[0].toString())?(d.empty(),a(Nc(f.template,U).childNodes)(c,function(a){d.append(a)},
|
|
||||||
{futureParentElement:d})):(d.html(f.template),a(d.contents())(c))}}}],Ae=Ma({priority:450,compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),Me=function(){return{restrict:"A",priority:100,require:"ngModel",link:function(a,c,d,e){var f=c.attr(d.$attr.ngList)||", ",g="false"!==d.ngTrim,h=g?R(f):f;e.$parsers.push(function(a){if(!A(a)){var c=[];a&&m(a.split(h),function(a){a&&c.push(g?R(a):a)});return c}});e.$formatters.push(function(a){return G(a)?a.join(f):t});e.$isEmpty=function(a){return!a||
|
|
||||||
!a.length}}}},mb="ng-valid",Kd="ng-invalid",Va="ng-pristine",Jb="ng-dirty",Md="ng-pending",Lb=new J("ngModel"),xg=["$scope","$exceptionHandler","$attrs","$element","$parse","$animate","$timeout","$rootScope","$q","$interpolate",function(a,c,d,e,f,g,h,l,k,n){this.$modelValue=this.$viewValue=Number.NaN;this.$$rawModelValue=t;this.$validators={};this.$asyncValidators={};this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$untouched=!0;this.$touched=!1;this.$pristine=!0;this.$dirty=
|
|
||||||
!1;this.$valid=!0;this.$invalid=!1;this.$error={};this.$$success={};this.$pending=t;this.$name=n(d.name||"",!1)(a);var r=f(d.ngModel),s=r.assign,q=r,C=s,F=null,u,p=this;this.$$setOptions=function(a){if((p.$options=a)&&a.getterSetter){var c=f(d.ngModel+"()"),g=f(d.ngModel+"($$$p)");q=function(a){var d=r(a);z(d)&&(d=c(a));return d};C=function(a,c){z(r(a))?g(a,{$$$p:p.$modelValue}):s(a,p.$modelValue)}}else if(!r.assign)throw Lb("nonassign",d.ngModel,ua(e));};this.$render=v;this.$isEmpty=function(a){return A(a)||
|
|
||||||
""===a||null===a||a!==a};var K=e.inheritedData("$formController")||Ib,y=0;Hd({ctrl:this,$element:e,set:function(a,c){a[c]=!0},unset:function(a,c){delete a[c]},parentForm:K,$animate:g});this.$setPristine=function(){p.$dirty=!1;p.$pristine=!0;g.removeClass(e,Jb);g.addClass(e,Va)};this.$setDirty=function(){p.$dirty=!0;p.$pristine=!1;g.removeClass(e,Va);g.addClass(e,Jb);K.$setDirty()};this.$setUntouched=function(){p.$touched=!1;p.$untouched=!0;g.setClass(e,"ng-untouched","ng-touched")};this.$setTouched=
|
|
||||||
function(){p.$touched=!0;p.$untouched=!1;g.setClass(e,"ng-touched","ng-untouched")};this.$rollbackViewValue=function(){h.cancel(F);p.$viewValue=p.$$lastCommittedViewValue;p.$render()};this.$validate=function(){if(!V(p.$modelValue)||!isNaN(p.$modelValue)){var a=p.$$rawModelValue,c=p.$valid,d=p.$modelValue,e=p.$options&&p.$options.allowInvalid;p.$$runValidators(a,p.$$lastCommittedViewValue,function(f){e||c===f||(p.$modelValue=f?a:t,p.$modelValue!==d&&p.$$writeModelToScope())})}};this.$$runValidators=
|
|
||||||
function(a,c,d){function e(){var d=!0;m(p.$validators,function(e,f){var h=e(a,c);d=d&&h;g(f,h)});return d?!0:(m(p.$asyncValidators,function(a,c){g(c,null)}),!1)}function f(){var d=[],e=!0;m(p.$asyncValidators,function(f,h){var k=f(a,c);if(!k||!z(k.then))throw Lb("$asyncValidators",k);g(h,t);d.push(k.then(function(){g(h,!0)},function(a){e=!1;g(h,!1)}))});d.length?k.all(d).then(function(){h(e)},v):h(!0)}function g(a,c){l===y&&p.$setValidity(a,c)}function h(a){l===y&&d(a)}y++;var l=y;(function(){var a=
|
|
||||||
p.$$parserName||"parse";if(u===t)g(a,null);else return u||(m(p.$validators,function(a,c){g(c,null)}),m(p.$asyncValidators,function(a,c){g(c,null)})),g(a,u),u;return!0})()?e()?f():h(!1):h(!1)};this.$commitViewValue=function(){var a=p.$viewValue;h.cancel(F);if(p.$$lastCommittedViewValue!==a||""===a&&p.$$hasNativeValidators)p.$$lastCommittedViewValue=a,p.$pristine&&this.$setDirty(),this.$$parseAndValidate()};this.$$parseAndValidate=function(){var c=p.$$lastCommittedViewValue;if(u=A(c)?t:!0)for(var d=
|
|
||||||
0;d<p.$parsers.length;d++)if(c=p.$parsers[d](c),A(c)){u=!1;break}V(p.$modelValue)&&isNaN(p.$modelValue)&&(p.$modelValue=q(a));var e=p.$modelValue,f=p.$options&&p.$options.allowInvalid;p.$$rawModelValue=c;f&&(p.$modelValue=c,p.$modelValue!==e&&p.$$writeModelToScope());p.$$runValidators(c,p.$$lastCommittedViewValue,function(a){f||(p.$modelValue=a?c:t,p.$modelValue!==e&&p.$$writeModelToScope())})};this.$$writeModelToScope=function(){C(a,p.$modelValue);m(p.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}})};
|
|
||||||
this.$setViewValue=function(a,c){p.$viewValue=a;p.$options&&!p.$options.updateOnDefault||p.$$debounceViewValueCommit(c)};this.$$debounceViewValueCommit=function(c){var d=0,e=p.$options;e&&w(e.debounce)&&(e=e.debounce,V(e)?d=e:V(e[c])?d=e[c]:V(e["default"])&&(d=e["default"]));h.cancel(F);d?F=h(function(){p.$commitViewValue()},d):l.$$phase?p.$commitViewValue():a.$apply(function(){p.$commitViewValue()})};a.$watch(function(){var c=q(a);if(c!==p.$modelValue&&(p.$modelValue===p.$modelValue||c===c)){p.$modelValue=
|
|
||||||
p.$$rawModelValue=c;u=t;for(var d=p.$formatters,e=d.length,f=c;e--;)f=d[e](f);p.$viewValue!==f&&(p.$viewValue=p.$$lastCommittedViewValue=f,p.$render(),p.$$runValidators(c,f,v))}return c})}],Le=["$rootScope",function(a){return{restrict:"A",require:["ngModel","^?form","^?ngModelOptions"],controller:xg,priority:1,compile:function(c){c.addClass(Va).addClass("ng-untouched").addClass(mb);return{pre:function(a,c,f,g){var h=g[0],l=g[1]||Ib;h.$$setOptions(g[2]&&g[2].$options);l.$addControl(h);f.$observe("name",
|
|
||||||
function(a){h.$name!==a&&l.$$renameControl(h,a)});a.$on("$destroy",function(){l.$removeControl(h)})},post:function(c,e,f,g){var h=g[0];if(h.$options&&h.$options.updateOn)e.on(h.$options.updateOn,function(a){h.$$debounceViewValueCommit(a&&a.type)});e.on("blur",function(e){h.$touched||(a.$$phase?c.$evalAsync(h.$setTouched):c.$apply(h.$setTouched))})}}}}}],yg=/(\s+|^)default(\s+|$)/,Pe=function(){return{restrict:"A",controller:["$scope","$attrs",function(a,c){var d=this;this.$options=fa(a.$eval(c.ngModelOptions));
|
|
||||||
this.$options.updateOn!==t?(this.$options.updateOnDefault=!1,this.$options.updateOn=R(this.$options.updateOn.replace(yg,function(){d.$options.updateOnDefault=!0;return" "}))):this.$options.updateOnDefault=!0}]}},Be=Ma({terminal:!0,priority:1E3}),zg=J("ngOptions"),Ag=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/,
|
|
||||||
Je=["$compile","$parse",function(a,c){function d(a,d,e){function f(a,c,d,e,g){this.selectValue=a;this.viewValue=c;this.label=d;this.group=e;this.disabled=g}function n(a){var c;if(!q&&Ea(a))c=a;else{c=[];for(var d in a)a.hasOwnProperty(d)&&"$"!==d.charAt(0)&&c.push(d)}return c}var m=a.match(Ag);if(!m)throw zg("iexp",a,ua(d));var s=m[5]||m[7],q=m[6];a=/ as /.test(m[0])&&m[1];var t=m[9];d=c(m[2]?m[1]:s);var v=a&&c(a)||d,u=t&&c(t),p=t?function(a,c){return u(e,c)}:function(a){return Ga(a)},w=function(a,
|
|
||||||
c){return p(a,z(a,c))},y=c(m[2]||m[1]),A=c(m[3]||""),B=c(m[4]||""),N=c(m[8]),D={},z=q?function(a,c){D[q]=c;D[s]=a;return D}:function(a){D[s]=a;return D};return{trackBy:t,getTrackByValue:w,getWatchables:c(N,function(a){var c=[];a=a||[];for(var d=n(a),f=d.length,g=0;g<f;g++){var h=a===d?g:d[g],k=z(a[h],h),h=p(a[h],k);c.push(h);if(m[2]||m[1])h=y(e,k),c.push(h);m[4]&&(k=B(e,k),c.push(k))}return c}),getOptions:function(){for(var a=[],c={},d=N(e)||[],g=n(d),h=g.length,m=0;m<h;m++){var r=d===g?m:g[m],s=
|
|
||||||
z(d[r],r),q=v(e,s),r=p(q,s),u=y(e,s),x=A(e,s),s=B(e,s),q=new f(r,q,u,x,s);a.push(q);c[r]=q}return{items:a,selectValueMap:c,getOptionFromViewValue:function(a){return c[w(a)]},getViewValueFromOption:function(a){return t?ca.copy(a.viewValue):a.viewValue}}}}}var e=U.createElement("option"),f=U.createElement("optgroup");return{restrict:"A",terminal:!0,require:["select","?ngModel"],link:function(c,h,l,k){function n(a,c){a.element=c;c.disabled=a.disabled;a.value!==c.value&&(c.value=a.selectValue);a.label!==
|
|
||||||
c.label&&(c.label=a.label,c.textContent=a.label)}function r(a,c,d,e){c&&M(c.nodeName)===d?d=c:(d=e.cloneNode(!1),c?a.insertBefore(d,c):a.appendChild(d));return d}function s(a){for(var c;a;)c=a.nextSibling,Xb(a),a=c}function q(a){var c=p&&p[0],d=N&&N[0];if(c||d)for(;a&&(a===c||a===d);)a=a.nextSibling;return a}function t(){var a=D&&u.readValue();D=z.getOptions();var c={},d=h[0].firstChild;B&&h.prepend(p);d=q(d);D.items.forEach(function(a){var g,k;a.group?(g=c[a.group],g||(g=r(h[0],d,"optgroup",f),d=
|
|
||||||
g.nextSibling,g.label=a.group,g=c[a.group]={groupElement:g,currentOptionElement:g.firstChild}),k=r(g.groupElement,g.currentOptionElement,"option",e),n(a,k),g.currentOptionElement=k.nextSibling):(k=r(h[0],d,"option",e),n(a,k),d=k.nextSibling)});Object.keys(c).forEach(function(a){s(c[a].currentOptionElement)});s(d);v.$render();if(!v.$isEmpty(a)){var g=u.readValue();(z.trackBy?ka(a,g):a===g)||(v.$setViewValue(g),v.$render())}}var v=k[1];if(v){var u=k[0];k=l.multiple;for(var p,w=0,A=h.children(),I=A.length;w<
|
|
||||||
I;w++)if(""===A[w].value){p=A.eq(w);break}var B=!!p,N=y(e.cloneNode(!1));N.val("?");var D,z=d(l.ngOptions,h,c);k?(v.$isEmpty=function(a){return!a||0===a.length},u.writeValue=function(a){D.items.forEach(function(a){a.element.selected=!1});a&&a.forEach(function(a){(a=D.getOptionFromViewValue(a))&&!a.disabled&&(a.element.selected=!0)})},u.readValue=function(){var a=h.val()||[],c=[];m(a,function(a){a=D.selectValueMap[a];a.disabled||c.push(D.getViewValueFromOption(a))});return c},z.trackBy&&c.$watchCollection(function(){if(G(v.$viewValue))return v.$viewValue.map(function(a){return z.getTrackByValue(a)})},
|
|
||||||
function(){v.$render()})):(u.writeValue=function(a){var c=D.getOptionFromViewValue(a);c&&!c.disabled?h[0].value!==c.selectValue&&(N.remove(),B||p.remove(),h[0].value=c.selectValue,c.element.selected=!0,c.element.setAttribute("selected","selected")):null===a||B?(N.remove(),B||h.prepend(p),h.val(""),p.prop("selected",!0),p.attr("selected",!0)):(B||p.remove(),h.prepend(N),h.val("?"),N.prop("selected",!0),N.attr("selected",!0))},u.readValue=function(){var a=D.selectValueMap[h.val()];return a&&!a.disabled?
|
|
||||||
(B||p.remove(),N.remove(),D.getViewValueFromOption(a)):null},z.trackBy&&c.$watch(function(){return z.getTrackByValue(v.$viewValue)},function(){v.$render()}));B?(p.remove(),a(p)(c),p.removeClass("ng-scope")):p=y(e.cloneNode(!1));t();c.$watchCollection(z.getWatchables,t)}}}}],Ce=["$locale","$interpolate","$log",function(a,c,d){var e=/{}/g,f=/^when(Minus)?(.+)$/;return{link:function(g,h,l){function k(a){h.text(a||"")}var n=l.count,r=l.$attr.when&&h.attr(l.$attr.when),s=l.offset||0,q=g.$eval(r)||{},t=
|
|
||||||
{},w=c.startSymbol(),u=c.endSymbol(),p=w+n+"-"+s+u,y=ca.noop,z;m(l,function(a,c){var d=f.exec(c);d&&(d=(d[1]?"-":"")+M(d[2]),q[d]=h.attr(l.$attr[c]))});m(q,function(a,d){t[d]=c(a.replace(e,p))});g.$watch(n,function(c){var e=parseFloat(c),f=isNaN(e);f||e in q||(e=a.pluralCat(e-s));e===z||f&&V(z)&&isNaN(z)||(y(),f=t[e],A(f)?(null!=c&&d.debug("ngPluralize: no rule defined for '"+e+"' in "+r),y=v,k()):y=g.$watch(f,k),z=e)})}}}],De=["$parse","$animate",function(a,c){var d=J("ngRepeat"),e=function(a,c,
|
|
||||||
d,e,k,m,r){a[d]=e;k&&(a[k]=m);a.$index=c;a.$first=0===c;a.$last=c===r-1;a.$middle=!(a.$first||a.$last);a.$odd=!(a.$even=0===(c&1))};return{restrict:"A",multiElement:!0,transclude:"element",priority:1E3,terminal:!0,$$tlb:!0,compile:function(f,g){var h=g.ngRepeat,l=U.createComment(" end ngRepeat: "+h+" "),k=h.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);if(!k)throw d("iexp",h);var n=k[1],r=k[2],s=k[3],q=k[4],k=n.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
|
|
||||||
if(!k)throw d("iidexp",n);var v=k[3]||k[1],w=k[2];if(s&&(!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(s)||/^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(s)))throw d("badident",s);var u,p,z,A,I={$id:Ga};q?u=a(q):(z=function(a,c){return Ga(c)},A=function(a){return a});return function(a,f,g,k,n){u&&(p=function(c,d,e){w&&(I[w]=c);I[v]=d;I.$index=e;return u(a,I)});var q=ga();a.$watchCollection(r,function(g){var k,r,u=f[0],x,D=ga(),I,H,L,G,M,J,O;s&&(a[s]=g);if(Ea(g))M=
|
|
||||||
g,r=p||z;else for(O in r=p||A,M=[],g)g.hasOwnProperty(O)&&"$"!==O.charAt(0)&&M.push(O);I=M.length;O=Array(I);for(k=0;k<I;k++)if(H=g===M?k:M[k],L=g[H],G=r(H,L,k),q[G])J=q[G],delete q[G],D[G]=J,O[k]=J;else{if(D[G])throw m(O,function(a){a&&a.scope&&(q[a.id]=a)}),d("dupes",h,G,L);O[k]={id:G,scope:t,clone:t};D[G]=!0}for(x in q){J=q[x];G=qb(J.clone);c.leave(G);if(G[0].parentNode)for(k=0,r=G.length;k<r;k++)G[k].$$NG_REMOVED=!0;J.scope.$destroy()}for(k=0;k<I;k++)if(H=g===M?k:M[k],L=g[H],J=O[k],J.scope){x=
|
|
||||||
u;do x=x.nextSibling;while(x&&x.$$NG_REMOVED);J.clone[0]!=x&&c.move(qb(J.clone),null,y(u));u=J.clone[J.clone.length-1];e(J.scope,k,v,L,w,H,I)}else n(function(a,d){J.scope=d;var f=l.cloneNode(!1);a[a.length++]=f;c.enter(a,null,y(u));u=f;J.clone=a;D[J.id]=J;e(J.scope,k,v,L,w,H,I)});q=D})}}}}],Ee=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(c,d,e){c.$watch(e.ngShow,function(c){a[c?"removeClass":"addClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],xe=["$animate",
|
|
||||||
function(a){return{restrict:"A",multiElement:!0,link:function(c,d,e){c.$watch(e.ngHide,function(c){a[c?"addClass":"removeClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],Fe=Ma(function(a,c,d){a.$watch(d.ngStyle,function(a,d){d&&a!==d&&m(d,function(a,d){c.css(d,"")});a&&c.css(a)},!0)}),Ge=["$animate",function(a){return{require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(c,d,e,f){var g=[],h=[],l=[],k=[],n=function(a,c){return function(){a.splice(c,1)}};c.$watch(e.ngSwitch||
|
|
||||||
e.on,function(c){var d,e;d=0;for(e=l.length;d<e;++d)a.cancel(l[d]);d=l.length=0;for(e=k.length;d<e;++d){var q=qb(h[d].clone);k[d].$destroy();(l[d]=a.leave(q)).then(n(l,d))}h.length=0;k.length=0;(g=f.cases["!"+c]||f.cases["?"])&&m(g,function(c){c.transclude(function(d,e){k.push(e);var f=c.element;d[d.length++]=U.createComment(" end ngSwitchWhen: ");h.push({clone:d});a.enter(d,f.parent(),f)})})})}}}],He=Ma({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,c,d,e,
|
|
||||||
f){e.cases["!"+d.ngSwitchWhen]=e.cases["!"+d.ngSwitchWhen]||[];e.cases["!"+d.ngSwitchWhen].push({transclude:f,element:c})}}),Ie=Ma({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,c,d,e,f){e.cases["?"]=e.cases["?"]||[];e.cases["?"].push({transclude:f,element:c})}}),Ke=Ma({restrict:"EAC",link:function(a,c,d,e,f){if(!f)throw J("ngTransclude")("orphan",ua(c));f(function(a){c.empty();c.append(a)})}}),ke=["$templateCache",function(a){return{restrict:"E",terminal:!0,
|
|
||||||
compile:function(c,d){"text/ng-template"==d.type&&a.put(d.id,c[0].text)}}}],Bg={$setViewValue:v,$render:v},Cg=["$element","$scope","$attrs",function(a,c,d){var e=this,f=new Sa;e.ngModelCtrl=Bg;e.unknownOption=y(U.createElement("option"));e.renderUnknownOption=function(c){c="? "+Ga(c)+" ?";e.unknownOption.val(c);a.prepend(e.unknownOption);a.val(c)};c.$on("$destroy",function(){e.renderUnknownOption=v});e.removeUnknownOption=function(){e.unknownOption.parent()&&e.unknownOption.remove()};e.readValue=
|
|
||||||
function(){e.removeUnknownOption();return a.val()};e.writeValue=function(c){e.hasOption(c)?(e.removeUnknownOption(),a.val(c),""===c&&e.emptyOption.prop("selected",!0)):null==c&&e.emptyOption?(e.removeUnknownOption(),a.val("")):e.renderUnknownOption(c)};e.addOption=function(a,c){Ra(a,'"option value"');""===a&&(e.emptyOption=c);var d=f.get(a)||0;f.put(a,d+1)};e.removeOption=function(a){var c=f.get(a);c&&(1===c?(f.remove(a),""===a&&(e.emptyOption=t)):f.put(a,c-1))};e.hasOption=function(a){return!!f.get(a)}}],
|
|
||||||
le=function(){return{restrict:"E",require:["select","?ngModel"],controller:Cg,link:function(a,c,d,e){var f=e[1];if(f){var g=e[0];g.ngModelCtrl=f;f.$render=function(){g.writeValue(f.$viewValue)};c.on("change",function(){a.$apply(function(){f.$setViewValue(g.readValue())})});if(d.multiple){g.readValue=function(){var a=[];m(c.find("option"),function(c){c.selected&&a.push(c.value)});return a};g.writeValue=function(a){var d=new Sa(a);m(c.find("option"),function(a){a.selected=w(d.get(a.value))})};var h,
|
|
||||||
l=NaN;a.$watch(function(){l!==f.$viewValue||ka(h,f.$viewValue)||(h=ia(f.$viewValue),f.$render());l=f.$viewValue});f.$isEmpty=function(a){return!a||0===a.length}}}}}},ne=["$interpolate",function(a){function c(a){a[0].hasAttribute("selected")&&(a[0].selected=!0)}return{restrict:"E",priority:100,compile:function(d,e){if(A(e.value)){var f=a(d.text(),!0);f||e.$set("value",d.text())}return function(a,d,e){var k=d.parent(),m=k.data("$selectController")||k.parent().data("$selectController");m&&m.ngModelCtrl&&
|
|
||||||
(f?a.$watch(f,function(a,f){e.$set("value",a);f!==a&&m.removeOption(f);m.addOption(a,d);m.ngModelCtrl.$render();c(d)}):(m.addOption(e.value,d),m.ngModelCtrl.$render(),c(d)),d.on("$destroy",function(){m.removeOption(e.value);m.ngModelCtrl.$render()}))}}}}],me=ra({restrict:"E",terminal:!1}),Hc=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){e&&(d.required=!0,e.$validators.required=function(a,c){return!d.required||!e.$isEmpty(c)},d.$observe("required",function(){e.$validate()}))}}},
|
|
||||||
Gc=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){if(e){var f,g=d.ngPattern||d.pattern;d.$observe("pattern",function(a){L(a)&&0<a.length&&(a=new RegExp("^"+a+"$"));if(a&&!a.test)throw J("ngPattern")("noregexp",g,a,ua(c));f=a||t;e.$validate()});e.$validators.pattern=function(a){return e.$isEmpty(a)||A(f)||f.test(a)}}}}},Jc=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){if(e){var f=-1;d.$observe("maxlength",function(a){a=W(a);f=isNaN(a)?-1:a;e.$validate()});
|
|
||||||
e.$validators.maxlength=function(a,c){return 0>f||e.$isEmpty(c)||c.length<=f}}}}},Ic=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){if(e){var f=0;d.$observe("minlength",function(a){f=W(a)||0;e.$validate()});e.$validators.minlength=function(a,c){return e.$isEmpty(c)||c.length>=f}}}}};O.angular.bootstrap?console.log("WARNING: Tried to load angular more than once."):(ce(),ee(ca),y(U).ready(function(){Zd(U,Ac)}))})(window,document);!window.angular.$$csp()&&window.angular.element(document).find("head").prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');
|
|
||||||
//# sourceMappingURL=angular.min.js.map
|
|
|
@ -1,39 +0,0 @@
|
||||||
<html ng-app="counterApp">
|
|
||||||
<head>
|
|
||||||
<title>Servant: counter</title>
|
|
||||||
<style>
|
|
||||||
body { text-align: center; }
|
|
||||||
#counter { color: green; }
|
|
||||||
#inc { margin: 0px 20px; background-color: green; color: white; }
|
|
||||||
</style>
|
|
||||||
<script src="angular.min.js" type="text/javascript"></script>
|
|
||||||
<script src="api.js" type="text/javascript"></script>
|
|
||||||
</head>
|
|
||||||
<body ng-controller="CounterCtrl">
|
|
||||||
<h1>Angular version</h1>
|
|
||||||
<span id="counter">{{ 'Counter: ' + counter }}</span>
|
|
||||||
<button ng-click="incCounter()" id="inc">Increase</button>
|
|
||||||
<script type="text/javascript">
|
|
||||||
var counterApp = angular.module('counterApp', []);
|
|
||||||
|
|
||||||
counterApp.controller('CounterCtrl', ['$scope', '$http', '$interval', function ($scope,$http, $interval) {
|
|
||||||
// Let's define how we publish information to the $scope
|
|
||||||
var publishCounter = function (response) { $scope.counter = response.value; };
|
|
||||||
|
|
||||||
$scope.getCounter = function() {
|
|
||||||
getCounter($http)
|
|
||||||
.success(publishCounter);
|
|
||||||
}
|
|
||||||
$scope.incCounter = function() {
|
|
||||||
postCounter($http)
|
|
||||||
.success(publishCounter);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the counter on load
|
|
||||||
$scope.getCounter();
|
|
||||||
// we update the value every 1sec, in the same way
|
|
||||||
$interval($scope.getCounter, 1000);
|
|
||||||
}]);
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,39 +0,0 @@
|
||||||
<html ng-app="counterApp">
|
|
||||||
<head>
|
|
||||||
<title>Servant: counter</title>
|
|
||||||
<style>
|
|
||||||
body { text-align: center; }
|
|
||||||
#counter { color: green; }
|
|
||||||
#inc { margin: 0px 20px; background-color: green; color: white; }
|
|
||||||
</style>
|
|
||||||
<script src="angular.min.js" type="text/javascript"></script>
|
|
||||||
</head>
|
|
||||||
<body ng-controller="CounterCtrl">
|
|
||||||
<h1>Angular version (Service version)</h1>
|
|
||||||
<span id="counter">{{ 'Counter: ' + counter }}</span>
|
|
||||||
<button ng-click="incCounter()" id="inc">Increase</button>
|
|
||||||
<script type="text/javascript">
|
|
||||||
var counterApp = angular.module('counterApp', []);
|
|
||||||
|
|
||||||
counterApp.controller('CounterCtrl', function ($scope,counterSvc, $interval) {
|
|
||||||
// Let's define how we publish information to the $scope
|
|
||||||
var publishCounter = function (response) { $scope.counter = response.value; };
|
|
||||||
|
|
||||||
$scope.getCounter = function() {
|
|
||||||
counterSvc.getCounter()
|
|
||||||
.success(publishCounter);
|
|
||||||
}
|
|
||||||
$scope.incCounter = function() {
|
|
||||||
counterSvc.postCounter()
|
|
||||||
.success(publishCounter);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the counter on load
|
|
||||||
$scope.getCounter();
|
|
||||||
// we update the value every 1sec, in the same way
|
|
||||||
$interval($scope.getCounter, 1000);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<script src="api.service.js" type="text/javascript"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
10
servant-js/examples/www/axios/axios.min.js
vendored
10
servant-js/examples/www/axios/axios.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,40 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Servant: counter</title>
|
|
||||||
<style>
|
|
||||||
body { text-align: center; }
|
|
||||||
#counter { color: green; }
|
|
||||||
#inc { margin: 0px 20px; background-color: green; color: white; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Axios version</h1>
|
|
||||||
<span id="counter">Counter: 0</span>
|
|
||||||
<button id="inc">Increase</button>
|
|
||||||
|
|
||||||
<script src="axios.min.js" type="text/javascript"></script>
|
|
||||||
<script src="api.js" type="text/javascript"></script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
window.addEventListener('load', function() {
|
|
||||||
// we get the current value stored by the server when the page is loaded
|
|
||||||
getCounter().then(updateCounter).catch(alert);
|
|
||||||
|
|
||||||
// we update the value every 1sec, in the same way
|
|
||||||
window.setInterval(function() {
|
|
||||||
getCounter().then(updateCounter).catch(alert);
|
|
||||||
}, 1000);
|
|
||||||
});
|
|
||||||
|
|
||||||
function updateCounter(response)
|
|
||||||
{
|
|
||||||
document.getElementById('counter').innerHTML = 'Counter: ' + response.data.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// when the button is clicked, ask the server to increase
|
|
||||||
// the value by one
|
|
||||||
document.getElementById('inc').addEventListener('click', function() {
|
|
||||||
postCounter().then(updateCounter).catch(alert);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,19 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Servant: counter</title>
|
|
||||||
<style>
|
|
||||||
body { text-align: center; }
|
|
||||||
#counter { color: green; }
|
|
||||||
#inc { margin: 0px 20px; background-color: green; color: white; }
|
|
||||||
iframe { height: 20%; width: 80%}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<iframe src="vanilla/"></iframe>
|
|
||||||
<iframe src="jquery/"></iframe>
|
|
||||||
<iframe src="angular/"></iframe>
|
|
||||||
<iframe src="angular/service.html"></iframe>
|
|
||||||
<iframe src="axios/"></iframe>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,40 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Servant: counter</title>
|
|
||||||
<style>
|
|
||||||
body { text-align: center; }
|
|
||||||
#counter { color: green; }
|
|
||||||
#inc { margin: 0px 20px; background-color: green; color: white; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>JQuery version</h1>
|
|
||||||
<span id="counter">Counter: 0</span>
|
|
||||||
<button id="inc">Increase</button>
|
|
||||||
|
|
||||||
<script src="jquery.min.js" type="text/javascript"></script>
|
|
||||||
<script src="api.js" type="text/javascript"></script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
$(document).ready(function() {
|
|
||||||
// we get the current value stored by the server when the page is loaded
|
|
||||||
getCounter(updateCounter, alert);
|
|
||||||
|
|
||||||
// we update the value every 1sec, in the same way
|
|
||||||
window.setInterval(function() {
|
|
||||||
getCounter(updateCounter, alert);
|
|
||||||
}, 1000);
|
|
||||||
});
|
|
||||||
|
|
||||||
function updateCounter(response)
|
|
||||||
{
|
|
||||||
$('#counter').html('Counter: ' + response.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// when the button is clicked, ask the server to increase
|
|
||||||
// the value by one
|
|
||||||
$('#inc').click(function() {
|
|
||||||
postCounter(updateCounter, alert);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
4
servant-js/examples/www/jquery/jquery.min.js
vendored
4
servant-js/examples/www/jquery/jquery.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,39 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Servant: counter</title>
|
|
||||||
<style>
|
|
||||||
body { text-align: center; }
|
|
||||||
#counter { color: green; }
|
|
||||||
#inc { margin: 0px 20px; background-color: green; color: white; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Vanilla version</h1>
|
|
||||||
<span id="counter">Counter: 0</span>
|
|
||||||
<button id="inc">Increase</button>
|
|
||||||
|
|
||||||
<script src="api.js" type="text/javascript"></script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
window.addEventListener('load', function() {
|
|
||||||
// we get the current value stored by the server when the page is loaded
|
|
||||||
getCounter(updateCounter, alert);
|
|
||||||
|
|
||||||
// we update the value every 1sec, in the same way
|
|
||||||
window.setInterval(function() {
|
|
||||||
getCounter(updateCounter, alert);
|
|
||||||
}, 1000);
|
|
||||||
});
|
|
||||||
|
|
||||||
function updateCounter(response)
|
|
||||||
{
|
|
||||||
document.getElementById('counter').innerHTML = 'Counter: ' + response.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// when the button is clicked, ask the server to increase
|
|
||||||
// the value by one
|
|
||||||
document.getElementById('inc').addEventListener('click', function() {
|
|
||||||
postCounter(updateCounter, alert);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,8 +0,0 @@
|
||||||
#if __GLASGOW_HASKELL__ >= 710
|
|
||||||
#define OVERLAPPABLE_ {-# OVERLAPPABLE #-}
|
|
||||||
#define OVERLAPPING_ {-# OVERLAPPING #-}
|
|
||||||
#else
|
|
||||||
{-# LANGUAGE OverlappingInstances #-}
|
|
||||||
#define OVERLAPPABLE_
|
|
||||||
#define OVERLAPPING_
|
|
||||||
#endif
|
|
|
@ -1,97 +0,0 @@
|
||||||
name: servant-js
|
|
||||||
version: 0.8
|
|
||||||
synopsis: Automatically derive javascript functions to query servant webservices.
|
|
||||||
description:
|
|
||||||
Automatically derive javascript functions to query servant webservices.
|
|
||||||
.
|
|
||||||
Supports deriving functions using vanilla javascript AJAX requests, Angulari, Axios or JQuery.
|
|
||||||
.
|
|
||||||
You can find an example <https://github.com/haskell-servant/servant/blob/master/servant-js/examples/counter.hs here>
|
|
||||||
which serves the generated javascript to a webpage that allows you to trigger
|
|
||||||
webservice calls.
|
|
||||||
.
|
|
||||||
<https://github.com/haskell-servant/servant/blob/master/servant-js/CHANGELOG.md CHANGELOG>
|
|
||||||
license: BSD3
|
|
||||||
license-file: LICENSE
|
|
||||||
author: Servant Contributors
|
|
||||||
maintainer: haskell-servant-maintainers@googlegroups.com
|
|
||||||
copyright: 2015-2016 Servant Contributors
|
|
||||||
category: Web
|
|
||||||
build-type: Simple
|
|
||||||
cabal-version: >=1.10
|
|
||||||
homepage: http://haskell-servant.readthedocs.org/
|
|
||||||
Bug-reports: http://github.com/haskell-servant/servant/issues
|
|
||||||
extra-source-files:
|
|
||||||
include/*.h
|
|
||||||
CHANGELOG.md
|
|
||||||
README.md
|
|
||||||
source-repository head
|
|
||||||
type: git
|
|
||||||
location: http://github.com/haskell-servant/servant.git
|
|
||||||
|
|
||||||
flag example
|
|
||||||
description: Build the example too
|
|
||||||
manual: True
|
|
||||||
default: False
|
|
||||||
|
|
||||||
library
|
|
||||||
exposed-modules: Servant.JS
|
|
||||||
Servant.JS.Angular
|
|
||||||
Servant.JS.Axios
|
|
||||||
Servant.JS.Internal
|
|
||||||
Servant.JS.JQuery
|
|
||||||
Servant.JS.Vanilla
|
|
||||||
build-depends: base >= 4.5 && <5
|
|
||||||
, base-compat >= 0.9
|
|
||||||
, charset >= 0.3
|
|
||||||
, lens >= 4
|
|
||||||
, servant-foreign == 0.8.*
|
|
||||||
, servant == 0.8.*
|
|
||||||
, text >= 1.2 && < 1.3
|
|
||||||
|
|
||||||
hs-source-dirs: src
|
|
||||||
default-language: Haskell2010
|
|
||||||
ghc-options: -Wall
|
|
||||||
include-dirs: include
|
|
||||||
|
|
||||||
executable counter
|
|
||||||
main-is: counter.hs
|
|
||||||
ghc-options: -Wall
|
|
||||||
hs-source-dirs: examples
|
|
||||||
|
|
||||||
if flag(example)
|
|
||||||
buildable: True
|
|
||||||
else
|
|
||||||
buildable: False
|
|
||||||
|
|
||||||
build-depends: base >= 4.7 && < 5
|
|
||||||
, aeson >= 0.7 && < 0.12
|
|
||||||
, filepath >= 1
|
|
||||||
, lens >= 4
|
|
||||||
, servant == 0.8.*
|
|
||||||
, servant-server == 0.8.*
|
|
||||||
, servant-js
|
|
||||||
, stm
|
|
||||||
, transformers
|
|
||||||
, warp
|
|
||||||
default-language: Haskell2010
|
|
||||||
|
|
||||||
test-suite spec
|
|
||||||
type: exitcode-stdio-1.0
|
|
||||||
hs-source-dirs: test
|
|
||||||
ghc-options: -Wall
|
|
||||||
main-is: Spec.hs
|
|
||||||
other-modules:
|
|
||||||
Servant.JSSpec
|
|
||||||
Servant.JSSpec.CustomHeaders
|
|
||||||
build-depends: base
|
|
||||||
, base-compat
|
|
||||||
, hspec >= 2.1.8
|
|
||||||
, hspec-expectations
|
|
||||||
, language-ecmascript >= 0.16
|
|
||||||
, lens
|
|
||||||
, servant
|
|
||||||
, servant-js
|
|
||||||
, text
|
|
||||||
, QuickCheck
|
|
||||||
default-language: Haskell2010
|
|
|
@ -1,153 +0,0 @@
|
||||||
{-# LANGUAGE DataKinds #-}
|
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
|
||||||
{-# LANGUAGE TypeOperators #-}
|
|
||||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
|
||||||
-----------------------------------------------------------------------------
|
|
||||||
-- |
|
|
||||||
-- Module : Servant.JS
|
|
||||||
-- License : BSD3
|
|
||||||
-- Maintainer : Alp Mestanogullari <alpmestan@gmail.com>
|
|
||||||
-- Stability : experimental
|
|
||||||
-- Portability : non-portable
|
|
||||||
--
|
|
||||||
-- Generating Javascript code to query your APIs using vanilla Javascript,
|
|
||||||
-- Angular.js or JQuery.
|
|
||||||
--
|
|
||||||
-- Using this package is very simple. Say you have this API type around:
|
|
||||||
--
|
|
||||||
-- > type API = "users" :> Get '[JSON] [Users]
|
|
||||||
-- > :<|> "messages" :> Get '[JSON] [Message]
|
|
||||||
--
|
|
||||||
-- All you need to do to generate the Javascript code is to write a 'Proxy'
|
|
||||||
-- for this API type:
|
|
||||||
--
|
|
||||||
-- > api :: Proxy API
|
|
||||||
-- > api = Proxy
|
|
||||||
--
|
|
||||||
-- And pick one of the generators:
|
|
||||||
--
|
|
||||||
-- - 'vanillaJS' and 'vanillaJSWith' generate functions that use
|
|
||||||
-- /XMLHttpRequest/ to query your endpoints. The former just calls
|
|
||||||
-- the latter with default code-generation options.
|
|
||||||
-- - 'jquery' and 'jqueryWith' follow the same pattern except that they
|
|
||||||
-- generate functions that use /jQuery/'s AJAX functions.
|
|
||||||
-- - 'angular' and 'angularWith' do the same but use /Angular.js/'s $http
|
|
||||||
-- service. In addition, we provide 'angularService' and 'angularServiceWith'
|
|
||||||
-- which produce functions under an Angular service that your controlers
|
|
||||||
-- can depend on to query the API.
|
|
||||||
--
|
|
||||||
-- Let's keep it simple and produce vanilla Javascript code with the default options.
|
|
||||||
--
|
|
||||||
-- @
|
|
||||||
-- jsCode :: Text
|
|
||||||
-- jsCode = 'jsForAPI' api 'vanillaJS'
|
|
||||||
-- @
|
|
||||||
--
|
|
||||||
-- That's it! If you want to write that code to a file:
|
|
||||||
--
|
|
||||||
-- @
|
|
||||||
-- writeJSCode :: IO ()
|
|
||||||
-- writeJSCode = 'writeJSForAPI' api 'vanillaJS' "./my_api.js"
|
|
||||||
-- @
|
|
||||||
--
|
|
||||||
-- If you want to customize the rendering options, take a look
|
|
||||||
-- at 'CommonGeneratorOptions' which are generic options common to all the
|
|
||||||
-- generators. the /xxxWith/ variants all take 'CommonGeneratorOptions' whereas
|
|
||||||
-- the /xxx/ versions use 'defCommonGeneratorOptions'. Once you have some custom
|
|
||||||
--
|
|
||||||
-- > myOptions :: 'CommonGeneratorOptions'
|
|
||||||
--
|
|
||||||
-- All you need to do to use it is to use 'vanillaJSWith' and pass it @myOptions@.
|
|
||||||
--
|
|
||||||
-- @
|
|
||||||
-- jsCodeWithMyOptions :: Text
|
|
||||||
-- jsCodeWithMyOptions = 'jsForAPI' api ('vanillaJSWith' myOptions)
|
|
||||||
-- @
|
|
||||||
--
|
|
||||||
-- Follow the same pattern for any other generator.
|
|
||||||
--
|
|
||||||
-- /Note/: The Angular generators take an additional type of options,
|
|
||||||
-- namely 'AngularOptions', to let you tweak aspects of the code generation
|
|
||||||
-- that are specific to /Angular.js/.
|
|
||||||
module Servant.JS
|
|
||||||
( -- * Generating javascript code from an API type
|
|
||||||
jsForAPI
|
|
||||||
, writeJSForAPI
|
|
||||||
, JavaScriptGenerator
|
|
||||||
|
|
||||||
, -- * Options common to all generators
|
|
||||||
CommonGeneratorOptions(..)
|
|
||||||
, defCommonGeneratorOptions
|
|
||||||
|
|
||||||
, -- * Function renamers
|
|
||||||
concatCase
|
|
||||||
, snakeCase
|
|
||||||
, camelCase
|
|
||||||
|
|
||||||
, -- * Vanilla Javascript code generation
|
|
||||||
vanillaJS
|
|
||||||
, vanillaJSWith
|
|
||||||
|
|
||||||
, -- * JQuery code generation
|
|
||||||
jquery
|
|
||||||
, jqueryWith
|
|
||||||
|
|
||||||
, -- * Angular.js code generation
|
|
||||||
angular
|
|
||||||
, angularWith
|
|
||||||
, angularService
|
|
||||||
, angularServiceWith
|
|
||||||
, AngularOptions(..)
|
|
||||||
, defAngularOptions
|
|
||||||
|
|
||||||
, -- * Axios code generation
|
|
||||||
axios
|
|
||||||
, axiosWith
|
|
||||||
, AxiosOptions(..)
|
|
||||||
, defAxiosOptions
|
|
||||||
|
|
||||||
, -- * Misc.
|
|
||||||
listFromAPI
|
|
||||||
, javascript
|
|
||||||
, NoTypes
|
|
||||||
, GenerateList(..)
|
|
||||||
) where
|
|
||||||
|
|
||||||
import Prelude hiding (writeFile)
|
|
||||||
import Data.Proxy
|
|
||||||
import Data.Text
|
|
||||||
import Data.Text.IO (writeFile)
|
|
||||||
import Servant.API.ContentTypes
|
|
||||||
import Servant.JS.Angular
|
|
||||||
import Servant.JS.Axios
|
|
||||||
import Servant.JS.Internal
|
|
||||||
import Servant.JS.JQuery
|
|
||||||
import Servant.JS.Vanilla
|
|
||||||
import Servant.Foreign (listFromAPI)
|
|
||||||
|
|
||||||
-- | Generate the data necessary to generate javascript code
|
|
||||||
-- for all the endpoints of an API, as ':<|>'-separated values
|
|
||||||
-- of type 'AjaxReq'.
|
|
||||||
javascript :: HasForeign NoTypes NoContent api => Proxy api -> Foreign NoContent api
|
|
||||||
javascript p = foreignFor (Proxy :: Proxy NoTypes) (Proxy :: Proxy NoContent) p defReq
|
|
||||||
|
|
||||||
-- | Directly generate all the javascript functions for your API
|
|
||||||
-- from a 'Proxy' for your API type. You can then write it to
|
|
||||||
-- a file or integrate it in a page, for example.
|
|
||||||
jsForAPI :: (HasForeign NoTypes NoContent api, GenerateList NoContent (Foreign NoContent api))
|
|
||||||
=> Proxy api -- ^ proxy for your API type
|
|
||||||
-> JavaScriptGenerator -- ^ js code generator to use (angular, vanilla js, jquery, others)
|
|
||||||
-> Text -- ^ a text that you can embed in your pages or write to a file
|
|
||||||
jsForAPI p gen = gen (listFromAPI (Proxy :: Proxy NoTypes) (Proxy :: Proxy NoContent) p)
|
|
||||||
|
|
||||||
-- | Directly generate all the javascript functions for your API
|
|
||||||
-- from a 'Proxy' for your API type using the given generator
|
|
||||||
-- and write the resulting code to a file at the given path.
|
|
||||||
writeJSForAPI :: (HasForeign NoTypes NoContent api, GenerateList NoContent (Foreign NoContent api))
|
|
||||||
=> Proxy api -- ^ proxy for your API type
|
|
||||||
-> JavaScriptGenerator -- ^ js code generator to use (angular, vanilla js, jquery, others)
|
|
||||||
-> FilePath -- ^ path to the file you want to write the resulting javascript code into
|
|
||||||
-> IO ()
|
|
||||||
writeJSForAPI p gen fp = writeFile fp (jsForAPI p gen)
|
|
||||||
|
|
|
@ -1,149 +0,0 @@
|
||||||
{-#LANGUAGE OverloadedStrings #-}
|
|
||||||
module Servant.JS.Angular where
|
|
||||||
|
|
||||||
import Control.Lens
|
|
||||||
import Data.Maybe (isJust)
|
|
||||||
import Data.Monoid
|
|
||||||
import qualified Data.Text as T
|
|
||||||
import Data.Text (Text)
|
|
||||||
import Data.Text.Encoding (decodeUtf8)
|
|
||||||
import Servant.Foreign
|
|
||||||
import Servant.JS.Internal
|
|
||||||
|
|
||||||
-- | Options specific to the angular code generator
|
|
||||||
data AngularOptions = AngularOptions
|
|
||||||
{ serviceName :: Text -- ^ When generating code with wrapInService,
|
|
||||||
-- name of the service to generate
|
|
||||||
, prologue :: Text -> Text -> Text -- ^ beginning of the service definition
|
|
||||||
, epilogue :: Text -- ^ end of the service definition
|
|
||||||
}
|
|
||||||
|
|
||||||
-- | Default options for the Angular codegen. Used by 'wrapInService'.
|
|
||||||
defAngularOptions :: AngularOptions
|
|
||||||
defAngularOptions = AngularOptions
|
|
||||||
{ serviceName = ""
|
|
||||||
, prologue = \svc m -> m <> "service('" <> svc <> "', function($http) {\n"
|
|
||||||
<> " return ({"
|
|
||||||
, epilogue = "});\n});\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
-- | Instead of simply generating top level functions, generates a service instance
|
|
||||||
-- on which your controllers can depend to access your API.
|
|
||||||
-- This variant uses default 'AngularOptions'.
|
|
||||||
angularService :: AngularOptions -> JavaScriptGenerator
|
|
||||||
angularService ngOpts = angularServiceWith ngOpts defCommonGeneratorOptions
|
|
||||||
|
|
||||||
-- | Instead of simply generating top level functions, generates a service instance
|
|
||||||
-- on which your controllers can depend to access your API
|
|
||||||
angularServiceWith :: AngularOptions -> CommonGeneratorOptions -> JavaScriptGenerator
|
|
||||||
angularServiceWith ngOpts opts reqs =
|
|
||||||
prologue ngOpts svc mName
|
|
||||||
<> T.intercalate "," (map generator reqs) <>
|
|
||||||
epilogue ngOpts
|
|
||||||
where
|
|
||||||
generator req = generateAngularJSWith ngOpts opts req
|
|
||||||
svc = serviceName ngOpts
|
|
||||||
mName = if moduleName opts == ""
|
|
||||||
then "app."
|
|
||||||
else moduleName opts <> "."
|
|
||||||
|
|
||||||
-- | Generate regular javacript functions that use
|
|
||||||
-- the $http service, using default values for 'CommonGeneratorOptions'.
|
|
||||||
angular :: AngularOptions -> JavaScriptGenerator
|
|
||||||
angular ngopts = angularWith ngopts defCommonGeneratorOptions
|
|
||||||
|
|
||||||
-- | Generate regular javascript functions that use the $http service.
|
|
||||||
angularWith :: AngularOptions -> CommonGeneratorOptions -> JavaScriptGenerator
|
|
||||||
angularWith ngopts opts = T.intercalate "\n\n" . map (generateAngularJSWith ngopts opts)
|
|
||||||
|
|
||||||
-- | js codegen using $http service from Angular using default options
|
|
||||||
generateAngularJS :: AngularOptions -> AjaxReq -> Text
|
|
||||||
generateAngularJS ngOpts = generateAngularJSWith ngOpts defCommonGeneratorOptions
|
|
||||||
|
|
||||||
-- | js codegen using $http service from Angular
|
|
||||||
generateAngularJSWith :: AngularOptions -> CommonGeneratorOptions -> AjaxReq -> Text
|
|
||||||
generateAngularJSWith ngOptions opts req = "\n" <>
|
|
||||||
fname <> fsep <> " function(" <> argsStr <> ")\n"
|
|
||||||
<> "{\n"
|
|
||||||
<> " return $http(\n"
|
|
||||||
<> " { url: " <> url <> "\n"
|
|
||||||
<> dataBody
|
|
||||||
<> reqheaders
|
|
||||||
<> " , method: '" <> decodeUtf8 method <> "'\n"
|
|
||||||
<> " });\n"
|
|
||||||
<> "}\n"
|
|
||||||
|
|
||||||
where argsStr = T.intercalate ", " args
|
|
||||||
args = http
|
|
||||||
++ captures
|
|
||||||
++ map (view $ queryArgName . argPath) queryparams
|
|
||||||
++ body
|
|
||||||
++ map ( toValidFunctionName
|
|
||||||
. (<>) "header"
|
|
||||||
. view (headerArg . argPath)
|
|
||||||
) hs
|
|
||||||
|
|
||||||
-- If we want to generate Top Level Function, they must depend on
|
|
||||||
-- the $http service, if we generate a service, the functions will
|
|
||||||
-- inherit this dependency from the service
|
|
||||||
http = case T.length (serviceName ngOptions) of
|
|
||||||
0 -> ["$http"]
|
|
||||||
_ -> []
|
|
||||||
|
|
||||||
captures = map (view argPath . captureArg)
|
|
||||||
. filter isCapture
|
|
||||||
$ req ^. reqUrl . path
|
|
||||||
|
|
||||||
hs = req ^. reqHeaders
|
|
||||||
|
|
||||||
queryparams = req ^.. reqUrl.queryStr.traverse
|
|
||||||
|
|
||||||
body = if isJust (req ^. reqBody)
|
|
||||||
then [requestBody opts]
|
|
||||||
else []
|
|
||||||
|
|
||||||
dataBody =
|
|
||||||
if isJust (req ^. reqBody)
|
|
||||||
then " , data: JSON.stringify(body)\n" <>
|
|
||||||
" , contentType: 'application/json'\n"
|
|
||||||
else ""
|
|
||||||
|
|
||||||
reqheaders =
|
|
||||||
if null hs
|
|
||||||
then ""
|
|
||||||
else " , headers: { " <> headersStr <> " }\n"
|
|
||||||
|
|
||||||
where
|
|
||||||
headersStr = T.intercalate ", " $ map headerStr hs
|
|
||||||
headerStr header = "\"" <>
|
|
||||||
header ^. headerArg . argPath <>
|
|
||||||
"\": " <> toJSHeader header
|
|
||||||
|
|
||||||
namespace =
|
|
||||||
if hasService
|
|
||||||
then ""
|
|
||||||
else if hasNoModule
|
|
||||||
then "var "
|
|
||||||
else (moduleName opts) <> "."
|
|
||||||
where
|
|
||||||
hasNoModule = moduleName opts == ""
|
|
||||||
|
|
||||||
hasService = serviceName ngOptions /= ""
|
|
||||||
|
|
||||||
fsep = if hasService then ":" else " ="
|
|
||||||
|
|
||||||
fname = namespace <> (toValidFunctionName (functionNameBuilder opts $ req ^. reqFuncName))
|
|
||||||
|
|
||||||
method = req ^. reqMethod
|
|
||||||
url = if url' == "'" then "'/'" else url'
|
|
||||||
url' = "'"
|
|
||||||
<> urlPrefix opts
|
|
||||||
<> urlArgs
|
|
||||||
<> queryArgs
|
|
||||||
|
|
||||||
urlArgs = jsSegments
|
|
||||||
$ req ^.. reqUrl.path.traverse
|
|
||||||
|
|
||||||
queryArgs = if null queryparams
|
|
||||||
then ""
|
|
||||||
else " + '?" <> jsParams queryparams
|
|
|
@ -1,137 +0,0 @@
|
||||||
{-#LANGUAGE OverloadedStrings #-}
|
|
||||||
module Servant.JS.Axios where
|
|
||||||
|
|
||||||
import Control.Lens
|
|
||||||
import Data.Maybe (isJust)
|
|
||||||
import Data.Monoid
|
|
||||||
import Data.Text (Text)
|
|
||||||
import Data.Text.Encoding (decodeUtf8)
|
|
||||||
import qualified Data.Text as T
|
|
||||||
import Servant.Foreign
|
|
||||||
import Servant.JS.Internal
|
|
||||||
|
|
||||||
-- | Axios 'configuration' type
|
|
||||||
-- Let you customize the generation using Axios capabilities
|
|
||||||
data AxiosOptions = AxiosOptions
|
|
||||||
{ -- | indicates whether or not cross-site Access-Control requests
|
|
||||||
-- should be made using credentials
|
|
||||||
withCredentials :: !Bool
|
|
||||||
-- | the name of the cookie to use as a value for xsrf token
|
|
||||||
, xsrfCookieName :: !(Maybe Text)
|
|
||||||
-- | the name of the header to use as a value for xsrf token
|
|
||||||
, xsrfHeaderName :: !(Maybe Text)
|
|
||||||
}
|
|
||||||
|
|
||||||
-- | Default instance of the AxiosOptions
|
|
||||||
-- Defines the settings as they are in the Axios documentation
|
|
||||||
-- by default
|
|
||||||
defAxiosOptions :: AxiosOptions
|
|
||||||
defAxiosOptions = AxiosOptions
|
|
||||||
{ withCredentials = False
|
|
||||||
, xsrfCookieName = Nothing
|
|
||||||
, xsrfHeaderName = Nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
-- | Generate regular javacript functions that use
|
|
||||||
-- the axios library, using default values for 'CommonGeneratorOptions'.
|
|
||||||
axios :: AxiosOptions -> JavaScriptGenerator
|
|
||||||
axios aopts = axiosWith aopts defCommonGeneratorOptions
|
|
||||||
|
|
||||||
-- | Generate regular javascript functions that use the axios library.
|
|
||||||
axiosWith :: AxiosOptions -> CommonGeneratorOptions -> JavaScriptGenerator
|
|
||||||
axiosWith aopts opts = T.intercalate "\n\n" . map (generateAxiosJSWith aopts opts)
|
|
||||||
|
|
||||||
-- | js codegen using axios library using default options
|
|
||||||
generateAxiosJS :: AxiosOptions -> AjaxReq -> Text
|
|
||||||
generateAxiosJS aopts = generateAxiosJSWith aopts defCommonGeneratorOptions
|
|
||||||
|
|
||||||
-- | js codegen using axios library
|
|
||||||
generateAxiosJSWith :: AxiosOptions -> CommonGeneratorOptions -> AjaxReq -> Text
|
|
||||||
generateAxiosJSWith aopts opts req = "\n" <>
|
|
||||||
fname <> " = function(" <> argsStr <> ")\n"
|
|
||||||
<> "{\n"
|
|
||||||
<> " return axios({ url: " <> url <> "\n"
|
|
||||||
<> " , method: '" <> method <> "'\n"
|
|
||||||
<> dataBody
|
|
||||||
<> reqheaders
|
|
||||||
<> withCreds
|
|
||||||
<> xsrfCookie
|
|
||||||
<> xsrfHeader
|
|
||||||
<> " });\n"
|
|
||||||
<> "}\n"
|
|
||||||
|
|
||||||
where argsStr = T.intercalate ", " args
|
|
||||||
args = captures
|
|
||||||
++ map (view $ queryArgName . argPath) queryparams
|
|
||||||
++ body
|
|
||||||
++ map ( toValidFunctionName
|
|
||||||
. (<>) "header"
|
|
||||||
. view (headerArg . argPath)
|
|
||||||
) hs
|
|
||||||
|
|
||||||
captures = map (view argPath . captureArg)
|
|
||||||
. filter isCapture
|
|
||||||
$ req ^. reqUrl.path
|
|
||||||
|
|
||||||
hs = req ^. reqHeaders
|
|
||||||
|
|
||||||
queryparams = req ^.. reqUrl.queryStr.traverse
|
|
||||||
|
|
||||||
body = if isJust (req ^. reqBody)
|
|
||||||
then [requestBody opts]
|
|
||||||
else []
|
|
||||||
|
|
||||||
dataBody =
|
|
||||||
if isJust (req ^. reqBody)
|
|
||||||
then " , data: body\n" <>
|
|
||||||
" , responseType: 'json'\n"
|
|
||||||
else ""
|
|
||||||
|
|
||||||
withCreds =
|
|
||||||
if withCredentials aopts
|
|
||||||
then " , withCredentials: true\n"
|
|
||||||
else ""
|
|
||||||
|
|
||||||
xsrfCookie =
|
|
||||||
case xsrfCookieName aopts of
|
|
||||||
Just name -> " , xsrfCookieName: '" <> name <> "'\n"
|
|
||||||
Nothing -> ""
|
|
||||||
|
|
||||||
xsrfHeader =
|
|
||||||
case xsrfHeaderName aopts of
|
|
||||||
Just name -> " , xsrfHeaderName: '" <> name <> "'\n"
|
|
||||||
Nothing -> ""
|
|
||||||
|
|
||||||
reqheaders =
|
|
||||||
if null hs
|
|
||||||
then ""
|
|
||||||
else " , headers: { " <> headersStr <> " }\n"
|
|
||||||
|
|
||||||
where
|
|
||||||
headersStr = T.intercalate ", " $ map headerStr hs
|
|
||||||
headerStr header = "\"" <>
|
|
||||||
header ^. headerArg . argPath <>
|
|
||||||
"\": " <> toJSHeader header
|
|
||||||
|
|
||||||
namespace =
|
|
||||||
if hasNoModule
|
|
||||||
then "var "
|
|
||||||
else (moduleName opts) <> "."
|
|
||||||
where
|
|
||||||
hasNoModule = moduleName opts == ""
|
|
||||||
|
|
||||||
fname = namespace <> (toValidFunctionName (functionNameBuilder opts $ req ^. reqFuncName))
|
|
||||||
|
|
||||||
method = T.toLower . decodeUtf8 $ req ^. reqMethod
|
|
||||||
url = if url' == "'" then "'/'" else url'
|
|
||||||
url' = "'"
|
|
||||||
<> urlPrefix opts
|
|
||||||
<> urlArgs
|
|
||||||
<> queryArgs
|
|
||||||
|
|
||||||
urlArgs = jsSegments
|
|
||||||
$ req ^.. reqUrl.path.traverse
|
|
||||||
|
|
||||||
queryArgs = if null queryparams
|
|
||||||
then ""
|
|
||||||
else " + '?" <> jsParams queryparams
|
|
|
@ -1,191 +0,0 @@
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
|
||||||
|
|
||||||
module Servant.JS.Internal
|
|
||||||
( JavaScriptGenerator
|
|
||||||
, CommonGeneratorOptions(..)
|
|
||||||
, defCommonGeneratorOptions
|
|
||||||
, AjaxReq
|
|
||||||
, jsSegments
|
|
||||||
, segmentToStr
|
|
||||||
, segmentTypeToStr
|
|
||||||
, jsParams
|
|
||||||
, jsGParams
|
|
||||||
, paramToStr
|
|
||||||
, toValidFunctionName
|
|
||||||
, toJSHeader
|
|
||||||
-- re-exports
|
|
||||||
, (:<|>)(..)
|
|
||||||
, (:>)
|
|
||||||
, defReq
|
|
||||||
, reqHeaders
|
|
||||||
, HasForeign(..)
|
|
||||||
, HasForeignType(..)
|
|
||||||
, GenerateList(..)
|
|
||||||
, NoTypes
|
|
||||||
, ArgType(..)
|
|
||||||
, HeaderArg(..)
|
|
||||||
, QueryArg(..)
|
|
||||||
, Req(..)
|
|
||||||
, Segment(..)
|
|
||||||
, SegmentType(..)
|
|
||||||
, Url(..)
|
|
||||||
, Path
|
|
||||||
, Arg(..)
|
|
||||||
, FunctionName(..)
|
|
||||||
, PathSegment(..)
|
|
||||||
, concatCase
|
|
||||||
, snakeCase
|
|
||||||
, camelCase
|
|
||||||
, ReqBody
|
|
||||||
, JSON
|
|
||||||
, FormUrlEncoded
|
|
||||||
, Post
|
|
||||||
, Get
|
|
||||||
, Raw
|
|
||||||
, Header
|
|
||||||
) where
|
|
||||||
|
|
||||||
import Control.Lens ((^.))
|
|
||||||
import qualified Data.CharSet as Set
|
|
||||||
import qualified Data.CharSet.Unicode.Category as Set
|
|
||||||
import Data.Monoid
|
|
||||||
import qualified Data.Text as T
|
|
||||||
import Data.Text (Text)
|
|
||||||
import Servant.Foreign
|
|
||||||
|
|
||||||
type AjaxReq = Req NoContent
|
|
||||||
|
|
||||||
-- A 'JavascriptGenerator' just takes the data found in the API type
|
|
||||||
-- for each endpoint and generates Javascript code in a Text. Several
|
|
||||||
-- generators are available in this package.
|
|
||||||
type JavaScriptGenerator = [Req NoContent] -> Text
|
|
||||||
|
|
||||||
-- | This structure is used by specific implementations to let you
|
|
||||||
-- customize the output
|
|
||||||
data CommonGeneratorOptions = CommonGeneratorOptions
|
|
||||||
{
|
|
||||||
functionNameBuilder :: FunctionName -> Text
|
|
||||||
-- ^ function generating function names
|
|
||||||
, requestBody :: Text
|
|
||||||
-- ^ name used when a user want to send the request body
|
|
||||||
-- (to let you redefine it)
|
|
||||||
, successCallback :: Text
|
|
||||||
-- ^ name of the callback parameter when the request was successful
|
|
||||||
, errorCallback :: Text
|
|
||||||
-- ^ name of the callback parameter when the request reported an error
|
|
||||||
, moduleName :: Text
|
|
||||||
-- ^ namespace on which we define the foreign function (empty mean local var)
|
|
||||||
, urlPrefix :: Text
|
|
||||||
-- ^ a prefix we should add to the Url in the codegen
|
|
||||||
}
|
|
||||||
|
|
||||||
-- | Default options.
|
|
||||||
--
|
|
||||||
-- @
|
|
||||||
-- > defCommonGeneratorOptions = CommonGeneratorOptions
|
|
||||||
-- > { functionNameBuilder = camelCase
|
|
||||||
-- > , requestBody = "body"
|
|
||||||
-- > , successCallback = "onSuccess"
|
|
||||||
-- > , errorCallback = "onError"
|
|
||||||
-- > , moduleName = ""
|
|
||||||
-- > , urlPrefix = ""
|
|
||||||
-- > }
|
|
||||||
-- @
|
|
||||||
defCommonGeneratorOptions :: CommonGeneratorOptions
|
|
||||||
defCommonGeneratorOptions = CommonGeneratorOptions
|
|
||||||
{
|
|
||||||
functionNameBuilder = camelCase
|
|
||||||
, requestBody = "body"
|
|
||||||
, successCallback = "onSuccess"
|
|
||||||
, errorCallback = "onError"
|
|
||||||
, moduleName = ""
|
|
||||||
, urlPrefix = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
-- | Attempts to reduce the function name provided to that allowed by @'Foreign'@.
|
|
||||||
--
|
|
||||||
-- https://mathiasbynens.be/notes/javascript-identifiers
|
|
||||||
-- Couldn't work out how to handle zero-width characters.
|
|
||||||
--
|
|
||||||
-- @TODO: specify better default function name, or throw error?
|
|
||||||
toValidFunctionName :: Text -> Text
|
|
||||||
toValidFunctionName t =
|
|
||||||
case T.uncons t of
|
|
||||||
Just (x,xs) ->
|
|
||||||
setFirstChar x `T.cons` T.filter remainder xs
|
|
||||||
Nothing -> "_"
|
|
||||||
where
|
|
||||||
setFirstChar c = if Set.member c firstLetterOK then c else '_'
|
|
||||||
remainder c = Set.member c remainderOK
|
|
||||||
firstLetterOK = (filterBmpChars $ mconcat
|
|
||||||
[ Set.fromDistinctAscList "$_"
|
|
||||||
, Set.lowercaseLetter
|
|
||||||
, Set.uppercaseLetter
|
|
||||||
, Set.titlecaseLetter
|
|
||||||
, Set.modifierLetter
|
|
||||||
, Set.otherLetter
|
|
||||||
, Set.letterNumber
|
|
||||||
])
|
|
||||||
remainderOK = firstLetterOK
|
|
||||||
<> (filterBmpChars $ mconcat
|
|
||||||
[ Set.nonSpacingMark
|
|
||||||
, Set.spacingCombiningMark
|
|
||||||
, Set.decimalNumber
|
|
||||||
, Set.connectorPunctuation
|
|
||||||
])
|
|
||||||
|
|
||||||
-- Javascript identifiers can only contain codepoints in the Basic Multilingual Plane
|
|
||||||
-- that is, codepoints that can be encoded in UTF-16 without a surrogate pair (UCS-2)
|
|
||||||
-- that is, codepoints that can fit in 16-bits, up to 0xffff (65535)
|
|
||||||
filterBmpChars :: Set.CharSet -> Set.CharSet
|
|
||||||
filterBmpChars = Set.filter (< '\65536')
|
|
||||||
|
|
||||||
toJSHeader :: HeaderArg f -> Text
|
|
||||||
toJSHeader (HeaderArg n)
|
|
||||||
= toValidFunctionName ("header" <> n ^. argName . _PathSegment)
|
|
||||||
toJSHeader (ReplaceHeaderArg n p)
|
|
||||||
| pn `T.isPrefixOf` p = pv <> " + \"" <> rp <> "\""
|
|
||||||
| pn `T.isSuffixOf` p = "\"" <> rp <> "\" + " <> pv
|
|
||||||
| pn `T.isInfixOf` p = "\"" <> (T.replace pn ("\" + " <> pv <> " + \"") p)
|
|
||||||
<> "\""
|
|
||||||
| otherwise = p
|
|
||||||
where
|
|
||||||
pv = toValidFunctionName ("header" <> n ^. argName . _PathSegment)
|
|
||||||
pn = "{" <> n ^. argName . _PathSegment <> "}"
|
|
||||||
rp = T.replace pn "" p
|
|
||||||
|
|
||||||
jsSegments :: [Segment f] -> Text
|
|
||||||
jsSegments [] = ""
|
|
||||||
jsSegments [x] = "/" <> segmentToStr x False
|
|
||||||
jsSegments (x:xs) = "/" <> segmentToStr x True <> jsSegments xs
|
|
||||||
|
|
||||||
segmentToStr :: Segment f -> Bool -> Text
|
|
||||||
segmentToStr (Segment st) notTheEnd =
|
|
||||||
segmentTypeToStr st <> if notTheEnd then "" else "'"
|
|
||||||
|
|
||||||
segmentTypeToStr :: SegmentType f -> Text
|
|
||||||
segmentTypeToStr (Static s) = s ^. _PathSegment
|
|
||||||
segmentTypeToStr (Cap s) =
|
|
||||||
"' + encodeURIComponent(" <> s ^. argName . _PathSegment <> ") + '"
|
|
||||||
|
|
||||||
jsGParams :: Text -> [QueryArg f] -> Text
|
|
||||||
jsGParams _ [] = ""
|
|
||||||
jsGParams _ [x] = paramToStr x False
|
|
||||||
jsGParams s (x:xs) = paramToStr x True <> s <> jsGParams s xs
|
|
||||||
|
|
||||||
jsParams :: [QueryArg f] -> Text
|
|
||||||
jsParams = jsGParams "&"
|
|
||||||
|
|
||||||
paramToStr :: QueryArg f -> Bool -> Text
|
|
||||||
paramToStr qarg notTheEnd =
|
|
||||||
case qarg ^. queryArgType of
|
|
||||||
Normal -> name
|
|
||||||
<> "=' + encodeURIComponent("
|
|
||||||
<> name
|
|
||||||
<> if notTheEnd then ") + '" else ")"
|
|
||||||
Flag -> name <> "="
|
|
||||||
List -> name
|
|
||||||
<> "[]=' + encodeURIComponent("
|
|
||||||
<> name
|
|
||||||
<> if notTheEnd then ") + '" else ")"
|
|
||||||
where name = qarg ^. queryArgName . argName . _PathSegment
|
|
|
@ -1,103 +0,0 @@
|
||||||
{-#LANGUAGE OverloadedStrings #-}
|
|
||||||
module Servant.JS.JQuery where
|
|
||||||
|
|
||||||
import Control.Lens
|
|
||||||
import Data.Maybe (isJust)
|
|
||||||
import Data.Monoid
|
|
||||||
import qualified Data.Text as T
|
|
||||||
import Data.Text (Text)
|
|
||||||
import Data.Text.Encoding (decodeUtf8)
|
|
||||||
import Servant.Foreign
|
|
||||||
import Servant.JS.Internal
|
|
||||||
|
|
||||||
|
|
||||||
-- | Generate javascript functions that use the /jQuery/ library
|
|
||||||
-- to make the AJAX calls. Uses 'defCommonGeneratorOptions'
|
|
||||||
-- for the generator options.
|
|
||||||
jquery :: JavaScriptGenerator
|
|
||||||
jquery = mconcat . map generateJQueryJS
|
|
||||||
|
|
||||||
-- | Generate javascript functions that use the /jQuery/ library
|
|
||||||
-- to make the AJAX calls. Lets you specify your own 'CommonGeneratorOptions'.
|
|
||||||
jqueryWith :: CommonGeneratorOptions -> JavaScriptGenerator
|
|
||||||
jqueryWith opts = mconcat . map (generateJQueryJSWith opts)
|
|
||||||
|
|
||||||
-- | js codegen using JQuery using default options
|
|
||||||
generateJQueryJS :: AjaxReq -> Text
|
|
||||||
generateJQueryJS = generateJQueryJSWith defCommonGeneratorOptions
|
|
||||||
|
|
||||||
-- | js codegen using JQuery
|
|
||||||
generateJQueryJSWith :: CommonGeneratorOptions -> AjaxReq -> Text
|
|
||||||
generateJQueryJSWith opts req = "\n" <>
|
|
||||||
fname <> " = function(" <> argsStr <> ")\n"
|
|
||||||
<> "{\n"
|
|
||||||
<> " $.ajax(\n"
|
|
||||||
<> " { url: " <> url <> "\n"
|
|
||||||
<> " , success: " <> onSuccess <> "\n"
|
|
||||||
<> dataBody
|
|
||||||
<> reqheaders
|
|
||||||
<> " , error: " <> onError <> "\n"
|
|
||||||
<> " , type: '" <> decodeUtf8 method <> "'\n"
|
|
||||||
<> " });\n"
|
|
||||||
<> "}\n"
|
|
||||||
|
|
||||||
where argsStr = T.intercalate ", " args
|
|
||||||
args = captures
|
|
||||||
++ map (view $ queryArgName . argPath) queryparams
|
|
||||||
++ body
|
|
||||||
++ map (toValidFunctionName
|
|
||||||
. (<>) "header"
|
|
||||||
. view (headerArg . argPath)
|
|
||||||
) hs
|
|
||||||
++ [onSuccess, onError]
|
|
||||||
|
|
||||||
captures = map (view argPath . captureArg)
|
|
||||||
. filter isCapture
|
|
||||||
$ req ^. reqUrl.path
|
|
||||||
|
|
||||||
hs = req ^. reqHeaders
|
|
||||||
|
|
||||||
queryparams = req ^.. reqUrl.queryStr.traverse
|
|
||||||
|
|
||||||
body = if isJust (req ^. reqBody)
|
|
||||||
then [requestBody opts]
|
|
||||||
else []
|
|
||||||
|
|
||||||
onSuccess = successCallback opts
|
|
||||||
onError = errorCallback opts
|
|
||||||
|
|
||||||
dataBody =
|
|
||||||
if isJust $ req ^. reqBody
|
|
||||||
then " , data: JSON.stringify(body)\n" <>
|
|
||||||
" , contentType: 'application/json'\n"
|
|
||||||
else ""
|
|
||||||
|
|
||||||
reqheaders =
|
|
||||||
if null hs
|
|
||||||
then ""
|
|
||||||
else " , headers: { " <> headersStr <> " }\n"
|
|
||||||
|
|
||||||
where
|
|
||||||
headersStr = T.intercalate ", " $ map headerStr hs
|
|
||||||
headerStr header = "\"" <>
|
|
||||||
header ^. headerArg . argPath <>
|
|
||||||
"\": " <> toJSHeader header
|
|
||||||
|
|
||||||
namespace = if (moduleName opts) == ""
|
|
||||||
then "var "
|
|
||||||
else (moduleName opts) <> "."
|
|
||||||
fname = namespace <> (toValidFunctionName (functionNameBuilder opts $ req ^. reqFuncName))
|
|
||||||
|
|
||||||
method = req ^. reqMethod
|
|
||||||
url = if url' == "'" then "'/'" else url'
|
|
||||||
url' = "'"
|
|
||||||
<> urlPrefix opts
|
|
||||||
<> urlArgs
|
|
||||||
<> queryArgs
|
|
||||||
|
|
||||||
urlArgs = jsSegments
|
|
||||||
$ req ^.. reqUrl.path.traverse
|
|
||||||
|
|
||||||
queryArgs = if null queryparams
|
|
||||||
then ""
|
|
||||||
else " + '?" <> jsParams queryparams
|
|
|
@ -1,114 +0,0 @@
|
||||||
{-#LANGUAGE OverloadedStrings #-}
|
|
||||||
module Servant.JS.Vanilla where
|
|
||||||
|
|
||||||
import Control.Lens
|
|
||||||
import Data.Maybe (isJust)
|
|
||||||
import Data.Text (Text)
|
|
||||||
import Data.Text.Encoding (decodeUtf8)
|
|
||||||
import qualified Data.Text as T
|
|
||||||
import Data.Monoid
|
|
||||||
import Servant.Foreign
|
|
||||||
import Servant.JS.Internal
|
|
||||||
|
|
||||||
-- | Generate vanilla javascript functions to make AJAX requests
|
|
||||||
-- to your API, using /XMLHttpRequest/. Uses 'defCommonGeneratorOptions'
|
|
||||||
-- for the 'CommonGeneratorOptions'.
|
|
||||||
vanillaJS :: JavaScriptGenerator
|
|
||||||
vanillaJS = mconcat . map generateVanillaJS
|
|
||||||
|
|
||||||
-- | Generate vanilla javascript functions to make AJAX requests
|
|
||||||
-- to your API, using /XMLHttpRequest/. Lets you specify your
|
|
||||||
-- own options.
|
|
||||||
vanillaJSWith :: CommonGeneratorOptions -> JavaScriptGenerator
|
|
||||||
vanillaJSWith opts = mconcat . map (generateVanillaJSWith opts)
|
|
||||||
|
|
||||||
-- | js codegen using XmlHttpRequest using default generation options
|
|
||||||
generateVanillaJS :: AjaxReq -> Text
|
|
||||||
generateVanillaJS = generateVanillaJSWith defCommonGeneratorOptions
|
|
||||||
|
|
||||||
-- | js codegen using XmlHttpRequest
|
|
||||||
generateVanillaJSWith :: CommonGeneratorOptions -> AjaxReq -> Text
|
|
||||||
generateVanillaJSWith opts req = "\n" <>
|
|
||||||
fname <> " = function(" <> argsStr <> ")\n"
|
|
||||||
<> "{\n"
|
|
||||||
<> " var xhr = new XMLHttpRequest();\n"
|
|
||||||
<> " xhr.open('" <> decodeUtf8 method <> "', " <> url <> ", true);\n"
|
|
||||||
<> reqheaders
|
|
||||||
<> " xhr.setRequestHeader(\"Accept\",\"application/json\");\n"
|
|
||||||
<> (if isJust (req ^. reqBody) then " xhr.setRequestHeader(\"Content-Type\",\"application/json\");\n" else "")
|
|
||||||
<> " xhr.onreadystatechange = function (e) {\n"
|
|
||||||
<> " if (xhr.readyState == 4) {\n"
|
|
||||||
<> " if (xhr.status == 204 || xhr.status == 205) {\n"
|
|
||||||
<> " onSuccess();\n"
|
|
||||||
<> " } else if (xhr.status >= 200 && xhr.status < 300) {\n"
|
|
||||||
<> " var value = JSON.parse(xhr.responseText);\n"
|
|
||||||
<> " onSuccess(value);\n"
|
|
||||||
<> " } else {\n"
|
|
||||||
<> " var value = JSON.parse(xhr.responseText);\n"
|
|
||||||
<> " onError(value);\n"
|
|
||||||
<> " }\n"
|
|
||||||
<> " }\n"
|
|
||||||
<> " }\n"
|
|
||||||
<> " xhr.send(" <> dataBody <> ");\n"
|
|
||||||
<> "}\n"
|
|
||||||
|
|
||||||
where argsStr = T.intercalate ", " args
|
|
||||||
args = captures
|
|
||||||
++ map (view $ queryArgName . argPath) queryparams
|
|
||||||
++ body
|
|
||||||
++ map ( toValidFunctionName
|
|
||||||
. (<>) "header"
|
|
||||||
. view (headerArg . argPath)
|
|
||||||
) hs
|
|
||||||
++ [onSuccess, onError]
|
|
||||||
|
|
||||||
captures = map (view argPath . captureArg)
|
|
||||||
. filter isCapture
|
|
||||||
$ req ^. reqUrl.path
|
|
||||||
|
|
||||||
hs = req ^. reqHeaders
|
|
||||||
|
|
||||||
queryparams = req ^.. reqUrl.queryStr.traverse
|
|
||||||
|
|
||||||
body = if isJust(req ^. reqBody)
|
|
||||||
then [requestBody opts]
|
|
||||||
else []
|
|
||||||
|
|
||||||
onSuccess = successCallback opts
|
|
||||||
onError = errorCallback opts
|
|
||||||
|
|
||||||
dataBody =
|
|
||||||
if isJust (req ^. reqBody)
|
|
||||||
then "JSON.stringify(body)\n"
|
|
||||||
else "null"
|
|
||||||
|
|
||||||
|
|
||||||
reqheaders =
|
|
||||||
if null hs
|
|
||||||
then ""
|
|
||||||
else headersStr <> "\n"
|
|
||||||
|
|
||||||
where
|
|
||||||
headersStr = T.intercalate "\n" $ map headerStr hs
|
|
||||||
headerStr header = " xhr.setRequestHeader(\"" <>
|
|
||||||
header ^. headerArg . argPath <>
|
|
||||||
"\", " <> toJSHeader header <> ");"
|
|
||||||
|
|
||||||
namespace = if moduleName opts == ""
|
|
||||||
then "var "
|
|
||||||
else (moduleName opts) <> "."
|
|
||||||
fname = namespace <> (toValidFunctionName (functionNameBuilder opts $ req ^. reqFuncName))
|
|
||||||
|
|
||||||
method = req ^. reqMethod
|
|
||||||
url = if url' == "'" then "'/'" else url'
|
|
||||||
url' = "'"
|
|
||||||
<> urlPrefix opts
|
|
||||||
<> urlArgs
|
|
||||||
<> queryArgs
|
|
||||||
|
|
||||||
urlArgs = jsSegments
|
|
||||||
$ req ^.. reqUrl.path.traverse
|
|
||||||
|
|
||||||
queryArgs = if null queryparams
|
|
||||||
then ""
|
|
||||||
else " + '?" <> jsParams queryparams
|
|
|
@ -1,225 +0,0 @@
|
||||||
{-# LANGUAGE DataKinds #-}
|
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
|
||||||
{-# LANGUAGE QuasiQuotes #-}
|
|
||||||
{-# LANGUAGE ScopedTypeVariables #-}
|
|
||||||
{-# LANGUAGE TypeFamilies #-}
|
|
||||||
{-# LANGUAGE TypeOperators #-}
|
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
|
||||||
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
|
||||||
|
|
||||||
module Servant.JSSpec where
|
|
||||||
|
|
||||||
import Data.Either (isRight)
|
|
||||||
import Data.Monoid ()
|
|
||||||
import Data.Monoid.Compat ((<>))
|
|
||||||
import Data.Proxy
|
|
||||||
import Data.Text (Text)
|
|
||||||
import qualified Data.Text as T
|
|
||||||
import Language.ECMAScript3.Lexer (identifier)
|
|
||||||
import Language.ECMAScript3.Parser (program, parse)
|
|
||||||
import Prelude ()
|
|
||||||
import Prelude.Compat
|
|
||||||
import Test.Hspec hiding (shouldContain, shouldNotContain)
|
|
||||||
import Test.QuickCheck (Arbitrary (..),
|
|
||||||
choose, listOf,
|
|
||||||
property)
|
|
||||||
|
|
||||||
import Servant.API.Internal.Test.ComprehensiveAPI
|
|
||||||
import Servant.API.ContentTypes
|
|
||||||
import Servant.JS
|
|
||||||
import Servant.JS.Internal
|
|
||||||
import qualified Servant.JS.Angular as NG
|
|
||||||
import qualified Servant.JS.Axios as AX
|
|
||||||
import qualified Servant.JS.JQuery as JQ
|
|
||||||
import qualified Servant.JS.Vanilla as JS
|
|
||||||
import Servant.JSSpec.CustomHeaders
|
|
||||||
|
|
||||||
-- * comprehensive api
|
|
||||||
|
|
||||||
-- This declaration simply checks that all instances are in place.
|
|
||||||
_ = jsForAPI comprehensiveAPI vanillaJS :: Text
|
|
||||||
|
|
||||||
-- * specs
|
|
||||||
|
|
||||||
type TestAPI = "simple" :> ReqBody '[JSON,FormUrlEncoded] Text :> Post '[JSON] Bool
|
|
||||||
:<|> "has.extension" :> Get '[FormUrlEncoded,JSON] Bool
|
|
||||||
|
|
||||||
type TopLevelRawAPI = "something" :> Get '[JSON] Int
|
|
||||||
:<|> Raw
|
|
||||||
|
|
||||||
type HeaderHandlingAPI = "something" :> Header "Foo" Text
|
|
||||||
:> Get '[JSON] Int
|
|
||||||
|
|
||||||
type CustomAuthAPI = "something" :> Authorization "Basic" Text
|
|
||||||
:> Get '[JSON] Int
|
|
||||||
|
|
||||||
type CustomHeaderAPI = "something" :> MyLovelyHorse Text
|
|
||||||
:> Get '[JSON] Int
|
|
||||||
|
|
||||||
type CustomHeaderAPI2 = "something" :> WhatsForDinner Text
|
|
||||||
:> Get '[JSON] Int
|
|
||||||
|
|
||||||
headerHandlingProxy :: Proxy HeaderHandlingAPI
|
|
||||||
headerHandlingProxy = Proxy
|
|
||||||
|
|
||||||
customAuthProxy :: Proxy CustomAuthAPI
|
|
||||||
customAuthProxy = Proxy
|
|
||||||
|
|
||||||
customHeaderProxy :: Proxy CustomHeaderAPI
|
|
||||||
customHeaderProxy = Proxy
|
|
||||||
|
|
||||||
customHeaderProxy2 :: Proxy CustomHeaderAPI2
|
|
||||||
customHeaderProxy2 = Proxy
|
|
||||||
|
|
||||||
data TestNames = Vanilla
|
|
||||||
| VanillaCustom
|
|
||||||
| JQuery
|
|
||||||
| JQueryCustom
|
|
||||||
| Angular
|
|
||||||
| AngularCustom
|
|
||||||
| Axios
|
|
||||||
| AxiosCustom
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
customOptions :: CommonGeneratorOptions
|
|
||||||
customOptions = defCommonGeneratorOptions
|
|
||||||
{ successCallback = "okCallback"
|
|
||||||
, errorCallback = "errorCallback"
|
|
||||||
}
|
|
||||||
|
|
||||||
spec :: Spec
|
|
||||||
spec = describe "Servant.JQuery" $ do
|
|
||||||
generateJSSpec Vanilla JS.generateVanillaJS
|
|
||||||
generateJSSpec VanillaCustom (JS.generateVanillaJSWith customOptions)
|
|
||||||
generateJSSpec JQuery JQ.generateJQueryJS
|
|
||||||
generateJSSpec JQueryCustom (JQ.generateJQueryJSWith customOptions)
|
|
||||||
generateJSSpec Angular (NG.generateAngularJS NG.defAngularOptions)
|
|
||||||
generateJSSpec AngularCustom (NG.generateAngularJSWith NG.defAngularOptions customOptions)
|
|
||||||
generateJSSpec Axios (AX.generateAxiosJS AX.defAxiosOptions)
|
|
||||||
generateJSSpec AxiosCustom (AX.generateAxiosJSWith (AX.defAxiosOptions { AX.withCredentials = True }) customOptions)
|
|
||||||
|
|
||||||
angularSpec Angular
|
|
||||||
axiosSpec
|
|
||||||
--angularSpec AngularCustom
|
|
||||||
internalSpec
|
|
||||||
|
|
||||||
shouldContain :: Text -> Text -> Expectation
|
|
||||||
a `shouldContain` b = shouldSatisfy a (T.isInfixOf b)
|
|
||||||
|
|
||||||
shouldNotContain :: Text -> Text -> Expectation
|
|
||||||
a `shouldNotContain` b = shouldNotSatisfy a (T.isInfixOf b)
|
|
||||||
|
|
||||||
axiosSpec :: Spec
|
|
||||||
axiosSpec = describe specLabel $ do
|
|
||||||
let reqList = listFromAPI (Proxy :: Proxy NoTypes) (Proxy :: Proxy NoContent) (Proxy :: Proxy TestAPI)
|
|
||||||
it "should add withCredentials when needed" $ do
|
|
||||||
let jsText = genJS withCredOpts $ reqList
|
|
||||||
output jsText
|
|
||||||
jsText `shouldContain` "withCredentials: true"
|
|
||||||
it "should add xsrfCookieName when needed" $ do
|
|
||||||
let jsText = genJS cookieOpts $ reqList
|
|
||||||
output jsText
|
|
||||||
jsText `shouldContain` ("xsrfCookieName: 'MyXSRFcookie'")
|
|
||||||
it "should add withCredentials when needed" $ do
|
|
||||||
let jsText = genJS headerOpts $ reqList
|
|
||||||
output jsText
|
|
||||||
jsText `shouldContain` ("xsrfHeaderName: 'MyXSRFheader'")
|
|
||||||
where
|
|
||||||
specLabel = "Axios"
|
|
||||||
output _ = return ()
|
|
||||||
withCredOpts = AX.defAxiosOptions { AX.withCredentials = True }
|
|
||||||
cookieOpts = AX.defAxiosOptions { AX.xsrfCookieName = Just "MyXSRFcookie" }
|
|
||||||
headerOpts = AX.defAxiosOptions { AX.xsrfHeaderName = Just "MyXSRFheader" }
|
|
||||||
genJS :: AxiosOptions -> [AjaxReq] -> Text
|
|
||||||
genJS opts req = mconcat . map (AX.generateAxiosJS opts) $ req
|
|
||||||
|
|
||||||
angularSpec :: TestNames -> Spec
|
|
||||||
angularSpec test = describe specLabel $ do
|
|
||||||
let reqList = listFromAPI (Proxy :: Proxy NoTypes) (Proxy :: Proxy NoContent) (Proxy :: Proxy TestAPI)
|
|
||||||
it "should implement a service globally" $ do
|
|
||||||
let jsText = genJS reqList
|
|
||||||
output jsText
|
|
||||||
jsText `shouldContain` (".service('" <> testName <> "'")
|
|
||||||
|
|
||||||
it "should depend on $http service globally" $ do
|
|
||||||
let jsText = genJS reqList
|
|
||||||
output jsText
|
|
||||||
jsText `shouldContain` ("('" <> testName <> "', function($http) {")
|
|
||||||
|
|
||||||
it "should not depend on $http service in handlers" $ do
|
|
||||||
let jsText = genJS reqList
|
|
||||||
output jsText
|
|
||||||
jsText `shouldNotContain` "getsomething($http, "
|
|
||||||
where
|
|
||||||
specLabel = "AngularJS(" <> (show test) <> ")"
|
|
||||||
output _ = return ()
|
|
||||||
testName = "MyService"
|
|
||||||
ngOpts = NG.defAngularOptions { NG.serviceName = testName }
|
|
||||||
genJS req = NG.angularService ngOpts req
|
|
||||||
|
|
||||||
instance Arbitrary T.Text where
|
|
||||||
-- Our arbitrary instance is generating only ASCII, since language-ecmascript lexer
|
|
||||||
-- is currently (October 2016) still a bit naïve
|
|
||||||
arbitrary = fmap T.pack $ listOf $ choose (minBound, '\127')
|
|
||||||
shrink xs = T.pack <$> shrink (T.unpack xs)
|
|
||||||
|
|
||||||
internalSpec :: Spec
|
|
||||||
internalSpec = describe "Internal" $ do
|
|
||||||
it "should generate only valid javascript identifiers for any ASCII route" $ do
|
|
||||||
let parseIdentifier = fmap (T.pack . filter (< '\65536')) . parse identifier ""
|
|
||||||
property $ \x -> let valid = toValidFunctionName x in
|
|
||||||
Right valid == parseIdentifier valid
|
|
||||||
it "should generate a valid javascript identifier when supplied with hyphens, unicode whitespace, non-bmp unicode" $ do
|
|
||||||
toValidFunctionName "a_--a\66352b\6158c\65075" `shouldBe` "a_abc\65075"
|
|
||||||
|
|
||||||
generateJSSpec :: TestNames -> (AjaxReq -> Text) -> Spec
|
|
||||||
generateJSSpec n gen = describe specLabel $ do
|
|
||||||
let parseFromText = parse program ""
|
|
||||||
it "should generate valid javascript" $ do
|
|
||||||
let s = jsForAPI (Proxy :: Proxy TestAPI) (mconcat . map gen)
|
|
||||||
parseFromText s `shouldSatisfy` isRight
|
|
||||||
|
|
||||||
it "should use non-empty function names" $ do
|
|
||||||
let (_ :<|> topLevel) = javascript (Proxy :: Proxy TopLevelRawAPI)
|
|
||||||
output $ genJS (topLevel "GET")
|
|
||||||
parseFromText (genJS $ topLevel "GET") `shouldSatisfy` isRight
|
|
||||||
|
|
||||||
it "should handle simple HTTP headers" $ do
|
|
||||||
let jsText = genJS $ javascript headerHandlingProxy
|
|
||||||
output jsText
|
|
||||||
parseFromText jsText `shouldSatisfy` isRight
|
|
||||||
jsText `shouldContain` "headerFoo"
|
|
||||||
jsText `shouldContain` (header n "Foo" $ "headerFoo")
|
|
||||||
|
|
||||||
it "should handle complex HTTP headers" $ do
|
|
||||||
let jsText = genJS $ javascript customAuthProxy
|
|
||||||
output jsText
|
|
||||||
parseFromText jsText `shouldSatisfy` isRight
|
|
||||||
jsText `shouldContain` "headerAuthorization"
|
|
||||||
jsText `shouldContain` (header n "Authorization" $ "\"Basic \" + headerAuthorization")
|
|
||||||
|
|
||||||
it "should handle complex, custom HTTP headers" $ do
|
|
||||||
let jsText = genJS $ javascript customHeaderProxy
|
|
||||||
output jsText
|
|
||||||
parseFromText jsText `shouldSatisfy` isRight
|
|
||||||
jsText `shouldContain` "headerXMyLovelyHorse"
|
|
||||||
jsText `shouldContain` (header n "X-MyLovelyHorse" $ "\"I am good friends with \" + headerXMyLovelyHorse")
|
|
||||||
|
|
||||||
it "should handle complex, custom HTTP headers (template replacement)" $ do
|
|
||||||
let jsText = genJS $ javascript customHeaderProxy2
|
|
||||||
output jsText
|
|
||||||
parseFromText jsText `shouldSatisfy` isRight
|
|
||||||
jsText `shouldContain` "headerXWhatsForDinner"
|
|
||||||
jsText `shouldContain` (header n "X-WhatsForDinner" $ "\"I would like \" + headerXWhatsForDinner + \" with a cherry on top.\"")
|
|
||||||
|
|
||||||
it "can generate the whole javascript code string at once with jsForAPI" $ do
|
|
||||||
let jsStr = jsForAPI (Proxy :: Proxy TestAPI) (mconcat . map gen)
|
|
||||||
parseFromText jsStr `shouldSatisfy` isRight
|
|
||||||
where
|
|
||||||
specLabel = "generateJS(" <> (show n) <> ")"
|
|
||||||
output _ = return ()
|
|
||||||
genJS req = gen req
|
|
||||||
header :: TestNames -> Text -> Text -> Text
|
|
||||||
header v headerName headerValue
|
|
||||||
| v `elem` [Vanilla, VanillaCustom] = "xhr.setRequestHeader(\"" <> headerName <> "\", " <> headerValue <> ");\n"
|
|
||||||
| otherwise = "headers: { \"" <> headerName <> "\": " <> headerValue <> " }\n"
|
|
|
@ -1,60 +0,0 @@
|
||||||
{-# LANGUAGE DataKinds #-}
|
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
|
||||||
{-# LANGUAGE KindSignatures #-}
|
|
||||||
{-# LANGUAGE PolyKinds #-}
|
|
||||||
{-# LANGUAGE ScopedTypeVariables #-}
|
|
||||||
{-# LANGUAGE TypeFamilies #-}
|
|
||||||
{-# LANGUAGE TypeOperators #-}
|
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
|
||||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
|
||||||
|
|
||||||
module Servant.JSSpec.CustomHeaders where
|
|
||||||
|
|
||||||
import Control.Lens
|
|
||||||
import Data.Monoid
|
|
||||||
import Data.Proxy
|
|
||||||
import Data.Text (pack)
|
|
||||||
import GHC.TypeLits
|
|
||||||
import Servant.API.ContentTypes
|
|
||||||
import Servant.JS.Internal
|
|
||||||
|
|
||||||
-- | This is a hypothetical combinator that fetches an Authorization header.
|
|
||||||
-- The symbol in the header denotes what kind of authentication we are
|
|
||||||
-- using -- Basic, Digest, whatever.
|
|
||||||
data Authorization (sym :: Symbol) a
|
|
||||||
|
|
||||||
instance (KnownSymbol sym, HasForeign lang NoContent api)
|
|
||||||
=> HasForeign lang NoContent (Authorization sym a :> api) where
|
|
||||||
type Foreign NoContent (Authorization sym a :> api) = Foreign NoContent api
|
|
||||||
|
|
||||||
foreignFor lang ftype Proxy req = foreignFor lang ftype (Proxy :: Proxy api) $
|
|
||||||
req & reqHeaders <>~
|
|
||||||
[ ReplaceHeaderArg (Arg "Authorization" NoContent)
|
|
||||||
$ tokenType (pack . symbolVal $ (Proxy :: Proxy sym)) ]
|
|
||||||
where
|
|
||||||
tokenType t = t <> " {Authorization}"
|
|
||||||
|
|
||||||
-- | This is a combinator that fetches an X-MyLovelyHorse header.
|
|
||||||
data MyLovelyHorse a
|
|
||||||
|
|
||||||
instance (HasForeign lang NoContent api)
|
|
||||||
=> HasForeign lang NoContent (MyLovelyHorse a :> api) where
|
|
||||||
type Foreign NoContent (MyLovelyHorse a :> api) = Foreign NoContent api
|
|
||||||
|
|
||||||
foreignFor lang ftype Proxy req = foreignFor lang ftype (Proxy :: Proxy api) $
|
|
||||||
req & reqHeaders <>~ [ ReplaceHeaderArg (Arg "X-MyLovelyHorse" NoContent) tpl ]
|
|
||||||
where
|
|
||||||
tpl = "I am good friends with {X-MyLovelyHorse}"
|
|
||||||
|
|
||||||
-- | This is a combinator that fetches an X-WhatsForDinner header.
|
|
||||||
data WhatsForDinner a
|
|
||||||
|
|
||||||
instance (HasForeign lang NoContent api)
|
|
||||||
=> HasForeign lang NoContent (WhatsForDinner a :> api) where
|
|
||||||
type Foreign NoContent (WhatsForDinner a :> api) = Foreign NoContent api
|
|
||||||
|
|
||||||
foreignFor lang ftype Proxy req = foreignFor lang ftype (Proxy :: Proxy api) $
|
|
||||||
req & reqHeaders <>~ [ ReplaceHeaderArg (Arg "X-WhatsForDinner" NoContent) tpl ]
|
|
||||||
where
|
|
||||||
tpl = "I would like {X-WhatsForDinner} with a cherry on top."
|
|
|
@ -1,2 +0,0 @@
|
||||||
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
dependencies:
|
|
||||||
- name: servant
|
|
||||||
path: ../servant
|
|
||||||
- name: servant-server
|
|
||||||
path: ../servant-server
|
|
||||||
- name: servant-foreign
|
|
||||||
path: ../servant-foreign
|
|
|
@ -1 +0,0 @@
|
||||||
:set -Wall -itest -isrc -optP-include -optPdist/build/autogen/cabal_macros.h -Iinclude
|
|
|
@ -1,30 +0,0 @@
|
||||||
Copyright (c) 2015-2016, Servant Contributors
|
|
||||||
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following
|
|
||||||
disclaimer in the documentation and/or other materials provided
|
|
||||||
with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of Alp Mestanogullari nor the names of other
|
|
||||||
contributors may be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
@ -1,2 +0,0 @@
|
||||||
import Distribution.Simple
|
|
||||||
main = defaultMain
|
|
|
@ -1,26 +0,0 @@
|
||||||
{-# LANGUAGE DataKinds #-}
|
|
||||||
{-# LANGUAGE DeriveGeneric #-}
|
|
||||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
|
||||||
{-# LANGUAGE TypeOperators #-}
|
|
||||||
|
|
||||||
{-# OPTIONS_GHC -fno-warn-unused-binds #-}
|
|
||||||
|
|
||||||
import Data.Aeson
|
|
||||||
import GHC.Generics
|
|
||||||
import Network.Wai.Handler.Warp
|
|
||||||
import Servant
|
|
||||||
import Servant.Mock
|
|
||||||
import Test.QuickCheck.Arbitrary
|
|
||||||
|
|
||||||
newtype User = User { username :: String }
|
|
||||||
deriving (Eq, Show, Arbitrary, Generic)
|
|
||||||
|
|
||||||
instance ToJSON User
|
|
||||||
|
|
||||||
type API = "user" :> Get '[JSON] User
|
|
||||||
|
|
||||||
api :: Proxy API
|
|
||||||
api = Proxy
|
|
||||||
|
|
||||||
main :: IO ()
|
|
||||||
main = run 8080 (serve api $ mock api Proxy)
|
|
|
@ -1,8 +0,0 @@
|
||||||
#if __GLASGOW_HASKELL__ >= 710
|
|
||||||
#define OVERLAPPABLE_ {-# OVERLAPPABLE #-}
|
|
||||||
#define OVERLAPPING_ {-# OVERLAPPING #-}
|
|
||||||
#else
|
|
||||||
{-# LANGUAGE OverlappingInstances #-}
|
|
||||||
#define OVERLAPPABLE_
|
|
||||||
#define OVERLAPPING_
|
|
||||||
#endif
|
|
|
@ -1,73 +0,0 @@
|
||||||
name: servant-mock
|
|
||||||
version: 0.8
|
|
||||||
synopsis: Derive a mock server for free from your servant API types
|
|
||||||
description:
|
|
||||||
Derive a mock server for free from your servant API types
|
|
||||||
.
|
|
||||||
See the @Servant.Mock@ module for the documentation and an example.
|
|
||||||
homepage: http://github.com/haskell-servant/servant
|
|
||||||
license: BSD3
|
|
||||||
license-file: LICENSE
|
|
||||||
author: Servant Contributors
|
|
||||||
maintainer: haskell-servant-maintainers@googlegroups.com
|
|
||||||
copyright: 2015-2016 Servant Contributors
|
|
||||||
category: Web
|
|
||||||
build-type: Simple
|
|
||||||
extra-source-files: include/*.h
|
|
||||||
cabal-version: >=1.10
|
|
||||||
bug-reports: http://github.com/haskell-servant/servant/issues
|
|
||||||
source-repository head
|
|
||||||
type: git
|
|
||||||
location: http://github.com/haskell-servant/servant.git
|
|
||||||
|
|
||||||
flag example
|
|
||||||
description: Build the example too
|
|
||||||
default: True
|
|
||||||
|
|
||||||
library
|
|
||||||
exposed-modules:
|
|
||||||
Servant.Mock
|
|
||||||
build-depends:
|
|
||||||
base >=4.7 && <5,
|
|
||||||
bytestring >= 0.10 && <0.11,
|
|
||||||
http-types >= 0.8 && <0.10,
|
|
||||||
servant == 0.8.*,
|
|
||||||
servant-server == 0.8.*,
|
|
||||||
transformers >= 0.3 && <0.6,
|
|
||||||
QuickCheck >= 2.7 && <2.9,
|
|
||||||
wai >= 3.0 && <3.3
|
|
||||||
hs-source-dirs: src
|
|
||||||
default-language: Haskell2010
|
|
||||||
include-dirs: include
|
|
||||||
ghc-options: -Wall
|
|
||||||
|
|
||||||
executable mock-app
|
|
||||||
main-is: main.hs
|
|
||||||
hs-source-dirs: example
|
|
||||||
default-language: Haskell2010
|
|
||||||
build-depends: aeson, base, servant-mock, servant-server, QuickCheck, warp
|
|
||||||
if flag(example)
|
|
||||||
buildable: True
|
|
||||||
else
|
|
||||||
buildable: False
|
|
||||||
ghc-options: -Wall
|
|
||||||
|
|
||||||
test-suite spec
|
|
||||||
type: exitcode-stdio-1.0
|
|
||||||
ghc-options: -Wall
|
|
||||||
default-language: Haskell2010
|
|
||||||
hs-source-dirs: test
|
|
||||||
main-is: Spec.hs
|
|
||||||
other-modules:
|
|
||||||
Servant.MockSpec
|
|
||||||
build-depends:
|
|
||||||
base,
|
|
||||||
hspec,
|
|
||||||
hspec-wai,
|
|
||||||
QuickCheck,
|
|
||||||
servant,
|
|
||||||
servant-server,
|
|
||||||
servant-mock,
|
|
||||||
aeson,
|
|
||||||
bytestring-conversion,
|
|
||||||
wai
|
|
|
@ -1,188 +0,0 @@
|
||||||
{-# LANGUAGE CPP #-}
|
|
||||||
{-# LANGUAGE DataKinds #-}
|
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
|
||||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
|
||||||
{-# LANGUAGE PolyKinds #-}
|
|
||||||
{-# LANGUAGE ScopedTypeVariables #-}
|
|
||||||
{-# LANGUAGE TypeFamilies #-}
|
|
||||||
{-# LANGUAGE TypeOperators #-}
|
|
||||||
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
|
||||||
|
|
||||||
#include "overlapping-compat.h"
|
|
||||||
|
|
||||||
-- |
|
|
||||||
-- Module : Servant.Mock
|
|
||||||
-- Copyright : 2015 Alp Mestanogullari
|
|
||||||
-- License : BSD3
|
|
||||||
--
|
|
||||||
-- Maintainer : Alp Mestanogullari <alpmestan@gmail.com>
|
|
||||||
-- Stability : experimental
|
|
||||||
-- Portability : portable
|
|
||||||
--
|
|
||||||
-- Automatically derive a mock webserver that implements some API type,
|
|
||||||
-- just from the said API type's definition.
|
|
||||||
--
|
|
||||||
-- Using this module couldn't be simpler. Given some API type, like:
|
|
||||||
--
|
|
||||||
-- > type API = "user" :> Get '[JSON] User
|
|
||||||
--
|
|
||||||
-- that describes your web application, all you have to do is define
|
|
||||||
-- a 'Proxy' to it:
|
|
||||||
--
|
|
||||||
-- > myAPI :: Proxy API
|
|
||||||
-- > myAPI = Proxy
|
|
||||||
--
|
|
||||||
-- and call 'mock', which has the following type:
|
|
||||||
--
|
|
||||||
-- @
|
|
||||||
-- 'mock' :: 'HasMock' api context => 'Proxy' api -> 'Proxy' context -> 'Server' api
|
|
||||||
-- @
|
|
||||||
--
|
|
||||||
-- What this says is, given some API type @api@ that it knows it can
|
|
||||||
-- "mock", 'mock' hands you an implementation of the API type. It does so
|
|
||||||
-- by having each request handler generate a random value of the
|
|
||||||
-- appropriate type (@User@ in our case). All you need for this to work is
|
|
||||||
-- to provide 'Arbitrary' instances for the data types returned as response
|
|
||||||
-- bodies, hence appearing next to 'Delete', 'Get', 'Patch', 'Post' and 'Put'.
|
|
||||||
--
|
|
||||||
-- To put this all to work and run the mock server, just call 'serve' on the
|
|
||||||
-- result of 'mock' to get an 'Application' that you can then run with warp.
|
|
||||||
--
|
|
||||||
-- @
|
|
||||||
-- main :: IO ()
|
|
||||||
-- main = Network.Wai.Handler.Warp.run 8080 $
|
|
||||||
-- 'serve' myAPI ('mock' myAPI Proxy)
|
|
||||||
-- @
|
|
||||||
module Servant.Mock ( HasMock(..) ) where
|
|
||||||
|
|
||||||
#if !MIN_VERSION_base(4,8,0)
|
|
||||||
import Control.Applicative
|
|
||||||
#endif
|
|
||||||
import Control.Monad.IO.Class
|
|
||||||
import Data.ByteString.Lazy.Char8 (pack)
|
|
||||||
import Data.Proxy
|
|
||||||
import GHC.TypeLits
|
|
||||||
import Network.HTTP.Types.Status
|
|
||||||
import Network.Wai
|
|
||||||
import Servant
|
|
||||||
import Servant.API.ContentTypes
|
|
||||||
import Test.QuickCheck.Arbitrary (Arbitrary (..), vector)
|
|
||||||
import Test.QuickCheck.Gen (Gen, generate)
|
|
||||||
|
|
||||||
-- | 'HasMock' defines an interpretation of API types
|
|
||||||
-- than turns them into random-response-generating
|
|
||||||
-- request handlers, hence providing an instance for
|
|
||||||
-- all the combinators of the core /servant/ library.
|
|
||||||
class HasServer api context => HasMock api context where
|
|
||||||
-- | Calling this method creates request handlers of
|
|
||||||
-- the right type to implement the API described by
|
|
||||||
-- @api@ that just generate random response values of
|
|
||||||
-- the right type. E.g:
|
|
||||||
--
|
|
||||||
-- @
|
|
||||||
-- type API = "user" :> Get '[JSON] User
|
|
||||||
-- :<|> "book" :> Get '[JSON] Book
|
|
||||||
--
|
|
||||||
-- api :: Proxy API
|
|
||||||
-- api = Proxy
|
|
||||||
--
|
|
||||||
-- -- let's say we will start with the frontend,
|
|
||||||
-- -- and hence need a placeholder server
|
|
||||||
-- server :: Server API
|
|
||||||
-- server = mock api Proxy
|
|
||||||
-- @
|
|
||||||
--
|
|
||||||
-- What happens here is that @'Server' API@
|
|
||||||
-- actually "means" 2 request handlers, of the following types:
|
|
||||||
--
|
|
||||||
-- @
|
|
||||||
-- getUser :: Handler User
|
|
||||||
-- getBook :: Handler Book
|
|
||||||
-- @
|
|
||||||
--
|
|
||||||
-- So under the hood, 'mock' uses the 'IO' bit to generate
|
|
||||||
-- random values of type 'User' and 'Book' every time these
|
|
||||||
-- endpoints are requested.
|
|
||||||
mock :: Proxy api -> Proxy context -> Server api
|
|
||||||
|
|
||||||
instance (HasMock a context, HasMock b context) => HasMock (a :<|> b) context where
|
|
||||||
mock _ context = mock (Proxy :: Proxy a) context :<|> mock (Proxy :: Proxy b) context
|
|
||||||
|
|
||||||
instance (KnownSymbol path, HasMock rest context) => HasMock (path :> rest) context where
|
|
||||||
mock _ = mock (Proxy :: Proxy rest)
|
|
||||||
|
|
||||||
instance (KnownSymbol s, FromHttpApiData a, HasMock rest context) => HasMock (Capture s a :> rest) context where
|
|
||||||
mock _ context = \_ -> mock (Proxy :: Proxy rest) context
|
|
||||||
|
|
||||||
instance (KnownSymbol s, FromHttpApiData a, HasMock rest context) => HasMock (CaptureAll s a :> rest) context where
|
|
||||||
mock _ context = \_ -> mock (Proxy :: Proxy rest) context
|
|
||||||
|
|
||||||
instance (AllCTUnrender ctypes a, HasMock rest context) => HasMock (ReqBody ctypes a :> rest) context where
|
|
||||||
mock _ context = \_ -> mock (Proxy :: Proxy rest) context
|
|
||||||
|
|
||||||
instance HasMock rest context => HasMock (RemoteHost :> rest) context where
|
|
||||||
mock _ context = \_ -> mock (Proxy :: Proxy rest) context
|
|
||||||
|
|
||||||
instance HasMock rest context => HasMock (IsSecure :> rest) context where
|
|
||||||
mock _ context = \_ -> mock (Proxy :: Proxy rest) context
|
|
||||||
|
|
||||||
instance HasMock rest context => HasMock (Vault :> rest) context where
|
|
||||||
mock _ context = \_ -> mock (Proxy :: Proxy rest) context
|
|
||||||
|
|
||||||
instance HasMock rest context => HasMock (HttpVersion :> rest) context where
|
|
||||||
mock _ context = \_ -> mock (Proxy :: Proxy rest) context
|
|
||||||
|
|
||||||
instance (KnownSymbol s, FromHttpApiData a, HasMock rest context)
|
|
||||||
=> HasMock (QueryParam s a :> rest) context where
|
|
||||||
mock _ context = \_ -> mock (Proxy :: Proxy rest) context
|
|
||||||
|
|
||||||
instance (KnownSymbol s, FromHttpApiData a, HasMock rest context)
|
|
||||||
=> HasMock (QueryParams s a :> rest) context where
|
|
||||||
mock _ context = \_ -> mock (Proxy :: Proxy rest) context
|
|
||||||
|
|
||||||
instance (KnownSymbol s, HasMock rest context) => HasMock (QueryFlag s :> rest) context where
|
|
||||||
mock _ context = \_ -> mock (Proxy :: Proxy rest) context
|
|
||||||
|
|
||||||
instance (KnownSymbol h, FromHttpApiData a, HasMock rest context) => HasMock (Header h a :> rest) context where
|
|
||||||
mock _ context = \_ -> mock (Proxy :: Proxy rest) context
|
|
||||||
|
|
||||||
instance (Arbitrary a, KnownNat status, ReflectMethod method, AllCTRender ctypes a)
|
|
||||||
=> HasMock (Verb method status ctypes a) context where
|
|
||||||
mock _ _ = mockArbitrary
|
|
||||||
|
|
||||||
instance OVERLAPPING_
|
|
||||||
(GetHeaders (Headers headerTypes a), Arbitrary (HList headerTypes),
|
|
||||||
Arbitrary a, KnownNat status, ReflectMethod method, AllCTRender ctypes a)
|
|
||||||
=> HasMock (Verb method status ctypes (Headers headerTypes a)) context where
|
|
||||||
mock _ _ = mockArbitrary
|
|
||||||
|
|
||||||
instance HasMock Raw context where
|
|
||||||
mock _ _ = \_req respond -> do
|
|
||||||
bdy <- genBody
|
|
||||||
respond $ responseLBS status200 [] bdy
|
|
||||||
|
|
||||||
where genBody = pack <$> generate (vector 100 :: Gen [Char])
|
|
||||||
|
|
||||||
instance (HasContextEntry context (NamedContext name subContext), HasMock rest subContext) =>
|
|
||||||
HasMock (WithNamedContext name subContext rest) context where
|
|
||||||
|
|
||||||
mock _ _ = mock (Proxy :: Proxy rest) (Proxy :: Proxy subContext)
|
|
||||||
|
|
||||||
mockArbitrary :: (MonadIO m, Arbitrary a) => m a
|
|
||||||
mockArbitrary = liftIO (generate arbitrary)
|
|
||||||
|
|
||||||
-- utility instance
|
|
||||||
instance (Arbitrary (HList ls), Arbitrary a)
|
|
||||||
=> Arbitrary (Headers ls a) where
|
|
||||||
arbitrary = Headers <$> arbitrary <*> arbitrary
|
|
||||||
|
|
||||||
instance Arbitrary (HList '[]) where
|
|
||||||
arbitrary = pure HNil
|
|
||||||
|
|
||||||
instance (Arbitrary a, Arbitrary (HList hs))
|
|
||||||
=> Arbitrary (HList (Header h a ': hs)) where
|
|
||||||
arbitrary = HCons <$> fmap Header arbitrary <*> arbitrary
|
|
||||||
|
|
||||||
instance Arbitrary NoContent where
|
|
||||||
arbitrary = pure NoContent
|
|
|
@ -1,85 +0,0 @@
|
||||||
{-# LANGUAGE ConstraintKinds #-}
|
|
||||||
{-# LANGUAGE DataKinds #-}
|
|
||||||
{-# LANGUAGE DeriveGeneric #-}
|
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
|
||||||
{-# LANGUAGE TypeOperators #-}
|
|
||||||
|
|
||||||
module Servant.MockSpec where
|
|
||||||
|
|
||||||
import Data.Aeson as Aeson
|
|
||||||
import Data.ByteString.Conversion.To
|
|
||||||
import Data.Proxy
|
|
||||||
import Data.String
|
|
||||||
import GHC.Generics
|
|
||||||
import Network.Wai
|
|
||||||
import Servant.API
|
|
||||||
import Test.Hspec hiding (pending)
|
|
||||||
import Test.Hspec.Wai
|
|
||||||
import Test.QuickCheck
|
|
||||||
|
|
||||||
import Servant
|
|
||||||
import Servant.API.Internal.Test.ComprehensiveAPI
|
|
||||||
import Servant.Mock
|
|
||||||
|
|
||||||
-- This declaration simply checks that all instances are in place.
|
|
||||||
_ = mock comprehensiveAPI (Proxy :: Proxy '[NamedContext "foo" '[]])
|
|
||||||
|
|
||||||
data Body
|
|
||||||
= Body
|
|
||||||
| ArbitraryBody
|
|
||||||
deriving (Generic)
|
|
||||||
|
|
||||||
instance ToJSON Body
|
|
||||||
|
|
||||||
instance Arbitrary Body where
|
|
||||||
arbitrary = return ArbitraryBody
|
|
||||||
|
|
||||||
data TestHeader
|
|
||||||
= TestHeader
|
|
||||||
| ArbitraryHeader
|
|
||||||
deriving (Show)
|
|
||||||
|
|
||||||
instance ToByteString TestHeader where
|
|
||||||
builder = fromString . show
|
|
||||||
|
|
||||||
instance Arbitrary TestHeader where
|
|
||||||
arbitrary = return ArbitraryHeader
|
|
||||||
|
|
||||||
spec :: Spec
|
|
||||||
spec = do
|
|
||||||
describe "mock" $ do
|
|
||||||
context "Get" $ do
|
|
||||||
let api :: Proxy (Get '[JSON] Body)
|
|
||||||
api = Proxy
|
|
||||||
app = serve api (mock api Proxy)
|
|
||||||
with (return app) $ do
|
|
||||||
it "serves arbitrary response bodies" $ do
|
|
||||||
get "/" `shouldRespondWith` 200{
|
|
||||||
matchBody = Just $ Aeson.encode ArbitraryBody
|
|
||||||
}
|
|
||||||
|
|
||||||
context "response headers" $ do
|
|
||||||
let withHeader :: Proxy (Get '[JSON] (Headers '[Header "foo" TestHeader] Body))
|
|
||||||
withHeader = Proxy
|
|
||||||
withoutHeader :: Proxy (Get '[JSON] (Headers '[] Body))
|
|
||||||
withoutHeader = Proxy
|
|
||||||
toApp :: (HasMock api '[]) => Proxy api -> IO Application
|
|
||||||
toApp api = return $ serve api (mock api (Proxy :: Proxy '[]))
|
|
||||||
with (toApp withHeader) $ do
|
|
||||||
it "serves arbitrary response bodies" $ do
|
|
||||||
get "/" `shouldRespondWith` 200{
|
|
||||||
matchHeaders = return $ MatchHeader $ \ h ->
|
|
||||||
if h == [("Content-Type", "application/json"), ("foo", "ArbitraryHeader")]
|
|
||||||
then Nothing
|
|
||||||
else Just ("headers not correct\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
with (toApp withoutHeader) $ do
|
|
||||||
it "works for no additional headers" $ do
|
|
||||||
get "/" `shouldRespondWith` 200{
|
|
||||||
matchHeaders = return $ MatchHeader $ \ h ->
|
|
||||||
if h == [("Content-Type", "application/json")]
|
|
||||||
then Nothing
|
|
||||||
else Just ("headers not correct\n")
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
|
|
|
@ -1,5 +0,0 @@
|
||||||
dependencies:
|
|
||||||
- name: servant
|
|
||||||
path: ../servant
|
|
||||||
- name: servant-server
|
|
||||||
path: ../servant-server
|
|
|
@ -1,5 +1,5 @@
|
||||||
name: servant-server
|
name: servant-server
|
||||||
version: 0.8
|
version: 0.9
|
||||||
synopsis: A family of combinators for defining webservices APIs and serving them
|
synopsis: A family of combinators for defining webservices APIs and serving them
|
||||||
description:
|
description:
|
||||||
A family of combinators for defining webservices APIs and serving them
|
A family of combinators for defining webservices APIs and serving them
|
||||||
|
@ -47,18 +47,18 @@ library
|
||||||
build-depends:
|
build-depends:
|
||||||
base >= 4.7 && < 4.10
|
base >= 4.7 && < 4.10
|
||||||
, base-compat >= 0.9 && < 0.10
|
, base-compat >= 0.9 && < 0.10
|
||||||
, aeson >= 0.7 && < 0.12
|
, aeson >= 0.7 && < 1.1
|
||||||
, attoparsec >= 0.12 && < 0.14
|
, attoparsec >= 0.12 && < 0.14
|
||||||
, base64-bytestring >= 1.0 && < 1.1
|
, base64-bytestring >= 1.0 && < 1.1
|
||||||
, bytestring >= 0.10 && < 0.11
|
, bytestring >= 0.10 && < 0.11
|
||||||
, containers >= 0.5 && < 0.6
|
, containers >= 0.5 && < 0.6
|
||||||
, http-api-data >= 0.1 && < 0.3
|
, http-api-data >= 0.3 && < 0.4
|
||||||
, http-types >= 0.8 && < 0.10
|
, http-types >= 0.8 && < 0.10
|
||||||
, network-uri >= 2.6 && < 2.7
|
, network-uri >= 2.6 && < 2.7
|
||||||
, mtl >= 2 && < 2.3
|
, mtl >= 2 && < 2.3
|
||||||
, network >= 2.6 && < 2.7
|
, network >= 2.6 && < 2.7
|
||||||
, safe >= 0.3 && < 0.4
|
, safe >= 0.3 && < 0.4
|
||||||
, servant == 0.8.*
|
, servant == 0.9.*
|
||||||
, split >= 0.2 && < 0.3
|
, split >= 0.2 && < 0.3
|
||||||
, string-conversions >= 0.3 && < 0.5
|
, string-conversions >= 0.3 && < 0.5
|
||||||
, system-filepath >= 0.4 && < 0.5
|
, system-filepath >= 0.4 && < 0.5
|
||||||
|
@ -99,6 +99,7 @@ test-suite spec
|
||||||
hs-source-dirs: test
|
hs-source-dirs: test
|
||||||
main-is: Spec.hs
|
main-is: Spec.hs
|
||||||
other-modules:
|
other-modules:
|
||||||
|
Servant.ArbitraryMonadServerSpec
|
||||||
Servant.Server.ErrorSpec
|
Servant.Server.ErrorSpec
|
||||||
Servant.Server.Internal.ContextSpec
|
Servant.Server.Internal.ContextSpec
|
||||||
Servant.Server.RouterSpec
|
Servant.Server.RouterSpec
|
||||||
|
@ -113,7 +114,6 @@ test-suite spec
|
||||||
, aeson
|
, aeson
|
||||||
, base64-bytestring
|
, base64-bytestring
|
||||||
, bytestring
|
, bytestring
|
||||||
, bytestring-conversion
|
|
||||||
, directory
|
, directory
|
||||||
, exceptions
|
, exceptions
|
||||||
, hspec == 2.*
|
, hspec == 2.*
|
||||||
|
|
|
@ -42,12 +42,10 @@ import Network.Wai (Application, Request, Response,
|
||||||
responseLBS, vault)
|
responseLBS, vault)
|
||||||
import Prelude ()
|
import Prelude ()
|
||||||
import Prelude.Compat
|
import Prelude.Compat
|
||||||
import Web.HttpApiData (FromHttpApiData)
|
import Web.HttpApiData (FromHttpApiData, parseHeaderMaybe,
|
||||||
import Web.HttpApiData.Internal (parseHeaderMaybe,
|
|
||||||
parseQueryParamMaybe,
|
parseQueryParamMaybe,
|
||||||
parseUrlPieceMaybe,
|
parseUrlPieceMaybe,
|
||||||
parseUrlPieces)
|
parseUrlPieces)
|
||||||
|
|
||||||
import Servant.API ((:<|>) (..), (:>), BasicAuth, Capture,
|
import Servant.API ((:<|>) (..), (:>), BasicAuth, Capture,
|
||||||
CaptureAll, Verb,
|
CaptureAll, Verb,
|
||||||
ReflectMethod(reflectMethod),
|
ReflectMethod(reflectMethod),
|
||||||
|
|
|
@ -17,7 +17,6 @@ import Control.Monad (forM_, when, unless)
|
||||||
import Control.Monad.Trans.Except (throwE)
|
import Control.Monad.Trans.Except (throwE)
|
||||||
import Data.Aeson (FromJSON, ToJSON, decode', encode)
|
import Data.Aeson (FromJSON, ToJSON, decode', encode)
|
||||||
import qualified Data.ByteString.Base64 as Base64
|
import qualified Data.ByteString.Base64 as Base64
|
||||||
import Data.ByteString.Conversion ()
|
|
||||||
import Data.Char (toUpper)
|
import Data.Char (toUpper)
|
||||||
import Data.Monoid
|
import Data.Monoid
|
||||||
import Data.Proxy (Proxy (Proxy))
|
import Data.Proxy (Proxy (Proxy))
|
||||||
|
|
|
@ -1,3 +1,15 @@
|
||||||
|
0.9
|
||||||
|
---
|
||||||
|
|
||||||
|
* Added Eq, Show, Read, Generic and Ord instances to IsSecure
|
||||||
|
* BACKWARDS INCOMPATIBLE: replace use of `ToFromByteString` with `To/FromHttpApiData` for `GetHeaders/BuildHeadersTo`
|
||||||
|
* BACKWARDS INCOMPATIBLE: Moved `From/ToFormUrlEncoded` classes, which were renamed to `From/ToForm` to `http-api-data`
|
||||||
|
|
||||||
|
0.8.1
|
||||||
|
----
|
||||||
|
|
||||||
|
* Add `CaptureAll` combinator. Captures all of the remaining segments in a URL.
|
||||||
|
|
||||||
0.8
|
0.8
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name: servant
|
name: servant
|
||||||
version: 0.8
|
version: 0.9
|
||||||
synopsis: A family of combinators for defining webservices APIs
|
synopsis: A family of combinators for defining webservices APIs
|
||||||
description:
|
description:
|
||||||
A family of combinators for defining webservices APIs and serving them
|
A family of combinators for defining webservices APIs and serving them
|
||||||
|
@ -51,12 +51,11 @@ library
|
||||||
build-depends:
|
build-depends:
|
||||||
base >= 4.7 && < 4.10
|
base >= 4.7 && < 4.10
|
||||||
, base-compat >= 0.9 && < 0.10
|
, base-compat >= 0.9 && < 0.10
|
||||||
, aeson >= 0.7 && < 0.12
|
, aeson >= 0.7 && < 1.1
|
||||||
, attoparsec >= 0.12 && < 0.14
|
, attoparsec >= 0.12 && < 0.14
|
||||||
, bytestring >= 0.10 && < 0.11
|
, bytestring >= 0.10 && < 0.11
|
||||||
, bytestring-conversion >= 0.3 && < 0.4
|
|
||||||
, case-insensitive >= 1.2 && < 1.3
|
, case-insensitive >= 1.2 && < 1.3
|
||||||
, http-api-data >= 0.1 && < 0.3
|
, http-api-data >= 0.3 && < 0.4
|
||||||
, http-media >= 0.4 && < 0.7
|
, http-media >= 0.4 && < 0.7
|
||||||
, http-types >= 0.8 && < 0.10
|
, http-types >= 0.8 && < 0.10
|
||||||
, mtl >= 2.0 && < 2.3
|
, mtl >= 2.0 && < 2.3
|
||||||
|
|
|
@ -62,10 +62,10 @@ import Servant.API.Alternative ((:<|>) (..))
|
||||||
import Servant.API.BasicAuth (BasicAuth,BasicAuthData(..))
|
import Servant.API.BasicAuth (BasicAuth,BasicAuthData(..))
|
||||||
import Servant.API.Capture (Capture, CaptureAll)
|
import Servant.API.Capture (Capture, CaptureAll)
|
||||||
import Servant.API.ContentTypes (Accept (..), FormUrlEncoded,
|
import Servant.API.ContentTypes (Accept (..), FormUrlEncoded,
|
||||||
FromFormUrlEncoded (..), JSON,
|
JSON,
|
||||||
MimeRender (..), NoContent (NoContent),
|
MimeRender (..), NoContent (NoContent),
|
||||||
MimeUnrender (..), OctetStream,
|
MimeUnrender (..), OctetStream,
|
||||||
PlainText, ToFormUrlEncoded (..))
|
PlainText)
|
||||||
import Servant.API.Experimental.Auth (AuthProtect)
|
import Servant.API.Experimental.Auth (AuthProtect)
|
||||||
import Servant.API.Header (Header (..))
|
import Servant.API.Header (Header (..))
|
||||||
import Servant.API.HttpVersion (HttpVersion (..))
|
import Servant.API.HttpVersion (HttpVersion (..))
|
||||||
|
|
|
@ -66,8 +66,6 @@ module Servant.API.ContentTypes
|
||||||
, AllMime(..)
|
, AllMime(..)
|
||||||
, AllMimeRender(..)
|
, AllMimeRender(..)
|
||||||
, AllMimeUnrender(..)
|
, AllMimeUnrender(..)
|
||||||
, FromFormUrlEncoded(..)
|
|
||||||
, ToFormUrlEncoded(..)
|
|
||||||
, eitherDecodeLenient
|
, eitherDecodeLenient
|
||||||
, canHandleAcceptH
|
, canHandleAcceptH
|
||||||
) where
|
) where
|
||||||
|
@ -82,10 +80,8 @@ import Data.Attoparsec.ByteString.Char8 (endOfInput, parseOnly,
|
||||||
import qualified Data.ByteString as BS
|
import qualified Data.ByteString as BS
|
||||||
import Data.ByteString.Lazy (ByteString, fromStrict,
|
import Data.ByteString.Lazy (ByteString, fromStrict,
|
||||||
toStrict)
|
toStrict)
|
||||||
import qualified Data.ByteString.Lazy as B
|
|
||||||
import qualified Data.ByteString.Lazy.Char8 as BC
|
import qualified Data.ByteString.Lazy.Char8 as BC
|
||||||
import Data.Maybe (isJust)
|
import Data.Maybe (isJust)
|
||||||
import Data.Monoid.Compat
|
|
||||||
import Data.String.Conversions (cs)
|
import Data.String.Conversions (cs)
|
||||||
import qualified Data.Text as TextS
|
import qualified Data.Text as TextS
|
||||||
import qualified Data.Text.Encoding as TextS
|
import qualified Data.Text.Encoding as TextS
|
||||||
|
@ -94,8 +90,9 @@ import qualified Data.Text.Lazy.Encoding as TextL
|
||||||
import Data.Typeable
|
import Data.Typeable
|
||||||
import GHC.Generics (Generic)
|
import GHC.Generics (Generic)
|
||||||
import qualified Network.HTTP.Media as M
|
import qualified Network.HTTP.Media as M
|
||||||
import Network.URI (escapeURIString,
|
import Web.FormUrlEncoded (FromForm, ToForm,
|
||||||
isUnreserved, unEscapeString)
|
urlEncodeAsForm,
|
||||||
|
urlDecodeAsForm)
|
||||||
import Prelude ()
|
import Prelude ()
|
||||||
import Prelude.Compat
|
import Prelude.Compat
|
||||||
|
|
||||||
|
@ -290,12 +287,12 @@ instance OVERLAPPABLE_
|
||||||
ToJSON a => MimeRender JSON a where
|
ToJSON a => MimeRender JSON a where
|
||||||
mimeRender _ = encode
|
mimeRender _ = encode
|
||||||
|
|
||||||
-- | @encodeFormUrlEncoded . toFormUrlEncoded@
|
-- | @urlEncodeAsForm@
|
||||||
-- Note that the @mimeUnrender p (mimeRender p x) == Right x@ law only
|
-- Note that the @mimeUnrender p (mimeRender p x) == Right x@ law only
|
||||||
-- holds if every element of x is non-null (i.e., not @("", "")@)
|
-- holds if every element of x is non-null (i.e., not @("", "")@)
|
||||||
instance OVERLAPPABLE_
|
instance OVERLAPPABLE_
|
||||||
ToFormUrlEncoded a => MimeRender FormUrlEncoded a where
|
ToForm a => MimeRender FormUrlEncoded a where
|
||||||
mimeRender _ = encodeFormUrlEncoded . toFormUrlEncoded
|
mimeRender _ = urlEncodeAsForm
|
||||||
|
|
||||||
-- | `TextL.encodeUtf8`
|
-- | `TextL.encodeUtf8`
|
||||||
instance MimeRender PlainText TextL.Text where
|
instance MimeRender PlainText TextL.Text where
|
||||||
|
@ -348,11 +345,11 @@ eitherDecodeLenient input =
|
||||||
instance FromJSON a => MimeUnrender JSON a where
|
instance FromJSON a => MimeUnrender JSON a where
|
||||||
mimeUnrender _ = eitherDecodeLenient
|
mimeUnrender _ = eitherDecodeLenient
|
||||||
|
|
||||||
-- | @decodeFormUrlEncoded >=> fromFormUrlEncoded@
|
-- | @urlDecodeAsForm@
|
||||||
-- Note that the @mimeUnrender p (mimeRender p x) == Right x@ law only
|
-- Note that the @mimeUnrender p (mimeRender p x) == Right x@ law only
|
||||||
-- holds if every element of x is non-null (i.e., not @("", "")@)
|
-- holds if every element of x is non-null (i.e., not @("", "")@)
|
||||||
instance FromFormUrlEncoded a => MimeUnrender FormUrlEncoded a where
|
instance FromForm a => MimeUnrender FormUrlEncoded a where
|
||||||
mimeUnrender _ = decodeFormUrlEncoded >=> fromFormUrlEncoded
|
mimeUnrender _ = left TextS.unpack . urlDecodeAsForm
|
||||||
|
|
||||||
-- | @left show . TextL.decodeUtf8'@
|
-- | @left show . TextL.decodeUtf8'@
|
||||||
instance MimeUnrender PlainText TextL.Text where
|
instance MimeUnrender PlainText TextL.Text where
|
||||||
|
@ -375,49 +372,6 @@ instance MimeUnrender OctetStream BS.ByteString where
|
||||||
mimeUnrender _ = Right . toStrict
|
mimeUnrender _ = Right . toStrict
|
||||||
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------
|
|
||||||
-- * FormUrlEncoded
|
|
||||||
|
|
||||||
-- | A type that can be converted to @application/x-www-form-urlencoded@
|
|
||||||
class ToFormUrlEncoded a where
|
|
||||||
toFormUrlEncoded :: a -> [(TextS.Text, TextS.Text)]
|
|
||||||
|
|
||||||
instance ToFormUrlEncoded [(TextS.Text, TextS.Text)] where
|
|
||||||
toFormUrlEncoded = id
|
|
||||||
|
|
||||||
-- | A type that can be converted from @application/x-www-form-urlencoded@,
|
|
||||||
-- with the possibility of failure.
|
|
||||||
class FromFormUrlEncoded a where
|
|
||||||
fromFormUrlEncoded :: [(TextS.Text, TextS.Text)] -> Either String a
|
|
||||||
|
|
||||||
instance FromFormUrlEncoded [(TextS.Text, TextS.Text)] where
|
|
||||||
fromFormUrlEncoded = return
|
|
||||||
|
|
||||||
encodeFormUrlEncoded :: [(TextS.Text, TextS.Text)] -> ByteString
|
|
||||||
encodeFormUrlEncoded xs =
|
|
||||||
let escape :: TextS.Text -> ByteString
|
|
||||||
escape = cs . escapeURIString isUnreserved . cs
|
|
||||||
encodePair :: (TextS.Text, TextS.Text) -> ByteString
|
|
||||||
encodePair (k, "") = escape k
|
|
||||||
encodePair (k, v) = escape k <> "=" <> escape v
|
|
||||||
in B.intercalate "&" $ map encodePair xs
|
|
||||||
|
|
||||||
decodeFormUrlEncoded :: ByteString -> Either String [(TextS.Text, TextS.Text)]
|
|
||||||
decodeFormUrlEncoded "" = return []
|
|
||||||
decodeFormUrlEncoded q = do
|
|
||||||
let xs :: [TextS.Text]
|
|
||||||
xs = TextS.splitOn "&" . cs $ q
|
|
||||||
parsePair :: TextS.Text -> Either String (TextS.Text, TextS.Text)
|
|
||||||
parsePair p =
|
|
||||||
case TextS.splitOn "=" p of
|
|
||||||
[k,v] -> return ( unescape k
|
|
||||||
, unescape v
|
|
||||||
)
|
|
||||||
[k] -> return ( unescape k, "" )
|
|
||||||
_ -> Left $ "not a valid pair: " <> cs p
|
|
||||||
unescape :: TextS.Text -> TextS.Text
|
|
||||||
unescape = cs . unEscapeString . cs . TextS.intercalate "%20" . TextS.splitOn "+"
|
|
||||||
mapM parsePair xs
|
|
||||||
|
|
||||||
-- $setup
|
-- $setup
|
||||||
-- >>> import Servant.API
|
-- >>> import Servant.API
|
||||||
|
|
|
@ -13,6 +13,13 @@ import Servant.API
|
||||||
type GET = Get '[JSON] NoContent
|
type GET = Get '[JSON] NoContent
|
||||||
|
|
||||||
type ComprehensiveAPI =
|
type ComprehensiveAPI =
|
||||||
|
ComprehensiveAPIWithoutRaw :<|>
|
||||||
|
Raw
|
||||||
|
|
||||||
|
comprehensiveAPI :: Proxy ComprehensiveAPI
|
||||||
|
comprehensiveAPI = Proxy
|
||||||
|
|
||||||
|
type ComprehensiveAPIWithoutRaw =
|
||||||
GET :<|>
|
GET :<|>
|
||||||
Get '[JSON] Int :<|>
|
Get '[JSON] Int :<|>
|
||||||
Capture "foo" Int :> GET :<|>
|
Capture "foo" Int :> GET :<|>
|
||||||
|
@ -22,7 +29,6 @@ type ComprehensiveAPI =
|
||||||
QueryParam "foo" Int :> GET :<|>
|
QueryParam "foo" Int :> GET :<|>
|
||||||
QueryParams "foo" Int :> GET :<|>
|
QueryParams "foo" Int :> GET :<|>
|
||||||
QueryFlag "foo" :> GET :<|>
|
QueryFlag "foo" :> GET :<|>
|
||||||
-- Raw :<|>
|
|
||||||
RemoteHost :> GET :<|>
|
RemoteHost :> GET :<|>
|
||||||
ReqBody '[JSON] Int :> GET :<|>
|
ReqBody '[JSON] Int :> GET :<|>
|
||||||
Get '[JSON] (Headers '[Header "foo" Int] NoContent) :<|>
|
Get '[JSON] (Headers '[Header "foo" Int] NoContent) :<|>
|
||||||
|
@ -33,5 +39,5 @@ type ComprehensiveAPI =
|
||||||
WithNamedContext "foo" '[] GET :<|>
|
WithNamedContext "foo" '[] GET :<|>
|
||||||
CaptureAll "foo" Int :> GET
|
CaptureAll "foo" Int :> GET
|
||||||
|
|
||||||
comprehensiveAPI :: Proxy ComprehensiveAPI
|
comprehensiveAPIWithoutRaw :: Proxy ComprehensiveAPIWithoutRaw
|
||||||
comprehensiveAPI = Proxy
|
comprehensiveAPIWithoutRaw = Proxy
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
{-# LANGUAGE DeriveDataTypeable #-}
|
{-# LANGUAGE DeriveDataTypeable #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
module Servant.API.IsSecure
|
module Servant.API.IsSecure
|
||||||
( -- $issecure
|
( -- $issecure
|
||||||
IsSecure(..)
|
IsSecure(..)
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Data.Typeable
|
import Data.Typeable
|
||||||
|
import GHC.Generics (Generic)
|
||||||
|
|
||||||
-- | Was this request made over an SSL connection?
|
-- | Was this request made over an SSL connection?
|
||||||
--
|
--
|
||||||
|
@ -19,7 +21,7 @@ data IsSecure = Secure -- ^ the connection to the server
|
||||||
-- is secure (HTTPS)
|
-- is secure (HTTPS)
|
||||||
| NotSecure -- ^ the connection to the server
|
| NotSecure -- ^ the connection to the server
|
||||||
-- is not secure (HTTP)
|
-- is not secure (HTTP)
|
||||||
deriving Typeable
|
deriving (Eq, Show, Read, Generic, Ord, Typeable)
|
||||||
|
|
||||||
-- $issecure
|
-- $issecure
|
||||||
--
|
--
|
||||||
|
|
|
@ -31,8 +31,8 @@ module Servant.API.ResponseHeaders
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Data.ByteString.Char8 as BS (pack, unlines, init)
|
import Data.ByteString.Char8 as BS (pack, unlines, init)
|
||||||
import Data.ByteString.Conversion (ToByteString, toByteString',
|
import Web.HttpApiData (ToHttpApiData, toHeader,
|
||||||
FromByteString, fromByteString)
|
FromHttpApiData, parseHeader)
|
||||||
import qualified Data.CaseInsensitive as CI
|
import qualified Data.CaseInsensitive as CI
|
||||||
import Data.Proxy
|
import Data.Proxy
|
||||||
import GHC.TypeLits (KnownSymbol, symbolVal)
|
import GHC.TypeLits (KnownSymbol, symbolVal)
|
||||||
|
@ -68,17 +68,17 @@ class BuildHeadersTo hs where
|
||||||
instance OVERLAPPING_ BuildHeadersTo '[] where
|
instance OVERLAPPING_ BuildHeadersTo '[] where
|
||||||
buildHeadersTo _ = HNil
|
buildHeadersTo _ = HNil
|
||||||
|
|
||||||
instance OVERLAPPABLE_ ( FromByteString v, BuildHeadersTo xs, KnownSymbol h )
|
instance OVERLAPPABLE_ ( FromHttpApiData v, BuildHeadersTo xs, KnownSymbol h )
|
||||||
=> BuildHeadersTo ((Header h v) ': xs) where
|
=> BuildHeadersTo ((Header h v) ': xs) where
|
||||||
buildHeadersTo headers =
|
buildHeadersTo headers =
|
||||||
let wantedHeader = CI.mk . pack $ symbolVal (Proxy :: Proxy h)
|
let wantedHeader = CI.mk . pack $ symbolVal (Proxy :: Proxy h)
|
||||||
matching = snd <$> filter (\(h, _) -> h == wantedHeader) headers
|
matching = snd <$> filter (\(h, _) -> h == wantedHeader) headers
|
||||||
in case matching of
|
in case matching of
|
||||||
[] -> MissingHeader `HCons` buildHeadersTo headers
|
[] -> MissingHeader `HCons` buildHeadersTo headers
|
||||||
xs -> case fromByteString (BS.init $ BS.unlines xs) of
|
xs -> case parseHeader (BS.init $ BS.unlines xs) of
|
||||||
Nothing -> UndecodableHeader (BS.init $ BS.unlines xs)
|
Left _err -> UndecodableHeader (BS.init $ BS.unlines xs)
|
||||||
`HCons` buildHeadersTo headers
|
`HCons` buildHeadersTo headers
|
||||||
Just h -> Header h `HCons` buildHeadersTo headers
|
Right h -> Header h `HCons` buildHeadersTo headers
|
||||||
|
|
||||||
-- * Getting
|
-- * Getting
|
||||||
|
|
||||||
|
@ -88,10 +88,10 @@ class GetHeaders ls where
|
||||||
instance OVERLAPPING_ GetHeaders (HList '[]) where
|
instance OVERLAPPING_ GetHeaders (HList '[]) where
|
||||||
getHeaders _ = []
|
getHeaders _ = []
|
||||||
|
|
||||||
instance OVERLAPPABLE_ ( KnownSymbol h, ToByteString x, GetHeaders (HList xs) )
|
instance OVERLAPPABLE_ ( KnownSymbol h, ToHttpApiData x, GetHeaders (HList xs) )
|
||||||
=> GetHeaders (HList (Header h x ': xs)) where
|
=> GetHeaders (HList (Header h x ': xs)) where
|
||||||
getHeaders hdrs = case hdrs of
|
getHeaders hdrs = case hdrs of
|
||||||
Header val `HCons` rest -> (headerName , toByteString' val):getHeaders rest
|
Header val `HCons` rest -> (headerName , toHeader val):getHeaders rest
|
||||||
UndecodableHeader h `HCons` rest -> (headerName, h) :getHeaders rest
|
UndecodableHeader h `HCons` rest -> (headerName, h) :getHeaders rest
|
||||||
MissingHeader `HCons` rest -> getHeaders rest
|
MissingHeader `HCons` rest -> getHeaders rest
|
||||||
where headerName = CI.mk . pack $ symbolVal (Proxy :: Proxy h)
|
where headerName = CI.mk . pack $ symbolVal (Proxy :: Proxy h)
|
||||||
|
@ -99,7 +99,7 @@ instance OVERLAPPABLE_ ( KnownSymbol h, ToByteString x, GetHeaders (HList xs) )
|
||||||
instance OVERLAPPING_ GetHeaders (Headers '[] a) where
|
instance OVERLAPPING_ GetHeaders (Headers '[] a) where
|
||||||
getHeaders _ = []
|
getHeaders _ = []
|
||||||
|
|
||||||
instance OVERLAPPABLE_ ( KnownSymbol h, GetHeaders (HList rest), ToByteString v )
|
instance OVERLAPPABLE_ ( KnownSymbol h, GetHeaders (HList rest), ToHttpApiData v )
|
||||||
=> GetHeaders (Headers (Header h v ': rest) a) where
|
=> GetHeaders (Headers (Header h v ': rest) a) where
|
||||||
getHeaders hs = getHeaders $ getHeadersHList hs
|
getHeaders hs = getHeaders $ getHeadersHList hs
|
||||||
|
|
||||||
|
@ -111,11 +111,11 @@ class AddHeader h v orig new
|
||||||
addHeader :: v -> orig -> new -- ^ N.B.: The same header can't be added multiple times
|
addHeader :: v -> orig -> new -- ^ N.B.: The same header can't be added multiple times
|
||||||
|
|
||||||
|
|
||||||
instance OVERLAPPING_ ( KnownSymbol h, ToByteString v )
|
instance OVERLAPPING_ ( KnownSymbol h, ToHttpApiData v )
|
||||||
=> AddHeader h v (Headers (fst ': rest) a) (Headers (Header h v ': fst ': rest) a) where
|
=> AddHeader h v (Headers (fst ': rest) a) (Headers (Header h v ': fst ': rest) a) where
|
||||||
addHeader a (Headers resp heads) = Headers resp (HCons (Header a) heads)
|
addHeader a (Headers resp heads) = Headers resp (HCons (Header a) heads)
|
||||||
|
|
||||||
instance OVERLAPPABLE_ ( KnownSymbol h, ToByteString v
|
instance OVERLAPPABLE_ ( KnownSymbol h, ToHttpApiData v
|
||||||
, new ~ (Headers '[Header h v] a) )
|
, new ~ (Headers '[Header h v] a) )
|
||||||
=> AddHeader h v a new where
|
=> AddHeader h v a new where
|
||||||
addHeader a resp = Headers resp (HCons (Header a) HNil)
|
addHeader a resp = Headers resp (HCons (Header a) HNil)
|
||||||
|
|
|
@ -11,7 +11,6 @@ module Servant.API.ContentTypesSpec where
|
||||||
import Prelude ()
|
import Prelude ()
|
||||||
import Prelude.Compat
|
import Prelude.Compat
|
||||||
|
|
||||||
import Control.Arrow
|
|
||||||
import Data.Aeson
|
import Data.Aeson
|
||||||
import Data.ByteString.Char8 (ByteString, append, pack)
|
import Data.ByteString.Char8 (ByteString, append, pack)
|
||||||
import qualified Data.ByteString.Lazy as BSL
|
import qualified Data.ByteString.Lazy as BSL
|
||||||
|
@ -25,7 +24,6 @@ import Data.String.Conversions (cs)
|
||||||
import qualified Data.Text as TextS
|
import qualified Data.Text as TextS
|
||||||
import qualified Data.Text.Lazy as TextL
|
import qualified Data.Text.Lazy as TextL
|
||||||
import GHC.Generics
|
import GHC.Generics
|
||||||
import Network.URL (exportParams, importParams)
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import Test.QuickCheck
|
import Test.QuickCheck
|
||||||
import "quickcheck-instances" Test.QuickCheck.Instances ()
|
import "quickcheck-instances" Test.QuickCheck.Instances ()
|
||||||
|
@ -68,21 +66,6 @@ spec = describe "Servant.API.ContentTypes" $ do
|
||||||
it "has mimeUnrender reverse mimeRender for valid top-level json " $ do
|
it "has mimeUnrender reverse mimeRender for valid top-level json " $ do
|
||||||
property $ \x -> mimeUnrender p (mimeRender p x) == Right (x::SomeData)
|
property $ \x -> mimeUnrender p (mimeRender p x) == Right (x::SomeData)
|
||||||
|
|
||||||
describe "The FormUrlEncoded Content-Type type" $ do
|
|
||||||
let p = Proxy :: Proxy FormUrlEncoded
|
|
||||||
|
|
||||||
it "has mimeUnrender reverse mimeRender" $ do
|
|
||||||
property $ \x -> mempty `notElem` x
|
|
||||||
==> mimeUnrender p (mimeRender p x) == Right (x::[(TextS.Text,TextS.Text)])
|
|
||||||
|
|
||||||
it "has mimeUnrender reverse exportParams (Network.URL)" $ do
|
|
||||||
property $ \x -> mempty `notElem` x
|
|
||||||
==> (mimeUnrender p . cs . exportParams . map (cs *** cs) $ x) == Right (x::[(TextS.Text,TextS.Text)])
|
|
||||||
|
|
||||||
it "has importParams (Network.URL) reverse mimeRender" $ do
|
|
||||||
property $ \x -> mempty `notElem` x
|
|
||||||
==> (fmap (map (cs *** cs)) . importParams . cs . mimeRender p $ x) == Just (x::[(TextS.Text,TextS.Text)])
|
|
||||||
|
|
||||||
describe "The PlainText Content-Type type" $ do
|
describe "The PlainText Content-Type type" $ do
|
||||||
let p = Proxy :: Proxy PlainText
|
let p = Proxy :: Proxy PlainText
|
||||||
|
|
||||||
|
|
|
@ -3,5 +3,3 @@ servant-server
|
||||||
servant-client
|
servant-client
|
||||||
servant-docs
|
servant-docs
|
||||||
servant-foreign
|
servant-foreign
|
||||||
servant-js
|
|
||||||
servant-mock
|
|
||||||
|
|
|
@ -4,8 +4,6 @@ packages:
|
||||||
- servant-client/
|
- servant-client/
|
||||||
- servant-docs/
|
- servant-docs/
|
||||||
- servant-foreign/
|
- servant-foreign/
|
||||||
- servant-js/
|
|
||||||
- servant-mock/
|
|
||||||
- servant-server/
|
- servant-server/
|
||||||
extra-deps:
|
extra-deps:
|
||||||
- base-compat-0.9.1
|
- base-compat-0.9.1
|
||||||
|
@ -16,13 +14,10 @@ extra-deps:
|
||||||
- hspec-core-2.2.3
|
- hspec-core-2.2.3
|
||||||
- hspec-discover-2.2.3
|
- hspec-discover-2.2.3
|
||||||
- hspec-expectations-0.7.2
|
- hspec-expectations-0.7.2
|
||||||
- http-api-data-0.2.2
|
- http-api-data-0.3
|
||||||
- primitive-0.6.1.0
|
- primitive-0.6.1.0
|
||||||
- servant-0.7.1
|
|
||||||
- servant-client-0.7.1
|
|
||||||
- servant-docs-0.7.1
|
|
||||||
- servant-server-0.7.1
|
|
||||||
- should-not-typecheck-2.1.0
|
- should-not-typecheck-2.1.0
|
||||||
- time-locale-compat-0.1.1.1
|
- time-locale-compat-0.1.1.1
|
||||||
|
- uri-bytestring-0.2.2.0
|
||||||
- wai-app-static-3.1.5
|
- wai-app-static-3.1.5
|
||||||
resolver: lts-2.22
|
resolver: lts-2.22
|
||||||
|
|
|
@ -4,8 +4,8 @@ packages:
|
||||||
- servant-client/
|
- servant-client/
|
||||||
- servant-docs/
|
- servant-docs/
|
||||||
- servant-foreign/
|
- servant-foreign/
|
||||||
- servant-js/
|
|
||||||
- servant-mock/
|
|
||||||
- servant-server/
|
- servant-server/
|
||||||
extra-deps: []
|
extra-deps:
|
||||||
|
- http-api-data-0.3
|
||||||
|
- uri-bytestring-0.2.2.0
|
||||||
flags: {}
|
flags: {}
|
||||||
|
|
|
@ -4,9 +4,9 @@ packages:
|
||||||
- servant-client/
|
- servant-client/
|
||||||
- servant-docs/
|
- servant-docs/
|
||||||
- servant-foreign/
|
- servant-foreign/
|
||||||
- servant-js/
|
|
||||||
- servant-mock/
|
|
||||||
- servant-server/
|
- servant-server/
|
||||||
- doc/tutorial
|
- doc/tutorial
|
||||||
extra-deps:
|
extra-deps:
|
||||||
|
- http-api-data-0.3
|
||||||
|
- servant-js-0.9 # needed for tutorial
|
||||||
resolver: lts-6.0
|
resolver: lts-6.0
|
||||||
|
|
Loading…
Reference in a new issue