remove treestatus and tooltool and releng-frontend (#2273)

This commit is contained in:
Rok Garbas 2019-11-11 14:30:04 +01:00 коммит произвёл GitHub
Родитель 2959e48390
Коммит 4217e886d5
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
132 изменённых файлов: 0 добавлений и 30164 удалений

Просмотреть файл

@ -121,191 +121,4 @@ PROJECTS_CONFIG = {
},
],
},
'releng-frontend': {
'update': False,
'run': 'ELM',
'run_options': {
'port': 8010,
},
'requires': [
'docs',
'tooltool/api',
'treestatus/api',
],
'deploys': [
{
'target': 'S3',
'options': {
'testing': {
'enable': True,
's3_bucket': 'relengstatic-testing-relengfrontend-static-website',
'url': 'https://testing.mozilla-releng.net',
'dns': 'd1l70lpksx3ik7.cloudfront.net.',
'csp': [
'https://login.taskcluster.net',
'https://auth.taskcluster.net',
],
},
'staging': {
'enable': True,
's3_bucket': 'relengstatic-staging-relengfrontend-static-website',
'url': 'https://staging.mozilla-releng.net',
'dns': 'dpwmwa9tge2p3.cloudfront.net.',
'csp': [
'https://login.taskcluster.net',
'https://auth.taskcluster.net',
],
},
'production': {
'enable': True,
's3_bucket': 'relengstatic-prod-relengfrontend-static-website',
'url': 'https://mozilla-releng.net',
'dns': 'd1qqwps52z1e12.cloudfront.net.',
'dns_domain': 'www.mozilla-releng.net',
'csp': [
'https://login.taskcluster.net',
'https://auth.taskcluster.net',
],
},
},
},
],
},
'tooltool/client': {
'update': False,
},
'tooltool/api': {
'update': False,
'run': 'FLASK',
'run_options': {
'port': 8002,
},
'requires': [
'postgresql',
],
'deploys': [
{
'target': 'DOCKERHUB',
'options': {
'testing': {
'enable': True,
'url': 'https://dev.tooltool.mozilla-releng.net',
'nix_path_attribute': 'dockerflow',
'docker_registry': 'index.docker.io',
'docker_repo': 'mozilla/release-services',
'docker_stable_tag': 'tooltool_api_dockerflow_testing',
},
'staging': {
'enable': True,
'url': 'https://stage.tooltool.mozilla-releng.net',
'nix_path_attribute': 'dockerflow',
'docker_registry': 'index.docker.io',
'docker_repo': 'mozilla/release-services',
'docker_stable_tag': 'tooltool_api_dockerflow_staging',
},
'production': {
'enable': True,
'url': 'https://tooltool.mozilla-releng.net',
'nix_path_attribute': 'dockerflow',
'docker_registry': 'index.docker.io',
'docker_repo': 'mozilla/release-services',
'docker_stable_tag': 'tooltool_api_dockerflow_production',
},
},
},
{
'target': 'TASKCLUSTER_HOOK',
'options': {
'testing': {
'enable': True,
'nix_path_attribute': 'cron.replicate.testing',
'name-suffix': '-replicate',
'docker_registry': 'index.docker.io',
'docker_repo': 'mozillareleng/services',
},
'staging': {
'enable': True,
'nix_path_attribute': 'cron.replicate.staging',
'name-suffix': '-replicate',
'docker_registry': 'index.docker.io',
'docker_repo': 'mozillareleng/services',
},
'production': {
'enable': True,
'nix_path_attribute': 'cron.replicate.production',
'name-suffix': '-replicate',
'docker_registry': 'index.docker.io',
'docker_repo': 'mozillareleng/services',
},
},
},
{
'target': 'TASKCLUSTER_HOOK',
'options': {
'testing': {
'enable': True,
'nix_path_attribute': 'cron.check_pending_uploads.testing',
'name-suffix': '-check_pending_uploads',
'docker_registry': 'index.docker.io',
'docker_repo': 'mozillareleng/services',
},
'staging': {
'enable': True,
'nix_path_attribute': 'cron.check_pending_uploads.staging',
'name-suffix': '-check_pending_uploads',
'docker_registry': 'index.docker.io',
'docker_repo': 'mozillareleng/services',
},
'production': {
'enable': True,
'nix_path_attribute': 'cron.check_pending_uploads.production',
'name-suffix': '-check_pending_uploads',
'docker_registry': 'index.docker.io',
'docker_repo': 'mozillareleng/services',
},
},
},
],
},
'treestatus/api': {
'update': False,
'run': 'FLASK',
'run_options': {
'port': 8000,
},
'requires': [
'postgresql',
],
'deploys': [
{
'target': 'DOCKERHUB',
'options': {
'testing': {
'enable': True,
'url': 'https://dev.treestatus.mozilla-releng.net',
'nix_path_attribute': 'dockerflow',
'docker_registry': 'index.docker.io',
'docker_repo': 'mozilla/release-services',
'docker_stable_tag': 'treestatus_api_dockerflow_testing',
},
'staging': {
'enable': True,
'url': 'https://stage.treestatus.mozilla-releng.net',
'nix_path_attribute': 'dockerflow',
'docker_registry': 'index.docker.io',
'docker_repo': 'mozilla/release-services',
'docker_stable_tag': 'treestatus_api_dockerflow_staging',
},
'production': {
'enable': True,
'url': 'https://treestatus.mozilla-releng.net',
'nix_path_attribute': 'dockerflow',
'docker_registry': 'index.docker.io',
'docker_repo': 'mozilla/release-services',
'docker_stable_tag': 'treestatus_api_dockerflow_production',
},
},
},
],
},
}

Просмотреть файл

@ -1,100 +0,0 @@
===============
RelengAPI Tools
===============
TODO: what this app is for, list useful links
Tools used in this project:
- react
- immutable
- redux
- redux-router
- bootstrap (v4)
Commands
========
- ``npm run dev``
Run development instance of application, on http://localhost:8080,
via `webpack-dev-server`_, with hot reloading of code via
`webpack hot module reload`_.
- ``npm run build``
Build production ready static files into ``./build`` folder.
- ``npm run test``
Run all the tests for the project one time.
Coverage can be found in ``./coverage`` folder.
- ``npm run test:dev``
Open browser, then run all tests, keep browser open and listen for code
changes, then rerun the tests.
Coverage can be found in ``./coverage`` folder.
Configuration
=============
This project does not use any of the *traditional* javascript building tools
(eg. `Gulp`_, `Grunt`_). It connects all with `webpack`_ and different webpack
loaders. Webpack is used here because it also provides nice development
environment (hot reload of code). Configuration for development, testing and
production can be found in ``./webpack.config.js``.
Webpack hooks and applies this `babel`_ transforms:
- `babel-preset-es2015`_
- `babel-preset-react`_
- `babel-preset-stage-0`_
Configuration for `eslint`_ configuration, which is used in webpack's build
chain is configured in ``./packages.json``, since this is the location eslint
linter looks.
For testing, test runner `karma`_ is used. It used webpack's configuration to
apply all babel transforms and runs tests in for different browsers.
Configuration for karma can be found in ``./karma.conf.js``
Deploying
=========
TODO: ask dustin how setup ssl and deploy taskcluster docs to s3
.. _`webpack-dev-server`: https://www.npmjs.com/package/webpack-dev-server
.. _`webpack hot module reload`: http://webpack.github.io/docs/hot-module-replacement-with-webpack.html
.. _`Gulp`: http://gulpjs.com/
.. _`Grunt`: http://gruntjs.com/
.. _`webpack`: http://webpack.github.io/
.. _`karma`: https://karma-runner.github.io/
.. _`babel`: http://babeljs.io/
.. _`eslint`: http://eslint.org/
.. _`babel-preset-es2015`: http://babeljs.io/docs/plugins/preset-es2015/
.. _`babel-preset-react`: http://babeljs.io/docs/plugins/preset-react/
.. _`babel-preset-stage-0`: http://babeljs.io/docs/plugins/preset-stage-0/
todo:
https://pageshot.dev.mozaws.net/
https://mozilla.github.io/mozmaker/demo/
https://github.com/mozilla/tabzilla
https://github.com/xgrommx/awesome-redux
https://github.com/reactjs/reselect
http://indexiatech.github.io/re-notif/
https://github.com/michaelcontento/redux-storage
https://github.com/raisemarketplace/redux-loop
http://yelouafi.github.io/redux-saga/
http://redux.js.org/docs/advanced/Middleware.html
http://redux.js.org/docs/advanced/ExampleRedditAPI.html
http://yelouafi.github.io/redux-saga/docs/basics/ErrorHandling.html
https://auth0.com/blog/2016/01/04/secure-your-react-and-redux-app-with-jwt-authentication/

Просмотреть файл

@ -1 +0,0 @@
1.0.0

Просмотреть файл

@ -1,25 +0,0 @@
{ releng_pkgs
}:
let
inherit (builtins) readFile;
inherit (releng_pkgs.lib) mkFrontend;
inherit (releng_pkgs.pkgs.lib) fileContents;
nodejs = releng_pkgs.pkgs."nodejs-6_x";
node_modules = import ./node-modules.nix {
inherit nodejs;
inherit (releng_pkgs) pkgs;
};
elm_packages = import ./elm-packages.nix;
in mkFrontend {
inProduction = true;
project_name = "releng-frontend";
inherit nodejs node_modules elm_packages;
version = fileContents ./VERSION;
src = ./.;
src_path = "src/releng_frontend";
postInstall = ''
cp -R src/static/* $out/
'';
}

Просмотреть файл

@ -1,22 +0,0 @@
{
"version": "1.0.0",
"summary": "Mozilla RelEng Services Frontend",
"repository": "https://github.com/mozilla/release-services.git",
"license": "MPL2",
"source-directories": [
"src"
],
"exposed-modules": [],
"dependencies": {
"elm-lang/core": "5.1.1 <= v < 6.0.0",
"elm-lang/html": "2.0.0 <= v < 3.0.0",
"elm-lang/http": "1.0.0 <= v < 2.0.0",
"elm-lang/navigation": "2.1.0 <= v < 3.0.0",
"elm-lang/virtual-dom": "2.0.4 <= v < 3.0.0",
"etaque/elm-form": "2.0.0 <= v < 3.0.0",
"evancz/focus": "2.0.2 <= v < 3.0.0",
"evancz/url-parser": "2.0.1 <= v < 3.0.0",
"krisajenkins/remotedata": "4.3.0 <= v < 5.0.0"
},
"elm-version": "0.18.0 <= v < 0.19.0"
}

Просмотреть файл

@ -1,62 +0,0 @@
{
"elm-community/lazy-list" = {
version = "1.0.0";
sha256 = "1hxpyd20rmicr6k6wwzyxhqwwbsh22ml7gr0j74vqql1k7yysshh";
};
"krisajenkins/remotedata" = {
version = "4.3.0";
sha256 = "0k07s964ahq0mfgvwl8yp93ziqfzz3r38h4xawbl44g7pfajlz3v";
};
"elm-community/elm-test" = {
version = "4.1.1";
sha256 = "04shqwlw3k7pqzj5qnlk1ga9y5jq0ccmgni3lj27fdfgniymxgai";
};
"elm-lang/navigation" = {
version = "2.1.0";
sha256 = "0xyamkcw3misjfyc523g0bh97lffrikaxk2hklwmla9i7j39ivf3";
};
"elm-community/shrink" = {
version = "2.0.0";
sha256 = "1f205dvgyqg4phf0hvk57nsxwhfhwhy5y4v6msc3fa9xgyzx7fik";
};
"elm-lang/virtual-dom" = {
version = "2.0.4";
sha256 = "13kc3jim6js827h91m6wx6n16qb3b6p6ms6789ks106zpwmhfwxv";
};
"evancz/url-parser" = {
version = "2.0.1";
sha256 = "0c9vxd7m11mmshlqi3s2arsr4jjkzxqcb907fvamxhaxvygkd4i7";
};
"etaque/elm-form" = {
version = "2.1.0";
sha256 = "19d904r73fqm35dxa1njyrmn613dj963hr8gbg8hdybzq46qdhzk";
};
"mgold/elm-random-pcg" = {
version = "5.0.1";
sha256 = "0gf0qhavcsg30abh99z332srqswfrpaafsphcw74wxny4fx0a49q";
};
"elm-lang/lazy" = {
version = "2.0.0";
sha256 = "086f57n0ajdyn8rax11qp1dlym1hz3s6qv5hrmq8azw0q27byipn";
};
"elm-lang/dom" = {
version = "1.1.1";
sha256 = "1y6nm3np10l34mhpc9c88vv7kc7qgaxim4cmagf4ig82rwi9mpl1";
};
"elm-lang/html" = {
version = "2.0.0";
sha256 = "05sqjd5n8jnq4lv5v0ipcg98b8im1isnnl4wns1zzn4w5nbrjjzi";
};
"elm-lang/http" = {
version = "1.0.0";
sha256 = "1fh9k278q4i2y447wkk8jd4n12px1nsmycd0fql8k0i3ypcibm8h";
};
"evancz/focus" = {
version = "2.0.2";
sha256 = "1680pbkx67cdk1fp7pdciiv00grq86kk15amf30pma9zmysp938p";
};
"elm-lang/core" = {
version = "5.1.1";
sha256 = "1ndw8ffn2la48gvs7xf3zrc5d7zqrq4j6scbrb60mb2ml1a5hhry";
};
}

Просмотреть файл

@ -1,349 +0,0 @@
# This file originates from node2nix
{stdenv, nodejs, python2, utillinux, runCommand, writeTextFile}:
let
python = if nodejs ? python then nodejs.python else python2;
# Create a tar wrapper that filters all the 'Ignoring unknown extended header keyword' noise
tarWrapper = runCommand "tarWrapper" {} ''
mkdir -p $out/bin
cat > $out/bin/tar <<EOF
#! ${stdenv.shell} -e
$(type -p tar) "\$@" --warning=no-unknown-keyword
EOF
chmod +x $out/bin/tar
'';
# Function that generates a TGZ file from a NPM project
buildNodeSourceDist =
{ name, version, src, ... }:
stdenv.mkDerivation {
name = "node-tarball-${name}-${version}";
inherit src;
buildInputs = [ nodejs ];
buildPhase = ''
export HOME=$TMPDIR
tgzFile=$(npm pack)
'';
installPhase = ''
mkdir -p $out/tarballs
mv $tgzFile $out/tarballs
mkdir -p $out/nix-support
echo "file source-dist $out/tarballs/$tgzFile" >> $out/nix-support/hydra-build-products
'';
};
includeDependencies = {dependencies}:
stdenv.lib.optionalString (dependencies != [])
(stdenv.lib.concatMapStrings (dependency:
''
# Bundle the dependencies of the package
mkdir -p node_modules
cd node_modules
# Only include dependencies if they don't exist. They may also be bundled in the package.
if [ ! -e "${dependency.name}" ]
then
${composePackage dependency}
fi
cd ..
''
) dependencies);
# Recursively composes the dependencies of a package
composePackage = { name, packageName, src, dependencies ? [], ... }@args:
''
DIR=$(pwd)
cd $TMPDIR
unpackFile ${src}
# Make the base dir in which the target dependency resides first
mkdir -p "$(dirname "$DIR/${packageName}")"
if [ -f "${src}" ]
then
# Figure out what directory has been unpacked
packageDir="$(find . -maxdepth 1 -type d | tail -1)"
# Restore write permissions to make building work
find "$packageDir" -type d -print0 | xargs -0 chmod u+x
chmod -R u+w "$packageDir"
# Move the extracted tarball into the output folder
mv "$packageDir" "$DIR/${packageName}"
elif [ -d "${src}" ]
then
# Get a stripped name (without hash) of the source directory.
# On old nixpkgs it's already set internally.
if [ -z "$strippedName" ]
then
strippedName="$(stripHash ${src})"
fi
# Restore write permissions to make building work
chmod -R u+w "$strippedName"
# Move the extracted directory into the output folder
mv "$strippedName" "$DIR/${packageName}"
fi
# Unset the stripped name to not confuse the next unpack step
unset strippedName
# Include the dependencies of the package
cd "$DIR/${packageName}"
${includeDependencies { inherit dependencies; }}
cd ..
${stdenv.lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
'';
pinpointDependencies = {dependencies, production}:
let
pinpointDependenciesFromPackageJSON = writeTextFile {
name = "pinpointDependencies.js";
text = ''
var fs = require('fs');
var path = require('path');
function resolveDependencyVersion(location, name) {
if(location == process.env['NIX_STORE']) {
return null;
} else {
var dependencyPackageJSON = path.join(location, "node_modules", name, "package.json");
if(fs.existsSync(dependencyPackageJSON)) {
var dependencyPackageObj = JSON.parse(fs.readFileSync(dependencyPackageJSON));
if(dependencyPackageObj.name == name) {
return dependencyPackageObj.version;
}
} else {
return resolveDependencyVersion(path.resolve(location, ".."), name);
}
}
}
function replaceDependencies(dependencies) {
if(typeof dependencies == "object" && dependencies !== null) {
for(var dependency in dependencies) {
var resolvedVersion = resolveDependencyVersion(process.cwd(), dependency);
if(resolvedVersion === null) {
process.stderr.write("WARNING: cannot pinpoint dependency: "+dependency+", context: "+process.cwd()+"\n");
} else {
dependencies[dependency] = resolvedVersion;
}
}
}
}
/* Read the package.json configuration */
var packageObj = JSON.parse(fs.readFileSync('./package.json'));
/* Pinpoint all dependencies */
replaceDependencies(packageObj.dependencies);
if(process.argv[2] == "development") {
replaceDependencies(packageObj.devDependencies);
}
replaceDependencies(packageObj.optionalDependencies);
/* Write the fixed package.json file */
fs.writeFileSync("package.json", JSON.stringify(packageObj, null, 2));
'';
};
in
''
node ${pinpointDependenciesFromPackageJSON} ${if production then "production" else "development"}
${stdenv.lib.optionalString (dependencies != [])
''
if [ -d node_modules ]
then
cd node_modules
${stdenv.lib.concatMapStrings (dependency: pinpointDependenciesOfPackage dependency) dependencies}
cd ..
fi
''}
'';
# Recursively traverses all dependencies of a package and pinpoints all
# dependencies in the package.json file to the versions that are actually
# being used.
pinpointDependenciesOfPackage = { packageName, dependencies ? [], production ? true, ... }@args:
''
if [ -d "${packageName}" ]
then
cd "${packageName}"
${pinpointDependencies { inherit dependencies production; }}
cd ..
${stdenv.lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
fi
'';
# Extract the Node.js source code which is used to compile packages with
# native bindings
nodeSources = runCommand "node-sources" {} ''
tar --no-same-owner --no-same-permissions -xf ${nodejs.src}
mv node-* $out
'';
# Builds and composes an NPM package including all its dependencies
buildNodePackage = { name, packageName, version, dependencies ? [], production ? true, npmFlags ? "", dontNpmInstall ? false, preRebuild ? "", ... }@args:
stdenv.lib.makeOverridable stdenv.mkDerivation (builtins.removeAttrs args [ "dependencies" ] // {
name = "node-${name}-${version}";
buildInputs = [ tarWrapper python nodejs ] ++ stdenv.lib.optional (stdenv.isLinux) utillinux ++ args.buildInputs or [];
dontStrip = args.dontStrip or true; # Striping may fail a build for some package deployments
inherit dontNpmInstall preRebuild;
unpackPhase = args.unpackPhase or "true";
buildPhase = args.buildPhase or "true";
compositionScript = composePackage args;
pinpointDependenciesScript = pinpointDependenciesOfPackage args;
passAsFile = [ "compositionScript" "pinpointDependenciesScript" ];
installPhase = args.installPhase or ''
# Create and enter a root node_modules/ folder
mkdir -p $out/lib/node_modules
cd $out/lib/node_modules
# Compose the package and all its dependencies
source $compositionScriptPath
# Pinpoint the versions of all dependencies to the ones that are actually being used
echo "pinpointing versions of dependencies..."
source $pinpointDependenciesScriptPath
# Patch the shebangs of the bundled modules to prevent them from
# calling executables outside the Nix store as much as possible
patchShebangs .
# Deploy the Node.js package by running npm install. Since the
# dependencies have been provided already by ourselves, it should not
# attempt to install them again, which is good, because we want to make
# it Nix's responsibility. If it needs to install any dependencies
# anyway (e.g. because the dependency parameters are
# incomplete/incorrect), it fails.
#
# The other responsibilities of NPM are kept -- version checks, build
# steps, postprocessing etc.
export HOME=$TMPDIR
cd "${packageName}"
runHook preRebuild
npm --registry http://www.example.com --nodedir=${nodeSources} ${npmFlags} ${stdenv.lib.optionalString production "--production"} rebuild
if [ "$dontNpmInstall" != "1" ]
then
# NPM tries to download packages even when they already exist if npm-shrinkwrap is used.
rm -f npm-shrinkwrap.json
npm --registry http://www.example.com --nodedir=${nodeSources} ${npmFlags} ${stdenv.lib.optionalString production "--production"} install
fi
# Create symlink to the deployed executable folder, if applicable
if [ -d "$out/lib/node_modules/.bin" ]
then
ln -s $out/lib/node_modules/.bin $out/bin
fi
# Create symlinks to the deployed manual page folders, if applicable
if [ -d "$out/lib/node_modules/${packageName}/man" ]
then
mkdir -p $out/share
for dir in "$out/lib/node_modules/${packageName}/man/"*
do
mkdir -p $out/share/man/$(basename "$dir")
for page in "$dir"/*
do
ln -s $page $out/share/man/$(basename "$dir")
done
done
fi
# Run post install hook, if provided
runHook postInstall
'';
});
# Builds a development shell
buildNodeShell = { name, packageName, version, src, dependencies ? [], production ? true, npmFlags ? "", dontNpmInstall ? false, ... }@args:
let
nodeDependencies = stdenv.mkDerivation {
name = "node-dependencies-${name}-${version}";
buildInputs = [ tarWrapper python nodejs ] ++ stdenv.lib.optional (stdenv.isLinux) utillinux ++ args.buildInputs or [];
includeScript = includeDependencies { inherit dependencies; };
pinpointDependenciesScript = pinpointDependenciesOfPackage args;
passAsFile = [ "includeScript" "pinpointDependenciesScript" ];
buildCommand = ''
mkdir -p $out/lib
cd $out/lib
source $includeScriptPath
# Pinpoint the versions of all dependencies to the ones that are actually being used
echo "pinpointing versions of dependencies..."
source $pinpointDependenciesScriptPath
# Create fake package.json to make the npm commands work properly
cat > package.json <<EOF
{
"name": "${packageName}",
"version": "${version}"
}
EOF
# Patch the shebangs of the bundled modules to prevent them from
# calling executables outside the Nix store as much as possible
patchShebangs .
export HOME=$PWD
npm --registry http://www.example.com --nodedir=${nodeSources} ${npmFlags} ${stdenv.lib.optionalString production "--production"} rebuild
${stdenv.lib.optionalString (!dontNpmInstall) ''
# NPM tries to download packages even when they already exist if npm-shrinkwrap is used.
rm -f npm-shrinkwrap.json
npm --registry http://www.example.com --nodedir=${nodeSources} ${npmFlags} ${stdenv.lib.optionalString production "--production"} install
''}
ln -s $out/lib/node_modules/.bin $out/bin
'';
};
in
stdenv.lib.makeOverridable stdenv.mkDerivation {
name = "node-shell-${name}-${version}";
buildInputs = [ python nodejs ] ++ stdenv.lib.optional (stdenv.isLinux) utillinux ++ args.buildInputs or [];
buildCommand = ''
mkdir -p $out/bin
cat > $out/bin/shell <<EOF
#! ${stdenv.shell} -e
$shellHook
exec ${stdenv.shell}
EOF
chmod +x $out/bin/shell
'';
# Provide the dependencies in a development shell through the NODE_PATH environment variable
inherit nodeDependencies;
shellHook = stdenv.lib.optionalString (dependencies != []) ''
export NODE_PATH=$nodeDependencies/lib/node_modules
'';
};
in
{ inherit buildNodeSourceDist buildNodePackage buildNodeShell; }

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,24 +0,0 @@
[
{ "bootstrap" : "^4.0.0-alpha.6" },
{ "copy-webpack-plugin" : "^4.0.1" },
{ "css-loader" : "^0.26.2" },
{ "elm-hot-loader" : "^0.5.4" },
{ "elm-webpack-loader" : "^4.2.0" },
{ "expose-loader" : "^0.7.3" },
{ "extract-text-webpack-plugin" : "^2.0.0" },
{ "file-loader" : "^0.10.1" },
{ "hawk" : "^6.0.0" },
{ "html-webpack-plugin" : "^2.28.0" },
{ "jquery" : "^3.1.0" },
{ "node-sass" : "^4.2.0" },
{ "raven-js" : "^3.15.0" },
{ "sass-loader" : "^6.0.0" },
{ "style-loader" : "^0.13.1" },
{ "svg-url-loader" : "^2.0.2" },
{ "tether" : "^1.4.0" },
{ "url" : "^0.11.0" },
{ "url-loader" : "^0.5.7" },
{ "webpack" : "^2.2.1" },
{ "webpack-dev-server" : "^2.4.1" },
{ "webpack-merge" : "^4.0.0" }
]

Просмотреть файл

@ -1,16 +0,0 @@
# This file has been generated by node2nix 1.3.0. Do not edit!
{pkgs ? import <nixpkgs> {
inherit system;
}, system ? builtins.currentSystem, nodejs ? pkgs."nodejs-6_x"}:
let
nodeEnv = import ./node-env.nix {
inherit (pkgs) stdenv python2 utillinux runCommand writeTextFile;
inherit nodejs;
};
in
import ./node-modules-generated.nix {
inherit (pkgs) fetchurl fetchgit;
inherit nodeEnv;
}

Просмотреть файл

@ -1,144 +0,0 @@
module App exposing (..)
import App.Tokens
import App.ToolTool
import App.TreeStatus
import App.TreeStatus.Form
import App.TreeStatus.Types
import App.Types
import App.UserScopes
import Hawk
import Navigation
import TaskclusterLogin
import UrlParser exposing ((</>), (<?>))
--
-- ROUTING
--
-- inspired by https://github.com/rofrol/elm-navigation-example
--
type Route
= NotFoundRoute
| HomeRoute
| LoginRoute (Maybe String) (Maybe String)
| LogoutRoute
| TokensRoute
| ToolToolRoute
| TreeStatusRoute App.TreeStatus.Types.Route
pages : List (App.Types.Page Route b)
pages =
[ App.Tokens.page TokensRoute
, App.ToolTool.page ToolToolRoute
, App.TreeStatus.page TreeStatusRoute
]
routeParser : UrlParser.Parser (Route -> a) a
routeParser =
pages
|> List.map (\x -> x.matcher)
|> List.append
[ UrlParser.map HomeRoute UrlParser.top
, UrlParser.map NotFoundRoute (UrlParser.s "404")
, UrlParser.map LoginRoute
(UrlParser.s "login"
<?> UrlParser.stringParam "code"
<?> UrlParser.stringParam "state"
)
, UrlParser.map LogoutRoute (UrlParser.s "logout")
]
|> UrlParser.oneOf
reverseRoute : Route -> String
reverseRoute route =
case route of
NotFoundRoute ->
"/404"
HomeRoute ->
"/"
LoginRoute _ _ ->
"/login"
LogoutRoute ->
"/logout"
TokensRoute ->
"/tokens"
ToolToolRoute ->
"/tooltool"
TreeStatusRoute route ->
App.TreeStatus.reverseRoute route
parseLocation : Navigation.Location -> Route
parseLocation location =
location
|> UrlParser.parsePath routeParser
|> Maybe.withDefault NotFoundRoute
navigateTo : Route -> Cmd Msg
navigateTo route =
route
|> reverseRoute
|> Navigation.newUrl
--
-- FLAGS
--
type alias Flags =
{ auth0 : Maybe TaskclusterLogin.Tokens
, treestatusUrl : String
, docsUrl : String
, version : String
}
--
-- MODEL
--
type alias Model =
{ history : List Navigation.Location
, route : Route
, user : TaskclusterLogin.Model
, userScopes : App.UserScopes.Model
, tokens : App.Tokens.Model
, tooltool : App.ToolTool.Model
, treestatus : App.TreeStatus.Types.Model App.TreeStatus.Form.AddTree App.TreeStatus.Form.UpdateTree App.TreeStatus.Form.UpdateStack App.TreeStatus.Form.UpdateLog
, docsUrl : String
, version : String
}
--
-- MESSAGES
--
type Msg
= UrlChange Navigation.Location
| NavigateTo Route
| TaskclusterLoginMsg TaskclusterLogin.Msg
| HawkMsg Hawk.Msg
| UserScopesMsg App.UserScopes.Msg
| TokensMsg App.Tokens.Msg
| ToolToolMsg App.ToolTool.Msg
| TreeStatusMsg App.TreeStatus.Types.Msg

Просмотреть файл

@ -1,747 +0,0 @@
module App.Clobberer exposing (..)
import App.Utils exposing (..)
import Dict exposing (Dict)
import Focus exposing ((=>), Focus, create, set)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Html.Keyed
import Http
import Json.Decode as JsonDecode exposing ((:=))
import Json.Encode as JsonEncode
import RemoteData as RemoteData exposing (RemoteData(..), WebData)
import RouteUrl.Builder exposing (Builder, builder, replacePath)
import Task exposing (Task)
-- TYPES
type alias BackendBuilder =
{ branch : String
, builddir : String
, lastclobber : Int
, name : String
, slave : String
, who : String
}
type alias BackendBranch =
{ name : String
, builders : List BackendBuilder
}
type alias BackendResponse =
List String
type alias BackendData =
List BackendBranch
type alias ModelBackend =
{ title : String
, data : WebData BackendData
, clobber : WebData BackendData
, clobber_messages : List ( String, String )
, selected_dropdown : Maybe String
, selected : Dict String (List String)
, selected_details : Bool
}
type alias Model =
{ baseUrl : String
, buildbot : ModelBackend
, taskcluster : ModelBackend
}
type Backend
= BuildbotBackend
| TaskclusterBackend
type Msg
= Clobber Backend
| Clobbered Backend (WebData BackendData)
| FetchData Backend
| DataFetched Backend (WebData BackendData)
| SelectDropdown Backend String
| AddSelected Backend String String
| RemoveSelected Backend String String
| ToggleSelectedDetails Backend
| SelectAll Backend String
| SelectNone Backend String
-- API
viewClobberButton backend model =
let
buttonNumber =
List.length <| List.concat <| Dict.values model.selected
buttonText =
"Submit clobberer (" ++ toString buttonNumber ++ ")"
buttonDisabled =
if buttonNumber == 0 then
True
else
False
in
button
[ class "btn btn-primary btn-large"
, disabled buttonDisabled
, App.Utils.onClick <| Clobber backend
]
[ text buttonText ]
viewClobberDetails backend model =
let
( buttonText, itemText, backendName ) =
case backend of
TaskclusterBackend ->
( "worker type(s)", "Worker Type: ", "taskcluster" )
BuildbotBackend ->
( "builder(s)", "Builder: ", "buildbot" )
button =
a
[ href <| "#clobber-selected-" ++ backendName
, attribute "data-toggle" "collapse"
, attribute "aria-expanded" "false"
, attribute "aria-controls" "collapseExample"
, App.Utils.onClick <| ToggleSelectedDetails backend
]
[ text <| "Show/Hide selected " ++ buttonText ++ " to be clobbered" ]
collapsed =
if model.selected_details == True then
" in"
else
""
items'' key value =
li []
[ div []
[ b [] [ text "Branch: " ]
, text key
]
, div []
[ b [] [ text itemText ]
, text value
]
]
items' ( key, value ) =
List.map (items'' key) value
items =
List.concat <| List.map items' <| Dict.toList model.selected
in
if items == [] then
[]
else
[ button
, ul
[ id <| "#clobber-selected-" ++ backendName
, class <| "collapse" ++ collapsed
]
items
]
viewBackend : Backend -> ModelBackend -> Html Msg
viewBackend backend model =
div []
[ viewClobberButton backend model
, div [ class "clobberer-submit-description" ] <| viewClobberDetails backend model
, case model.data of
Success data ->
dropdown (SelectDropdown backend) data model.selected_dropdown
Failure message ->
div [] [ error (FetchData backend) (toString message) ]
Loading ->
div [] [ loading ]
NotAsked ->
div [] []
, case model.selected_dropdown of
Nothing ->
div [] []
Just selected_dropdown ->
let
data =
case model.data of
Success data ->
data
_ ->
[]
items =
data
|> List.filter (\x -> x.name == selected_dropdown)
|> List.map (\x -> x.builders)
|> List.concat
|> List.foldr
(\x y ->
Dict.update x.name
(\z ->
Maybe.withDefault [] z
|> (::) x
|> Just
)
y
)
Dict.empty
in
table [ class "table table-hover" ]
[ thead []
[ tr []
[ th []
[ input
[ type' "checkbox"
, onCheck
(\checked ->
case checked of
True ->
SelectAll backend selected_dropdown
False ->
SelectNone backend selected_dropdown
)
, checked
(let
all =
items
|> Dict.keys
current =
Dict.get selected_dropdown model.selected
|> Maybe.withDefault []
in
if current == all && current /= [] then
True
else
False
)
]
[]
]
, th []
[ text
(case backend of
TaskclusterBackend ->
"Worker Type"
BuildbotBackend ->
"Builder"
)
]
, th []
[ text
(case backend of
TaskclusterBackend ->
"Caches"
BuildbotBackend ->
"Last clobber"
)
]
]
]
, Html.Keyed.node "tbody"
[]
(Dict.values <|
Dict.map
(\builder_name builders ->
( builder_name
, tr []
[ td []
[ input
[ type' "checkbox"
, onCheck
(\checked ->
case checked of
True ->
AddSelected backend selected_dropdown builder_name
False ->
RemoveSelected backend selected_dropdown builder_name
)
, checked
(case Dict.get selected_dropdown model.selected of
Just itemz ->
List.member builder_name itemz
Nothing ->
False
)
]
[]
]
, td []
[ text builder_name ]
, td []
[ ul [] <| List.map (\x -> li [] [ text (toString x.lastclobber) ]) builders
]
]
)
)
items
)
]
]
view : Model -> Html Msg
view model =
div []
[ h1 [] [ text "Clobberer" ]
, p [] [ text "A repairer of buildbot builders and taskcluster worker types." ]
, p [] [ text "TODO: link to documentation" ]
, div [ class "row" ]
[ div [ class "col-md-6" ]
[ h2 [] [ text "Taskcluster" ]
, viewBackend TaskclusterBackend model.taskcluster
]
, div [ class "col-md-6" ]
[ h2 [] [ text "Buildbot" ]
, viewBackend BuildbotBackend model.buildbot
]
]
]
initBackend : String -> ModelBackend
initBackend title =
{ title = title
, data = NotAsked
, clobber = NotAsked
, clobber_messages = []
, selected_dropdown = Just "mozilla-aurora"
, selected = Dict.empty
, selected_details = False
}
init : Model
init =
{ buildbot = initBackend "Buildbot"
, taskcluster = initBackend "Taskcluster"
, baseUrl = "data-clobberer-url not set"
}
initPage : String -> Model -> ( Model, Cmd Msg )
initPage baseUrl model =
( { buildbot = initBackend "Buildbot"
, taskcluster = initBackend "Taskcluster"
, baseUrl = baseUrl
}
, Cmd.batch
[ fetchBackend (DataFetched BuildbotBackend) (baseUrl ++ "/buildbot")
--XXX, fetchBackend (DataFetched TaskclusterBackend) (baseUrl ++ "/taskcluster")
]
)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Clobber backend ->
let
encodeBody key items =
( key, JsonEncode.list items )
( newModel, backendUrl, body ) =
case backend of
TaskclusterBackend ->
( set (taskcluster => clobber) Loading model
, model.baseUrl ++ "/taskcluster"
, Http.empty
-- TODO
--, Body.BodyString
-- <| JsonEncode.encode 0
-- <| JsonEncode.object []
-- <| List.map encodeBody
-- <| Dict.toList model.taskcluster.selected
)
BuildbotBackend ->
( set (buildbot => clobber) Loading model
, model.baseUrl ++ "/buildbot"
, Http.empty
-- TODO
--, Body.BodyString
-- <| JsonEncode.encode 0
-- <| JsonEncode.object []
-- <| List.map encodeBody
-- <| Dict.toList model.buildout.selected
)
in
( model, Cmd.none )
--(newModel, clobberBackend (Clobbered backend) backendUrl)
Clobbered backend newData ->
let
-- TODO from newData set clobber_message
newModel =
newData
--case backend of
-- TaskclusterBackend ->
-- set (taskcluster => clobber) NotAsked model
-- BuildbotBackend ->
-- set (buildbot => clobber) NotAsked model
in
( model, Cmd.none )
FetchData backend ->
let
( newModel, backendUrl ) =
case backend of
TaskclusterBackend ->
( set (taskcluster => data) Loading model
, model.baseUrl ++ "/taskcluster"
)
BuildbotBackend ->
( set (buildbot => data) Loading model
, model.baseUrl ++ "/buildbot"
)
in
( newModel, fetchBackend (DataFetched backend) backendUrl )
DataFetched backend newData ->
let
newModel =
case backend of
TaskclusterBackend ->
set (taskcluster => data) newData model
BuildbotBackend ->
set (buildbot => data) newData model
in
( newModel, Cmd.none )
SelectDropdown backend name ->
let
newModel =
case backend of
TaskclusterBackend ->
set (taskcluster => selected_dropdown) (Just name) model
BuildbotBackend ->
set (buildbot => selected_dropdown) (Just name) model
in
( newModel, Cmd.none )
AddSelected backend key value ->
let
newModel =
case backend of
TaskclusterBackend ->
let
selected' =
Dict.insert key values' model.taskcluster.selected
values' =
if List.member value values then
values
else
value :: values
values =
Maybe.withDefault [] <|
Dict.get key model.taskcluster.selected
in
set (taskcluster => selected) selected' model
BuildbotBackend ->
let
selected' =
Dict.insert key values' model.buildbot.selected
values' =
if List.member value values then
values
else
value :: values
values =
Maybe.withDefault [] <|
Dict.get key model.buildbot.selected
in
set (buildbot => selected) selected' model
in
( newModel, Cmd.none )
RemoveSelected backend key value ->
let
newModel =
case backend of
TaskclusterBackend ->
let
selected' =
Dict.insert key values' model.taskcluster.selected
values' =
List.filter (\x -> x /= value) values
values =
Maybe.withDefault [] <|
Dict.get key model.taskcluster.selected
in
set (taskcluster => selected) selected' model
BuildbotBackend ->
let
selected' =
Dict.insert key values' model.buildbot.selected
values' =
List.filter (\x -> x /= value) values
values =
Maybe.withDefault [] <|
Dict.get key model.buildbot.selected
in
set (buildbot => selected) selected' model
in
( newModel, Cmd.none )
ToggleSelectedDetails backend ->
let
newModel =
case backend of
TaskclusterBackend ->
let
toggled_selected_details =
not model.taskcluster.selected_details
in
set (taskcluster => selected_details) toggled_selected_details model
BuildbotBackend ->
let
toggled_selected_details =
not model.buildbot.selected_details
in
set (buildbot => selected_details) toggled_selected_details model
in
( newModel, Cmd.none )
SelectAll backend selected_dropdown ->
let
newModel =
case backend of
TaskclusterBackend ->
let
data =
case model.taskcluster.data of
Success data ->
data
_ ->
[]
newItems =
data
|> List.filter (\x -> x.name == selected_dropdown)
|> List.map (\x -> x.builders)
|> List.concat
|> List.foldr
(\x y ->
Dict.update x.name
(\z ->
Maybe.withDefault [] z
|> (::) x
|> Just
)
y
)
Dict.empty
|> Dict.keys
newSelected =
Dict.insert selected_dropdown newItems model.taskcluster.selected
in
set (taskcluster => selected) newSelected model
BuildbotBackend ->
let
data =
case model.buildbot.data of
Success data ->
data
_ ->
[]
newItems =
data
|> List.filter (\x -> x.name == selected_dropdown)
|> List.map (\x -> x.builders)
|> List.concat
|> List.foldr
(\x y ->
Dict.update x.name
(\z ->
Maybe.withDefault [] z
|> (::) x
|> Just
)
y
)
Dict.empty
|> Dict.keys
newSelected =
Dict.insert selected_dropdown newItems model.buildbot.selected
in
set (buildbot => selected) newSelected model
in
( newModel, Cmd.none )
SelectNone backend selected_dropdown ->
let
newModel =
case backend of
TaskclusterBackend ->
let
newSelected =
Dict.insert selected_dropdown [] model.taskcluster.selected
in
set (taskcluster => selected) newSelected model
BuildbotBackend ->
let
newSelected =
Dict.insert selected_dropdown [] model.buildbot.selected
in
set (buildbot => selected) newSelected model
in
( newModel, Cmd.none )
-- IMPLEMENTATION
buildbot : Focus { record | buildbot : a } a
buildbot =
create .buildbot (\f r -> { r | buildbot = f r.buildbot })
taskcluster : Focus { record | taskcluster : a } a
taskcluster =
create .taskcluster (\f r -> { r | taskcluster = f r.taskcluster })
data : Focus { record | data : a } a
data =
create .data (\f r -> { r | data = f r.data })
clobber : Focus { record | clobber : a } a
clobber =
create .clobber (\f r -> { r | clobber = f r.clobber })
selected_dropdown : Focus { record | selected_dropdown : a } a
selected_dropdown =
create .selected_dropdown (\f r -> { r | selected_dropdown = f r.selected_dropdown })
selected : Focus { record | selected : a } a
selected =
create .selected (\f r -> { r | selected = f r.selected })
selected_details : Focus { record | selected_details : a } a
selected_details =
create .selected_details (\f r -> { r | selected_details = f r.selected_details })
decodeFetchData : JsonDecode.Decoder BackendData
decodeFetchData =
JsonDecode.list
(JsonDecode.object2 BackendBranch
("name" := JsonDecode.string)
("builders"
:= JsonDecode.list
(JsonDecode.object6 BackendBuilder
("branch" := JsonDecode.string)
("builddir" := JsonDecode.string)
("lastclobber" := JsonDecode.int)
("name" := JsonDecode.string)
("slave" := JsonDecode.string)
("who" := JsonDecode.string)
)
)
)
decodeClobberData : JsonDecode.Decoder BackendResponse
decodeClobberData =
JsonDecode.list JsonDecode.string
fetchBackend : (WebData BackendData -> Msg) -> String -> Cmd Msg
fetchBackend afterMsg url =
getJson decodeFetchData url
|> RemoteData.asCmd
|> Cmd.map afterMsg
clobberBackend : (WebData BackendResponse -> Msg) -> String -> Http.Body -> Cmd Msg
clobberBackend afterMsg url body =
postJson decodeClobberData url body
|> RemoteData.asCmd
|> Cmd.map afterMsg
-- UTILS
getJson : JsonDecode.Decoder value -> String -> Task Http.Error value
getJson decoder url =
let
request =
{ verb = "GET"
, headers = [ ( "Accept", "application/json" ) ]
, url = url
, body = Http.empty
}
in
Http.fromJson decoder
(Http.send Http.defaultSettings request)
postJson : JsonDecode.Decoder value -> String -> Http.Body -> Task Http.Error value
postJson decoder url body =
let
request =
{ verb = "POST"
, headers = [ ( "Accept", "application/json" ) ]
, url = url
, body = body
}
in
Http.fromJson decoder
(Http.send Http.defaultSettings request)

Просмотреть файл

@ -1,157 +0,0 @@
module App.Form exposing (..)
import Form
import Form.Input
import Html exposing (..)
import Html.Attributes exposing (..)
maybeAppend : Maybe a -> (a -> b) -> List b -> List b
maybeAppend maybeValue f =
maybeValue
|> Maybe.map (\x -> [ f x ])
|> Maybe.withDefault []
|> List.append
maybeAppendError : Maybe a -> List (Html b) -> List (Html b)
maybeAppendError maybeError =
maybeAppend maybeError
(\x ->
div [ class "form-control-feedback" ]
[ text (toString x) ]
)
maybeAppendHelp : Maybe String -> List (Html a) -> List (Html a)
maybeAppendHelp maybeHelp =
maybeAppend maybeHelp
(\x ->
small [ class "form-text text-muted" ]
[ text x ]
)
maybeAppendLabel : Maybe String -> List (Html a) -> List (Html a)
maybeAppendLabel maybeLabel =
maybeAppend maybeLabel
(\x ->
label [ class "control-label" ]
[ text x ]
)
errorClass : Maybe error -> String
errorClass maybeError =
maybeError
|> Maybe.map (\_ -> "has-danger")
|> Maybe.withDefault ""
viewField :
Maybe a
-> Maybe String
-> List (Html Form.Msg)
-> Html Form.Msg
-> Html Form.Msg
viewField maybeError maybeLabel helpNodes inputNode =
div
[ class ("form-group " ++ errorClass maybeError) ]
([]
|> List.append helpNodes
|> maybeAppendError maybeError
|> List.append [ inputNode ]
|> maybeAppendLabel maybeLabel
)
viewTextInput :
Form.FieldState () String
-> String
-> List (Html Form.Msg)
-> List (Attribute Form.Msg)
-> Html Form.Msg
viewTextInput state labelText helpNodes attributes =
viewField
state.liveError
(Just labelText)
helpNodes
(Form.Input.textInput state
(attributes
|> List.append
[ class "form-control"
, value (Maybe.withDefault "" state.value)
]
)
)
viewSelectInput :
Form.FieldState a String
-> String
-> List (Html Form.Msg)
-> List ( String, String )
-> List (Attribute Form.Msg)
-> Html Form.Msg
viewSelectInput state labelText helpNodes options attributes =
viewField
state.liveError
(Just labelText)
helpNodes
(Form.Input.selectInput
options
state
(attributes |> List.append [ class "form-control" ])
)
viewRadioInput :
Form.FieldState a String
-> String
-> List (Html Form.Msg)
-> List ( String, String )
-> List (Attribute Form.Msg)
-> Html Form.Msg
viewRadioInput state labelText helpNodes options attributes =
let
item ( v, l ) =
label
[ class "radio-inline" ]
[ Form.Input.radioInput v state []
, text l
]
in
viewField
state.liveError
(Just labelText)
helpNodes
(div [] (List.map item options))
viewCheckboxInput :
Form.FieldState a Bool
-> String
-> Html Form.Msg
viewCheckboxInput state labelText =
label
[ class "custom-control custom-checkbox" ]
[ Form.Input.checkboxInput state [ class "custom-control-input" ]
, span
[ class "custom-control-indicator" ]
[]
, span
[ class "custom-control-description" ]
[ text labelText ]
]
viewButton : String -> List (Attribute msg) -> Html msg
viewButton labelText attributes =
button
(attributes
|> List.append
[ type_ "submit"
, class "btn btn-outline-primary"
]
)
[ text labelText ]

Просмотреть файл

@ -1,36 +0,0 @@
module App.Home exposing (..)
import App
import App.TreeStatus.Types
import Html exposing (..)
import Html.Attributes exposing (..)
import Utils
viewCard : String -> String -> Attribute msg -> Html msg
viewCard title description href =
div [ class "col-sm-6" ]
[ a
[ class "card card-block"
, href
]
[ h4 [ class "card-title" ]
[ text title ]
, p [ class "card-text" ]
[ text description ]
]
]
view : App.Model -> Html App.Msg
view model =
div [ class "row" ]
[ viewCard
"TreeStatus"
"Current status of Mozilla's version-control repositories."
(Utils.onClick <| App.NavigateTo (App.TreeStatusRoute App.TreeStatus.Types.ShowTreesRoute))
, viewCard
"ToolTool"
"Tooltool is tool for fetching binary artifacts for builds and tests. The web interface lets you browse the files currently available from the service."
(href "/tooltool")
]

Просмотреть файл

@ -1,158 +0,0 @@
module App.Layout exposing (..)
import App
import App.Utils
import Html exposing (..)
import Html.Attributes exposing (..)
import TaskclusterLogin
import Utils
viewDropdown : String -> List (Html msg) -> List (Html msg)
viewDropdown title pages =
[ div [ class "dropdown" ]
[ a
[ class "nav-link dropdown-toggle"
, id ("dropdown" ++ title)
, href "#"
, attribute "data-toggle" "dropdown"
, attribute "aria-haspopup" "true"
, attribute "aria-expanded" "false"
]
[ text title ]
, div
[ class "dropdown-menu dropdown-menu-right"
, attribute "aria-labelledby" "dropdownServices"
]
pages
]
]
viewUser : App.Model -> List (Html App.Msg)
viewUser model =
case model.user.credentials of
Just user ->
viewDropdown user.clientId
[ a
[ class "dropdown-item"
, href "https://tools.taskcluster.net/credentials"
, target "_blank"
]
[ text "Manage credentials" ]
, a
[ Utils.onClick (App.NavigateTo App.LogoutRoute)
, href "#"
, class "dropdown-item"
]
[ text "Logout" ]
]
Nothing ->
let
loginMsg =
App.TaskclusterLoginMsg <| TaskclusterLogin.Login
in
[ a
[ Utils.onClick loginMsg
, href "#"
, class "nav-link"
]
[ text "Login" ]
]
viewNavBar : App.Model -> List (Html App.Msg)
viewNavBar model =
[ a
[ Utils.onClick (App.NavigateTo App.HomeRoute)
, href "#"
, class "navbar-brand"
]
[ text "Release Engineering" ]
, ul [ class "navbar-nav" ]
[ li [ class "nav-item" ] (viewUser model)
]
]
viewFooter : App.Model -> List (Html App.Msg)
viewFooter model =
[ hr [] []
, ul []
[ li []
[ a [ href model.docsUrl ]
[ text "Documentation" ]
]
, li []
[ a [ href "https://github.com/mozilla/release-services/blob/master/CONTRIBUTING.rst" ]
[ text "Contribute" ]
]
, li []
[ a [ href "https://github.com/mozilla/release-services/issues/new" ]
[ text "Contact" ]
]
]
, div []
[ text "version: "
, a [ href ("https://github.com/mozilla/release-services/releases/tag/v" ++ model.version) ]
[ text model.version ]
]
]
viewNotFound : App.Model -> Html.Html App.Msg
viewNotFound model =
div [ class "hero-unit" ]
[ h1 [] [ text "Page Not Found" ] ]
view : (App.Model -> Html.Html App.Msg) -> App.Model -> Html.Html App.Msg
view viewRoute model =
let
routeName =
case model.route of
App.HomeRoute ->
"home"
App.ToolToolRoute ->
"tooltool"
App.TreeStatusRoute _ ->
"treestatus"
_ ->
""
isLoading =
case model.user.tokens of
Just _ ->
case model.user.credentials of
Just _ ->
if List.length model.userScopes.scopes == 0 then
True
else
False
_ ->
True
_ ->
False
in
div [ id ("page-" ++ routeName) ]
[ nav
[ id "navbar"
, class "navbar navbar-toggleable-md bg-faded navbar-inverse"
]
[ div [ class "container" ] (viewNavBar model) ]
, div [ id "content" ]
[ div [ class "container" ]
[ if isLoading then
App.Utils.loading
else
viewRoute model
]
]
, footer [ class "container" ] (viewFooter model)
]

Просмотреть файл

@ -1,39 +0,0 @@
module App.Tokens exposing (..)
import App.Types
import Html exposing (..)
import UrlParser
type alias Model =
{}
type Msg
= Load
page : a -> App.Types.Page a b
page outRoute =
{ title = "Tokens"
, description =
"Tokens are used to allow automated services to authenticate to Releng API without being tied to a user's identity."
, matcher = UrlParser.map outRoute (UrlParser.s "tokens")
}
init : Model
init =
{}
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Load ->
( model, Cmd.none )
view : Model -> Html Msg
view model =
div [] [ text "Not yet implemented!" ]

Просмотреть файл

@ -1,39 +0,0 @@
module App.ToolTool exposing (..)
import App.Types
import Html exposing (..)
import UrlParser
type alias Model =
{}
type Msg
= Load
page : a -> App.Types.Page a b
page outRoute =
{ title = "ToolTool"
, description =
"Tooltool is tool for fetching binary artifacts for builds and tests. The web interface lets you browse the files currently available from the service."
, matcher = UrlParser.map outRoute (UrlParser.s "tooltool")
}
init : Model
init =
{}
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Load ->
( model, Cmd.none )
view : Model -> Html Msg
view model =
div [] [ text "Not yet implemented!" ]

Просмотреть файл

@ -1,625 +0,0 @@
module App.TreeStatus exposing (..)
import App.TreeStatus.Api
import App.TreeStatus.Form
import App.TreeStatus.Types
import App.TreeStatus.View
import App.Types
import App.UserScopes
import App.Utils
import Form
import Hawk
import Html exposing (..)
import Html.Attributes exposing (..)
import Http
import Navigation
import RemoteData
import TaskclusterLogin
import Title
import UrlParser exposing ((</>))
import Utils
--
-- ROUTING
--
routeParser : UrlParser.Parser (App.TreeStatus.Types.Route -> a) a
routeParser =
UrlParser.oneOf
[ UrlParser.map App.TreeStatus.Types.ShowTreesRoute UrlParser.top
, UrlParser.map App.TreeStatus.Types.AddTreeRoute (UrlParser.s "add")
, UrlParser.map App.TreeStatus.Types.UpdateTreesRoute (UrlParser.s "update")
, UrlParser.map App.TreeStatus.Types.DeleteTreesRoute (UrlParser.s "delete")
, UrlParser.map App.TreeStatus.Types.ShowTreeRoute (UrlParser.s "show" </> UrlParser.string)
]
reverseRoute : App.TreeStatus.Types.Route -> String
reverseRoute route =
case route of
App.TreeStatus.Types.ShowTreesRoute ->
"/treestatus"
App.TreeStatus.Types.AddTreeRoute ->
"/treestatus/add"
App.TreeStatus.Types.UpdateTreesRoute ->
"/treestatus/update"
App.TreeStatus.Types.DeleteTreesRoute ->
"/treestatus/delete"
App.TreeStatus.Types.ShowTreeRoute name ->
"/treestatus/show/" ++ name
page : (App.TreeStatus.Types.Route -> a) -> App.Types.Page a b
page outRoute =
{ title = "TreeStatus"
, description = "Current status of Mozilla's version-control repositories."
, matcher = UrlParser.map outRoute (UrlParser.s "treestatus" </> routeParser)
}
--
-- UPDATE
--
init : String -> App.TreeStatus.Types.Model App.TreeStatus.Form.AddTree App.TreeStatus.Form.UpdateTree App.TreeStatus.Form.UpdateStack App.TreeStatus.Form.UpdateLog
init url =
{ baseUrl = url
, treesAlerts = []
, trees = RemoteData.NotAsked
, treesSelected = []
, tree = RemoteData.NotAsked
, treeLogs = RemoteData.NotAsked
, treeLogsAll = RemoteData.NotAsked
, showMoreTreeLogs = False
, formAddTree = App.TreeStatus.Form.initAddTree
, formUpdateTree = App.TreeStatus.Form.initUpdateTree ""
, showUpdateStackForm = Nothing
, formUpdateStack = App.TreeStatus.Form.initUpdateStack ""
, recentChangesAlerts = []
, recentChanges = RemoteData.NotAsked
, deleteTreesConfirm = False
, deleteError = Nothing
, showUpdateLog = Nothing
, formUpdateLog = App.TreeStatus.Form.initUpdateLog ""
}
update :
App.TreeStatus.Types.Route
-> App.TreeStatus.Types.Msg
-> App.TreeStatus.Types.Model App.TreeStatus.Form.AddTree App.TreeStatus.Form.UpdateTree App.TreeStatus.Form.UpdateStack App.TreeStatus.Form.UpdateLog
-> ( App.TreeStatus.Types.Model App.TreeStatus.Form.AddTree App.TreeStatus.Form.UpdateTree App.TreeStatus.Form.UpdateStack App.TreeStatus.Form.UpdateLog, Cmd App.TreeStatus.Types.Msg, Maybe Hawk.Request )
update currentRoute msg model =
case msg of
App.TreeStatus.Types.NavigateTo route ->
let
fetchIfNotAsked data fetch =
if data == RemoteData.NotAsked then
fetch
else
Cmd.none
fetchRecentChangesIfNotAsked =
fetchIfNotAsked
model.recentChanges
(App.TreeStatus.Api.fetchRecentChanges model.baseUrl)
showAllTrees =
( { model | trees = RemoteData.Loading }
, Cmd.batch
[ Title.set_title "TreeStatus"
, App.TreeStatus.Api.fetchTrees model.baseUrl
, App.TreeStatus.Api.fetchRecentChanges model.baseUrl
]
)
redirectToTrees =
List.isEmpty model.treesSelected
&& (currentRoute
== App.TreeStatus.Types.UpdateTreesRoute
|| currentRoute
== App.TreeStatus.Types.DeleteTreesRoute
)
( newModel, newCmd ) =
-- in case there are no trees selected and we end up on update
-- url we should redirect to ShowTreesRoute
if redirectToTrees then
showAllTrees
else
case route of
App.TreeStatus.Types.ShowTreesRoute ->
showAllTrees
App.TreeStatus.Types.AddTreeRoute ->
( { model | treesAlerts = [] }
, fetchIfNotAsked
model.recentChanges
(App.TreeStatus.Api.fetchRecentChanges model.baseUrl)
)
App.TreeStatus.Types.UpdateTreesRoute ->
( { model | treesAlerts = [] }
, fetchRecentChangesIfNotAsked
)
App.TreeStatus.Types.DeleteTreesRoute ->
( { model
| treesAlerts = []
, deleteTreesConfirm = False
, deleteError = Nothing
}
, fetchRecentChangesIfNotAsked
)
App.TreeStatus.Types.ShowTreeRoute name ->
( { model
| tree = RemoteData.Loading
, treeLogs = RemoteData.Loading
, treeLogsAll = RemoteData.NotAsked
, treesSelected = [ name ]
}
, Cmd.batch
[ App.TreeStatus.Api.fetchTree model.baseUrl name
, App.TreeStatus.Api.fetchTreeLogs model.baseUrl name False
, fetchRecentChangesIfNotAsked
]
)
newRoute =
if redirectToTrees then
App.TreeStatus.Types.ShowTreesRoute
else
route
in
( newModel
, Cmd.batch
[ reverseRoute newRoute
|> Navigation.newUrl
, newCmd
]
, Nothing
)
App.TreeStatus.Types.GetTreesResult trees ->
( { model | trees = trees }, Cmd.none, Nothing )
App.TreeStatus.Types.GetTreeResult tree ->
( { model | tree = tree }, Cmd.none, Nothing )
App.TreeStatus.Types.GetTreeLogsResult logs ->
( { model | treeLogs = logs }, Cmd.none, Nothing )
App.TreeStatus.Types.GetTreeLogsAllResult logs ->
( { model | treeLogsAll = logs }, Cmd.none, Nothing )
App.TreeStatus.Types.GetRecentChangesResult recentChanges ->
( { model | recentChanges = recentChanges }, Cmd.none, Nothing )
App.TreeStatus.Types.GetTreeLogs name all ->
( model
, App.TreeStatus.Api.fetchTreeLogs model.baseUrl name True
, Nothing
)
App.TreeStatus.Types.FormAddTreeMsg formMsg ->
let
( newModel, hawkRequest ) =
App.TreeStatus.Form.updateAddTree model formMsg
in
( newModel
, Cmd.none
, hawkRequest
)
App.TreeStatus.Types.FormAddTreeResult result ->
( { model | treesAlerts = App.Utils.getAlerts result }
, Cmd.batch
[ Utils.performMsg App.TreeStatus.Form.resetAddTree
|> Cmd.map App.TreeStatus.Types.FormAddTreeMsg
, reverseRoute App.TreeStatus.Types.ShowTreesRoute
|> Navigation.newUrl
]
, Nothing
)
App.TreeStatus.Types.FormUpdateTreesMsg formMsg ->
let
( newModel, hawkRequest ) =
App.TreeStatus.Form.updateUpdateTree currentRoute model formMsg
in
( newModel
, Cmd.none
, hawkRequest
)
App.TreeStatus.Types.FormUpdateTreesResult result ->
( { model | treesAlerts = App.Utils.getAlerts result }
, Cmd.batch
[ App.TreeStatus.Api.fetchTrees model.baseUrl
, App.TreeStatus.Api.fetchRecentChanges model.baseUrl
, Utils.performMsg App.TreeStatus.Form.resetUpdateTree
|> Cmd.map App.TreeStatus.Types.FormUpdateTreesMsg
, reverseRoute App.TreeStatus.Types.ShowTreesRoute
|> Navigation.newUrl
]
, Nothing
)
App.TreeStatus.Types.SelectAllTrees ->
let
treesSelected =
case model.trees of
RemoteData.Success trees ->
List.map .name trees
_ ->
[]
in
( { model | treesSelected = treesSelected }
, Cmd.none
, Nothing
)
App.TreeStatus.Types.SelectTree name ->
let
treesSelected =
if List.member name model.treesSelected then
model.treesSelected
else
name :: model.treesSelected
in
( { model | treesSelected = treesSelected }
, Cmd.none
, Nothing
)
App.TreeStatus.Types.UnselectAllTrees ->
( { model | treesSelected = [] }
, Cmd.none
, Nothing
)
App.TreeStatus.Types.UnselectTree name ->
let
treesSelected =
List.filter (\x -> x /= name) model.treesSelected
in
( { model | treesSelected = treesSelected }
, Cmd.none
, Nothing
)
App.TreeStatus.Types.DeleteTrees ->
let
filterOutTrees =
List.filter
(\x -> Basics.not (List.member x.name model.treesSelected))
filterTrees =
List.filter
(\x -> List.member x.name model.treesSelected)
treesToDelete =
model.trees
|> RemoteData.map filterTrees
|> RemoteData.withDefault []
request =
Hawk.Request
"DeleteTrees"
"DELETE"
(model.baseUrl ++ "/trees2")
[ Http.header "Accept" "application/json" ]
(Http.jsonBody (App.TreeStatus.Api.encoderTreeNames treesToDelete))
in
if model.deleteTreesConfirm then
( { model
| treesSelected = []
, trees = RemoteData.map filterOutTrees model.trees
}
, Cmd.none
, Just request
)
else
( { model | deleteError = Just "You need to confirm to be able to delete tree(s)." }
, Cmd.none
, Nothing
)
App.TreeStatus.Types.DeleteTreesResult result ->
( { model | treesAlerts = App.Utils.getAlerts result }
, reverseRoute App.TreeStatus.Types.ShowTreesRoute
|> Navigation.newUrl
, Nothing
)
App.TreeStatus.Types.RevertChange stack ->
( { model | recentChangesAlerts = [] }
, Cmd.none
, Just
(Hawk.Request
"RevertChange"
"DELETE"
(model.baseUrl ++ "/stack2/restore/" ++ toString stack)
[ Http.header "Accept" "application/json" ]
Http.emptyBody
)
)
App.TreeStatus.Types.DiscardChange stack ->
( { model | recentChangesAlerts = [] }
, Cmd.none
, Just
(Hawk.Request
"DiscardChange"
"DELETE"
(model.baseUrl ++ "/stack2/discard/" ++ toString stack)
[ Http.header "Accept" "application/json" ]
Http.emptyBody
)
)
App.TreeStatus.Types.RecentChangeResult result ->
( { model | recentChangesAlerts = App.Utils.getAlerts result }
, Cmd.batch
[ App.TreeStatus.Api.fetchRecentChanges model.baseUrl
, App.TreeStatus.Api.fetchTrees model.baseUrl
]
, Nothing
)
App.TreeStatus.Types.DeleteTreesConfirmToggle ->
( { model | deleteTreesConfirm = Basics.not model.deleteTreesConfirm }
, Cmd.none
, Nothing
)
App.TreeStatus.Types.UpdateStackShow stack ->
let
( reason, category, status ) =
case model.recentChanges of
RemoteData.Success recentChanges ->
recentChanges
|> List.filter (\x -> x.id == stack)
|> List.head
|> Maybe.map
(\x ->
x.trees
|> List.head
|> Maybe.map
(\y ->
( y.last_state.current_reason
, y.last_state.current_tags |> List.head |> Maybe.withDefault ""
, y.last_state.current_status
)
)
|> Maybe.withDefault ( "", "", "" )
)
|> Maybe.withDefault ( "", "", "" )
_ ->
( "", "", "" )
in
( { model
| showUpdateStackForm = Just stack
, formUpdateStack = Form.initial (App.TreeStatus.Form.initUpdateStackFields reason category) (App.TreeStatus.Form.validateUpdateLog status)
}
, Cmd.none
, Nothing
)
App.TreeStatus.Types.FormUpdateStackMsg formMsg ->
let
( newModel, hawkRequest ) =
App.TreeStatus.Form.updateUpdateStack model formMsg
in
( newModel
, Cmd.none
, hawkRequest
)
App.TreeStatus.Types.FormUpdateStackResult result ->
case currentRoute of
App.TreeStatus.Types.ShowTreeRoute name ->
( { model
| recentChangesAlerts = App.Utils.getAlerts result
, showUpdateStackForm = Nothing
, treeLogs = RemoteData.Loading
, treeLogsAll = RemoteData.Loading
}
, Cmd.batch
[ App.TreeStatus.Api.fetchRecentChanges model.baseUrl
, App.TreeStatus.Api.fetchTrees model.baseUrl
, App.TreeStatus.Api.fetchTreeLogs model.baseUrl name False
, App.TreeStatus.Api.fetchTreeLogs model.baseUrl name True
]
, Nothing
)
_ ->
( { model
| recentChangesAlerts = App.Utils.getAlerts result
, showUpdateStackForm = Nothing
}
, Cmd.batch
[ App.TreeStatus.Api.fetchRecentChanges model.baseUrl
, App.TreeStatus.Api.fetchTrees model.baseUrl
]
, Nothing
)
App.TreeStatus.Types.UpdateLogShow logId ->
let
treeLogs =
if RemoteData.isSuccess model.treeLogsAll then
model.treeLogsAll
else
model.treeLogs
( reason, category, status ) =
case treeLogs of
RemoteData.Success logs ->
logs
|> List.filter (\x -> x.id == logId)
|> List.head
|> Maybe.map
(\x ->
( x.reason
, x.tags
|> List.head
|> Maybe.withDefault ""
, x.status
)
)
|> Maybe.withDefault ( "", "", "" )
_ ->
( "", "", "" )
in
( { model
| showUpdateLog = Just logId
, formUpdateLog = Form.initial (App.TreeStatus.Form.initUpdateLogFields reason category) (App.TreeStatus.Form.validateUpdateLog status)
}
, Cmd.none
, Nothing
)
App.TreeStatus.Types.FormUpdateLogMsg formMsg ->
let
( newModel, hawkRequest ) =
App.TreeStatus.Form.updateUpdateLog model formMsg
in
( newModel
, Cmd.none
, hawkRequest
)
App.TreeStatus.Types.FormUpdateLogResult result ->
case currentRoute of
App.TreeStatus.Types.ShowTreeRoute name ->
( { model
| treeLogs = RemoteData.Loading
, treeLogsAll = RemoteData.Loading
}
, Cmd.batch
[ App.TreeStatus.Api.fetchTreeLogs model.baseUrl name False
, App.TreeStatus.Api.fetchTreeLogs model.baseUrl name True
]
, Nothing
)
_ ->
( model
, Cmd.none
, Nothing
)
view :
App.TreeStatus.Types.Route
-> TaskclusterLogin.Model
-> App.UserScopes.Model
-> App.TreeStatus.Types.Model App.TreeStatus.Form.AddTree App.TreeStatus.Form.UpdateTree App.TreeStatus.Form.UpdateStack App.TreeStatus.Form.UpdateLog
-> Html App.TreeStatus.Types.Msg
view route user scopes model =
let
isLoadingRemoteData remotedata =
RemoteData.isLoading remotedata || RemoteData.isNotAsked remotedata
isLoadingScopes =
case user.tokens of
Just _ ->
case user.credentials of
Just _ ->
if List.length scopes.scopes == 0 then
True
else
False
_ ->
True
_ ->
False
isLoading =
case route of
App.TreeStatus.Types.ShowTreesRoute ->
isLoadingRemoteData model.trees
|| isLoadingScopes
App.TreeStatus.Types.AddTreeRoute ->
False
App.TreeStatus.Types.UpdateTreesRoute ->
isLoadingRemoteData model.trees
App.TreeStatus.Types.DeleteTreesRoute ->
False
App.TreeStatus.Types.ShowTreeRoute name ->
isLoadingRemoteData model.tree
|| isLoadingRemoteData model.treeLogs
|| RemoteData.isLoading model.treeLogsAll
|| isLoadingScopes
in
div [ class "container" ]
[ h1 [] [ text "TreeStatus" ]
, p [ class "lead" ]
[ text "Current status of Mozilla's version-control repositories." ]
, if isLoading then
App.Utils.loading
else
viewLoaded route scopes.scopes model
]
viewLoaded :
App.TreeStatus.Types.Route
-> List String
-> App.TreeStatus.Types.Model App.TreeStatus.Form.AddTree App.TreeStatus.Form.UpdateTree App.TreeStatus.Form.UpdateStack App.TreeStatus.Form.UpdateLog
-> Html App.TreeStatus.Types.Msg
viewLoaded route scopes model =
div []
([]
|> App.Utils.appendItem
(App.Utils.viewAlerts model.recentChangesAlerts)
|> App.Utils.appendItems
(App.TreeStatus.View.viewRecentChanges scopes model.showUpdateStackForm model.formUpdateStack model.recentChanges)
|> App.Utils.appendItem
(App.Utils.viewAlerts model.treesAlerts)
|> App.Utils.appendItem
(App.TreeStatus.View.viewTreesTitle route)
|> App.Utils.appendItem
(App.TreeStatus.View.viewButtons route scopes model)
|> App.Utils.appendItems
(case route of
App.TreeStatus.Types.ShowTreesRoute ->
App.TreeStatus.View.viewTrees scopes model.trees model.treesSelected
App.TreeStatus.Types.AddTreeRoute ->
[ App.TreeStatus.Form.viewAddTree model.formAddTree
|> Html.map App.TreeStatus.Types.FormAddTreeMsg
]
App.TreeStatus.Types.UpdateTreesRoute ->
[ App.TreeStatus.Form.viewUpdateTree model.treesSelected model.trees model.formUpdateTree
|> Html.map App.TreeStatus.Types.FormUpdateTreesMsg
]
App.TreeStatus.Types.DeleteTreesRoute ->
App.TreeStatus.View.viewConfirmDelete model.deleteError model.deleteTreesConfirm model.treesSelected
App.TreeStatus.Types.ShowTreeRoute name ->
App.TreeStatus.View.viewTree scopes model.tree model.treeLogs model.treeLogsAll model.showUpdateLog model.formUpdateLog name
)
)

Просмотреть файл

@ -1,243 +0,0 @@
module App.TreeStatus.Api exposing (..)
import App.TreeStatus.Types
import Http
import Json.Decode as JsonDecode
import Json.Encode as JsonEncode
import RemoteData exposing (WebData)
encoderUpdateStack :
{ a
| reason : String
, tags : List String
}
-> JsonEncode.Value
encoderUpdateStack data =
JsonEncode.object
[ ( "reason", JsonEncode.string data.reason )
, ( "tags", JsonEncode.list (List.map JsonEncode.string data.tags) )
]
encoderUpdateTree :
{ a
| message_of_the_day : String
, reason : String
, remember : Bool
, status : String
, trees : List String
, tags : List String
}
-> JsonEncode.Value
encoderUpdateTree data =
JsonEncode.object
[ ( "trees", JsonEncode.list (List.map JsonEncode.string data.trees) )
, ( "status", JsonEncode.string data.status )
, ( "reason", JsonEncode.string data.reason )
, ( "tags", JsonEncode.list (List.map JsonEncode.string data.tags) )
, ( "message_of_the_day", JsonEncode.string data.message_of_the_day )
, ( "remember", JsonEncode.bool data.remember )
]
encoderUpdateTrees :
{ a
| reason : String
, remember : Bool
, status : String
, trees : List String
, tags : List String
}
-> JsonEncode.Value
encoderUpdateTrees data =
JsonEncode.object
[ ( "trees", JsonEncode.list (List.map JsonEncode.string data.trees) )
, ( "status", JsonEncode.string data.status )
, ( "reason", JsonEncode.string data.reason )
, ( "tags", JsonEncode.list (List.map JsonEncode.string data.tags) )
, ( "remember", JsonEncode.bool data.remember )
]
encoderTree : App.TreeStatus.Types.Tree -> JsonEncode.Value
encoderTree tree =
JsonEncode.object
[ ( "tree", JsonEncode.string tree.name )
, ( "status", JsonEncode.string tree.status )
, ( "reason", JsonEncode.string tree.reason )
, ( "message_of_the_day", JsonEncode.string tree.message_of_the_day )
]
encoderTreeNames : App.TreeStatus.Types.Trees -> JsonEncode.Value
encoderTreeNames trees =
JsonEncode.object
[ ( "trees", JsonEncode.list (List.map (\x -> JsonEncode.string x.name) trees) )
]
decoderTrees : JsonDecode.Decoder App.TreeStatus.Types.Trees
decoderTrees =
JsonDecode.list decoderTree2
|> JsonDecode.at [ "result" ]
decoderTree2 : JsonDecode.Decoder App.TreeStatus.Types.Tree
decoderTree2 =
JsonDecode.map5 App.TreeStatus.Types.Tree
(JsonDecode.field "tree" JsonDecode.string)
(JsonDecode.field "status" JsonDecode.string)
(JsonDecode.field "reason" JsonDecode.string)
(JsonDecode.field "message_of_the_day" JsonDecode.string)
(JsonDecode.field "tags" (JsonDecode.list JsonDecode.string))
decoderTree : JsonDecode.Decoder App.TreeStatus.Types.Tree
decoderTree =
JsonDecode.at [ "result" ] decoderTree2
decoderTreeLogs : JsonDecode.Decoder App.TreeStatus.Types.TreeLogs
decoderTreeLogs =
JsonDecode.list decoderTreeLog
|> JsonDecode.at [ "result" ]
decoderTreeLog : JsonDecode.Decoder App.TreeStatus.Types.TreeLog
decoderTreeLog =
JsonDecode.map7 App.TreeStatus.Types.TreeLog
(JsonDecode.field "id" JsonDecode.int)
(JsonDecode.field "tree" JsonDecode.string)
(JsonDecode.field "when" JsonDecode.string)
(JsonDecode.field "who" JsonDecode.string)
(JsonDecode.field "status" JsonDecode.string)
(JsonDecode.field "reason" JsonDecode.string)
(JsonDecode.field "tags" (JsonDecode.list JsonDecode.string))
decoderRecentChanges : JsonDecode.Decoder (List App.TreeStatus.Types.RecentChange)
decoderRecentChanges =
JsonDecode.list decoderRecentChange
|> JsonDecode.at [ "result" ]
decoderRecentChange : JsonDecode.Decoder App.TreeStatus.Types.RecentChange
decoderRecentChange =
JsonDecode.map6 App.TreeStatus.Types.RecentChange
(JsonDecode.field "id" JsonDecode.int)
(JsonDecode.field "trees" (JsonDecode.list decoderRecentChangeTree))
(JsonDecode.field "when" JsonDecode.string)
(JsonDecode.field "who" JsonDecode.string)
(JsonDecode.field "status" JsonDecode.string)
(JsonDecode.field "reason" JsonDecode.string)
decoderRecentChangeTree : JsonDecode.Decoder App.TreeStatus.Types.RecentChangeTree
decoderRecentChangeTree =
JsonDecode.map3 App.TreeStatus.Types.RecentChangeTree
(JsonDecode.field "id" JsonDecode.int)
(JsonDecode.field "tree" JsonDecode.string)
(JsonDecode.field "last_state" decoderRecentChangeTreeLastState)
decoderRecentChangeTreeLastState : JsonDecode.Decoder App.TreeStatus.Types.RecentChangeTreeLastState
decoderRecentChangeTreeLastState =
JsonDecode.map8 App.TreeStatus.Types.RecentChangeTreeLastState
(JsonDecode.field "reason" JsonDecode.string)
(JsonDecode.field "status" JsonDecode.string)
(JsonDecode.field "tags" (JsonDecode.list JsonDecode.string))
(JsonDecode.field "log_id" (JsonDecode.nullable JsonDecode.int))
(JsonDecode.field "current_reason" JsonDecode.string)
(JsonDecode.field "current_status" JsonDecode.string)
(JsonDecode.field "current_tags" (JsonDecode.list JsonDecode.string))
(JsonDecode.field "current_log_id" (JsonDecode.nullable JsonDecode.int))
get :
(RemoteData.RemoteData Http.Error a -> b)
-> String
-> JsonDecode.Decoder a
-> Cmd b
get msg url decoder =
Http.get url decoder
|> Http.toTask
|> RemoteData.asCmd
|> Cmd.map msg
fetchTrees :
String
-> Cmd App.TreeStatus.Types.Msg
fetchTrees url =
get App.TreeStatus.Types.GetTreesResult
(url ++ "/trees2")
decoderTrees
fetchTree :
String
-> String
-> Cmd App.TreeStatus.Types.Msg
fetchTree url name =
get App.TreeStatus.Types.GetTreeResult
(url ++ "/trees/" ++ name)
decoderTree
fetchTreeLogs :
String
-> String
-> Bool
-> Cmd App.TreeStatus.Types.Msg
fetchTreeLogs url name all =
case all of
True ->
get App.TreeStatus.Types.GetTreeLogsAllResult
(url ++ "/trees/" ++ name ++ "/logs?all=1")
decoderTreeLogs
False ->
get App.TreeStatus.Types.GetTreeLogsResult
(url ++ "/trees/" ++ name ++ "/logs?all=0")
decoderTreeLogs
fetchRecentChanges :
String
-> Cmd App.TreeStatus.Types.Msg
fetchRecentChanges url =
get App.TreeStatus.Types.GetRecentChangesResult
(url ++ "/stack")
decoderRecentChanges
hawkResponse :
Cmd (WebData String)
-> String
-> Cmd App.TreeStatus.Types.Msg
hawkResponse response route =
case route of
"AddTree" ->
Cmd.map App.TreeStatus.Types.FormAddTreeResult response
"DeleteTrees" ->
Cmd.map App.TreeStatus.Types.DeleteTreesResult response
"UpdateTrees" ->
Cmd.map App.TreeStatus.Types.FormUpdateTreesResult response
"RevertChange" ->
Cmd.map App.TreeStatus.Types.RecentChangeResult response
"DiscardChange" ->
Cmd.map App.TreeStatus.Types.RecentChangeResult response
"UpdateStack" ->
Cmd.map App.TreeStatus.Types.FormUpdateStackResult response
"UpdateLog" ->
Cmd.map App.TreeStatus.Types.FormUpdateLogResult response
_ ->
Cmd.none

Просмотреть файл

@ -1,763 +0,0 @@
module App.TreeStatus.Form exposing (..)
import App.Form
import App.TreeStatus.Api
import App.TreeStatus.Types
import App.Utils
import Form
import Form.Field
import Form.Input
import Form.Validate
import Hawk
import Html exposing (..)
import Html.Attributes exposing (..)
import Http
import RemoteData
import Utils
type alias AddTree =
{ name : String }
type alias UpdateStack =
{ reason : String
, tags : String
}
type alias UpdateLog =
{ reason : String
, tags : String
}
type alias UpdateTree =
{ status : String
, reason : String
, message_of_the_day : String
, tags : String
, remember : Bool
}
validateAddTree : Form.Validate.Validation () AddTree
validateAddTree =
Form.Validate.map AddTree
(Form.Validate.field "name" Form.Validate.string)
validateUpdateLog : String -> Form.Validate.Validation () UpdateLog
validateUpdateLog status =
if status == "closed" then
Form.Validate.map2 UpdateLog
(Form.Validate.field "reason" Form.Validate.string)
(Form.Validate.field "tags" Form.Validate.string)
else
Form.Validate.map2 UpdateLog
(Form.Validate.field "reason"
(Form.Validate.oneOf
[ Form.Validate.string
, Form.Validate.emptyString
]
)
)
(Form.Validate.field "tags"
(Form.Validate.oneOf
[ Form.Validate.string
, Form.Validate.emptyString
]
)
)
validateUpdateTree : String -> Form.Validate.Validation () UpdateTree
validateUpdateTree status =
if status == "closed" then
Form.Validate.map5 UpdateTree
(Form.Validate.field "status" Form.Validate.string)
(Form.Validate.field "reason" Form.Validate.string)
(Form.Validate.field "message_of_the_day"
(Form.Validate.oneOf
[ Form.Validate.string
, Form.Validate.emptyString
]
)
)
(Form.Validate.field "tags" Form.Validate.string)
(Form.Validate.field "remember" Form.Validate.bool)
else
Form.Validate.map5 UpdateTree
(Form.Validate.field "status" Form.Validate.string)
(Form.Validate.field "reason"
(Form.Validate.oneOf
[ Form.Validate.string
, Form.Validate.emptyString
]
)
)
(Form.Validate.field "message_of_the_day"
(Form.Validate.oneOf
[ Form.Validate.string
, Form.Validate.emptyString
]
)
)
(Form.Validate.field "tags"
(Form.Validate.oneOf
[ Form.Validate.string
, Form.Validate.emptyString
]
)
)
(Form.Validate.field "remember" Form.Validate.bool)
initAddTreeFields : List ( String, Form.Field.Field )
initAddTreeFields =
[ ( "name", Form.Field.string "" ) ]
initUpdateStackFields : String -> String -> List ( String, Form.Field.Field )
initUpdateStackFields reason tags =
[ ( "reason", Form.Field.string reason )
, ( "tags", Form.Field.string tags )
]
initUpdateLogFields : String -> String -> List ( String, Form.Field.Field )
initUpdateLogFields reason tags =
[ ( "reason", Form.Field.string reason )
, ( "tags", Form.Field.string tags )
]
initUpdateTreeFields : List ( String, Form.Field.Field )
initUpdateTreeFields =
[ ( "status", Form.Field.string "" )
, ( "reason", Form.Field.string "" )
, ( "message_of_the_day", Form.Field.string "" )
, ( "tags", Form.Field.string "" )
, ( "remember", Form.Field.bool True )
]
initAddTree : Form.Form () AddTree
initAddTree =
Form.initial initAddTreeFields validateAddTree
initUpdateStack : String -> Form.Form () UpdateStack
initUpdateStack status =
Form.initial (initUpdateStackFields "" "") (validateUpdateLog status)
initUpdateLog : String -> Form.Form () UpdateLog
initUpdateLog status =
Form.initial (initUpdateLogFields "" "") (validateUpdateLog status)
initUpdateTree : String -> Form.Form () UpdateTree
initUpdateTree status =
Form.initial initUpdateTreeFields (validateUpdateTree status)
resetAddTree : Form.Msg
resetAddTree =
Form.Reset initAddTreeFields
resetUpdateTree : Form.Msg
resetUpdateTree =
Form.Reset initUpdateTreeFields
updateAddTree :
App.TreeStatus.Types.Model AddTree UpdateTree UpdateStack UpdateLog
-> Form.Msg
-> ( App.TreeStatus.Types.Model AddTree UpdateTree UpdateStack UpdateLog, Maybe Hawk.Request )
updateAddTree model formMsg =
let
form =
Form.update validateAddTree formMsg model.formAddTree
tree name =
App.TreeStatus.Types.Tree name "closed" "new tree" "" []
newTreeRequest name =
Hawk.Request
"AddTree"
"PUT"
(model.baseUrl ++ "/trees/" ++ name)
-- probably this should be in Hawk.elm
[ Http.header "Accept" "application/json" ]
(Http.jsonBody (App.TreeStatus.Api.encoderTree (tree name)))
( trees, alerts, hawkRequest ) =
case formMsg of
Form.Submit ->
if Form.getErrors form /= [] then
( model.trees, [], Nothing )
else
-- opurtonistic update
( Form.getOutput form
|> Maybe.map (\x -> [ tree x.name ])
|> Maybe.withDefault []
|> (\y -> RemoteData.map (\x -> List.append x y) model.trees)
, []
, Form.getOutput form
|> Maybe.map (\x -> newTreeRequest x.name)
)
_ ->
( model.trees, model.treesAlerts, Nothing )
in
( { model
| formAddTree = form
, treesAlerts = alerts
, trees = trees
}
, hawkRequest
)
updateUpdateStack :
App.TreeStatus.Types.Model AddTree UpdateTree UpdateStack UpdateLog
-> Form.Msg
-> ( App.TreeStatus.Types.Model AddTree UpdateTree UpdateStack UpdateLog, Maybe Hawk.Request )
updateUpdateStack model formMsg =
let
status =
model.recentChanges
|> RemoteData.withDefault []
|> List.filter (\x -> Just x.id == model.showUpdateStackForm)
|> List.map
(\x ->
x.trees
|> List.head
|> Maybe.map (\y -> y.last_state.current_status)
|> Maybe.withDefault ""
)
|> List.head
|> Maybe.withDefault ""
form =
Form.update (validateUpdateLog status) formMsg model.formUpdateStack
formOutput =
Form.getOutput form
( recentChanges, alerts, hawkRequest, showUpdateStackForm ) =
case ( formMsg, model.recentChanges, model.showUpdateStackForm, formOutput ) of
( Form.Submit, RemoteData.Success recentChanges, Just recentChangeId, Just formOutput ) ->
let
newRecentChanges =
List.map updateRecentChange recentChanges
updateRecentChange recentChange =
if recentChangeId == recentChange.id then
{ recentChange
| reason = formOutput.reason
, trees = List.map updateRecentChangeTree recentChange.trees
}
else
recentChange
updateRecentChangeTree tree =
let
last_state =
tree.last_state
in
{ tree
| last_state =
{ last_state
| reason = formOutput.reason
, tags = [ formOutput.tags ]
}
}
hawkRequest_ =
Hawk.Request
"UpdateStack"
"PATCH"
(model.baseUrl ++ "/stack/" ++ toString recentChangeId)
[ Http.header "Accept" "application/json" ]
(Http.jsonBody (App.TreeStatus.Api.encoderUpdateStack { reason = formOutput.reason, tags = [ formOutput.tags ] }))
( alerts, hawkRequest ) =
if Form.getErrors form == [] then
( [], Just hawkRequest_ )
else
( model.recentChangesAlerts, Nothing )
in
( RemoteData.Success newRecentChanges, alerts, hawkRequest, model.showUpdateStackForm )
( Form.Reset _, _, _, _ ) ->
( model.recentChanges, model.recentChangesAlerts, Nothing, Nothing )
( _, _, _, _ ) ->
( model.recentChanges, model.recentChangesAlerts, Nothing, model.showUpdateStackForm )
in
( { model
| formUpdateStack = form
, recentChangesAlerts = alerts
, recentChanges = recentChanges
, showUpdateStackForm = showUpdateStackForm
}
, hawkRequest
)
updateUpdateLog :
App.TreeStatus.Types.Model AddTree UpdateTree UpdateStack UpdateLog
-> Form.Msg
-> ( App.TreeStatus.Types.Model AddTree UpdateTree UpdateStack UpdateLog, Maybe Hawk.Request )
updateUpdateLog model formMsg =
let
status =
logs
|> List.filter (\x -> Just x.id == model.showUpdateLog)
|> List.map (\x -> x.status)
|> List.head
|> Maybe.withDefault ""
logs =
RemoteData.withDefault []
(if RemoteData.isSuccess model.treeLogsAll then
model.treeLogsAll
else
model.treeLogs
)
formOutput =
Form.getOutput form |> Maybe.withDefault (UpdateLog "" "")
form =
Form.update (validateUpdateLog status) formMsg model.formUpdateLog
updateLog logId logs =
List.map
(\log ->
if logId == log.id then
{ log
| reason = formOutput.reason
, tags = [ formOutput.tags ]
}
else
log
)
logs
requestBody =
App.TreeStatus.Api.encoderUpdateStack
{ reason = formOutput.reason
, tags = [ formOutput.tags ]
}
makeHawkRequest logId =
Hawk.Request
"UpdateStack"
"PATCH"
(model.baseUrl ++ "/log/" ++ toString logId)
[ Http.header "Accept" "application/json" ]
(Http.jsonBody requestBody)
( showUpdateLog, treeLogs, treeLogsAll, hawkRequest ) =
case ( formMsg, model.showUpdateLog ) of
( Form.Submit, Just logId ) ->
let
errors =
Form.getErrors form
in
( if List.isEmpty errors then
Nothing
else
model.showUpdateLog
-- TODO: update recentChanges
, RemoteData.map (updateLog logId) model.treeLogs
, RemoteData.map (updateLog logId) model.treeLogsAll
-- TODO: pass in status
, if List.isEmpty errors then
Just (makeHawkRequest logId)
else
Nothing
)
( Form.Reset _, _ ) ->
( Nothing
, model.treeLogs
, model.treeLogsAll
, Nothing
)
( _, _ ) ->
( model.showUpdateLog
, model.treeLogs
, model.treeLogsAll
, Nothing
)
in
( { model
| formUpdateLog = form
, showUpdateLog = showUpdateLog
, treeLogs = treeLogs
, treeLogsAll = treeLogsAll
}
, hawkRequest
)
updateUpdateTree :
App.TreeStatus.Types.Route
-> App.TreeStatus.Types.Model AddTree UpdateTree UpdateStack UpdateLog
-> Form.Msg
-> ( App.TreeStatus.Types.Model AddTree UpdateTree UpdateStack UpdateLog, Maybe Hawk.Request )
updateUpdateTree route model formMsg =
let
status =
(Form.getFieldAsString "status" model.formUpdateTree).value |> Maybe.withDefault ""
form =
Form.update (validateUpdateTree status) formMsg model.formUpdateTree
createRequest data =
Hawk.Request
"UpdateTrees"
"PATCH"
(model.baseUrl ++ "/trees")
[ Http.header "Accept" "application/json" ]
((if List.length model.treesSelected /= 1 then
{ trees = model.treesSelected
, status = data.status
, reason = data.reason
, tags = [ data.tags ]
, remember = data.remember
}
|> App.TreeStatus.Api.encoderUpdateTrees
else
{ trees = model.treesSelected
, status = data.status
, reason = data.reason
, tags = [ data.tags ]
, message_of_the_day = data.message_of_the_day
, remember = data.remember
}
|> App.TreeStatus.Api.encoderUpdateTree
)
|> Http.jsonBody
)
( alerts, hawkRequest ) =
case formMsg of
Form.Submit ->
if Form.getErrors form /= [] then
( [], Nothing )
else
( []
, Form.getOutput form
|> Maybe.map (\x -> createRequest x)
)
_ ->
( model.treesAlerts, Nothing )
in
( { model
| formUpdateTree = form
, treesAlerts = alerts
}
, hawkRequest
)
fieldClass : { b | error : Maybe a } -> String
fieldClass field =
case field.error of
Just error ->
"input-group has-danger"
Nothing ->
"input-group "
fieldError : { b | error : Maybe a } -> Html c
fieldError field =
case field.error of
Just error ->
div [ class "has-danger" ]
[ span [ class "form-control-feedback" ]
[ text (toString error) ]
]
Nothing ->
text ""
viewAddTree : Form.Form () AddTree -> Html Form.Msg
viewAddTree form =
let
state =
Form.getFieldAsString "name" form
in
div
[ id "treestatus-form" ]
[ Html.form
[]
[ App.Form.viewField
(if Form.isSubmitted form then
state.error
else
Nothing
)
(Just "Tree name")
[]
(Form.Input.textInput state
[ class "form-control"
, value (Maybe.withDefault "" state.value)
, placeholder "New tree name ..."
]
)
, App.Form.viewButton
"Add"
[ Utils.onClick Form.Submit
]
, div [ class "clearfix" ] []
]
]
viewUpdateStack :
App.TreeStatus.Types.RecentChange
-> Form.Form () UpdateStack
-> Html Form.Msg
viewUpdateStack recentChange form =
div [ id "treestatus-form" ]
[ Html.form
[]
[ App.Form.viewRadioInput
(Form.getFieldAsString "tags" form)
"Reason category"
[]
(App.TreeStatus.Types.possibleTreeTags
|> List.append
(if recentChange.status == "closed" then
[]
else
[ ( "", "No category" ) ]
)
)
[]
, App.Form.viewTextInput
(Form.getFieldAsString "reason" form)
"Reason"
[ small
[ class "form-text text-muted" ]
[ p []
[ text
("Please indicate the reason for "
++ "closure, preferably with a bug link."
)
]
, p []
[ text
("Please indicate conditions for "
++ "reopening, especially if you might "
++ "disappear before reopening the "
++ "tree yourself."
)
]
]
]
[ placeholder "(no reason)" ]
]
, div [ class "btn-group" ]
[ App.Form.viewButton
"Update"
[ Utils.onClick Form.Submit
]
, App.Form.viewButton
"Cancel"
[ Utils.onClick (Form.Reset (initUpdateStackFields "" ""))
, class "btn btn-outline-danger"
]
]
, div [ class "clearfix" ] []
]
viewUpdateLog :
String
-> Form.Form () UpdateLog
-> Html Form.Msg
viewUpdateLog status form =
div [ id "treestatus-form" ]
[ Html.form
[]
[ App.Form.viewRadioInput
(Form.getFieldAsString "tags" form)
"Reason category"
[]
(App.TreeStatus.Types.possibleTreeTags
|> List.append
(if status == "closed" then
[]
else
[ ( "", "No category" ) ]
)
)
[]
, App.Form.viewTextInput
(Form.getFieldAsString "reason" form)
"Reason"
[ small
[ class "form-text text-muted" ]
[ p []
[ text
("Please indicate the reason for "
++ "closure, preferably with a bug link."
)
]
, p []
[ text
("Please indicate conditions for "
++ "reopening, especially if you might "
++ "disappear before reopening the "
++ "tree yourself."
)
]
]
]
[ placeholder "(no reason)" ]
]
, div [ class "btn-group" ]
[ App.Form.viewButton
"Update"
[ Utils.onClick Form.Submit
]
, App.Form.viewButton
"Cancel"
-- TODO: this should reset to original fields
[ Utils.onClick (Form.Reset (initUpdateLogFields "" ""))
, class "btn btn-outline-danger"
]
]
, div [ class "clearfix" ] []
]
viewUpdateTree :
List String
-> RemoteData.WebData App.TreeStatus.Types.Trees
-> Form.Form () UpdateTree
-> Html Form.Msg
viewUpdateTree treesSelected trees form =
div [ id "treestatus-form" ]
[ div
[]
([]
|> App.Utils.appendItem
(text "You are about to update the following trees:")
|> App.Utils.appendItem
(treesSelected
|> List.map
(\treeName ->
let
status =
trees
|> RemoteData.map (List.filter (\t -> t.name == treeName))
|> RemoteData.map (List.map (\t -> t.status))
|> RemoteData.toMaybe
|> Maybe.withDefault []
|> List.head
|> Maybe.withDefault "closed"
in
li []
[ text treeName
, text " ("
, span
[ class ("badge badge-" ++ App.Utils.treeStatusLevel status) ]
[ text status ]
, text ")"
]
)
|> ul []
)
)
, hr [] []
, Html.form
[]
[ App.Form.viewSelectInput
(Form.getFieldAsString "status" form)
"Status"
[]
(App.TreeStatus.Types.possibleTreeStatuses
|> List.append [ ( "", "" ) ]
)
[]
, App.Form.viewRadioInput
(Form.getFieldAsString "tags" form)
(if (Form.getFieldAsString "status" form).value == Just "closed" then
"Reason category (required to close)"
else
"Reason category"
)
[]
(App.TreeStatus.Types.possibleTreeTags
|> List.append [ ( "", "No category" ) ]
)
[]
, App.Form.viewTextInput
(Form.getFieldAsString "reason" form)
(if (Form.getFieldAsString "status" form).value == Just "closed" then
"Reason (required to close)"
else
"Reason"
)
[ small
[ class "form-text text-muted" ]
[ p []
[ text
("Please indicate the reason for "
++ "closure, preferably with a bug link."
)
]
, p []
[ text
("Please indicate conditions for "
++ "reopening, especially if you might "
++ "disappear before reopening the "
++ "tree yourself."
)
]
]
]
[ placeholder "(no reason)" ]
, div
[ class "form-group" ]
[ label [ class "control-label" ] [ text "Remember change" ]
, div
[]
[ App.Form.viewCheckboxInput
(Form.getFieldAsBool "remember" form)
"Remember this change to undo later"
]
]
, if List.length treesSelected /= 1 then
text ""
else
hr [] []
, if List.length treesSelected /= 1 then
text ""
else
App.Form.viewTextInput
(Form.getFieldAsString "message_of_the_day" form)
"Message of the day"
[]
[ placeholder "(no change)" ]
, hr [] []
, App.Form.viewButton
"Update"
[ Utils.onClick Form.Submit
]
, div [ class "clearfix" ] []
]
]

Просмотреть файл

@ -1,147 +0,0 @@
module App.TreeStatus.Types exposing (..)
import App.Types
import Form
import RemoteData exposing (WebData)
type Route
= AddTreeRoute
| UpdateTreesRoute
| DeleteTreesRoute
| ShowTreesRoute
| ShowTreeRoute String
type alias Tree =
{ name : String
, status : String
, reason : String
, message_of_the_day : String
, tags : List String
}
type alias Trees =
List Tree
type alias TreeLog =
{ id : Int
, name : String
, when : String
, who : String
, status : String
, reason : String
, tags : List String
}
type alias TreeLogs =
List TreeLog
type alias RecentChangeTreeLastState =
{ reason : String
, status : String
, tags : List String
, log_id : Maybe Int
, current_reason : String
, current_status : String
, current_tags : List String
, current_log_id : Maybe Int
}
type alias RecentChangeTree =
{ id : Int
, tree : String
, last_state : RecentChangeTreeLastState
}
type alias RecentChangeId =
Int
type alias RecentChange =
{ id : RecentChangeId
, trees : List RecentChangeTree
, when : String
, who : String
, status : String
, reason : String
}
type alias Model addForm updateForm updateStackForm updateLogForm =
{ baseUrl : String
, treesAlerts : List App.Types.Alert
, trees : RemoteData.WebData Trees
, treesSelected : List String
, tree : RemoteData.WebData Tree
, treeLogs : RemoteData.WebData TreeLogs
, treeLogsAll : RemoteData.WebData TreeLogs
, showMoreTreeLogs : Bool
, formAddTree : Form.Form () addForm
, formUpdateTree : Form.Form () updateForm
, formUpdateStack : Form.Form () updateStackForm
, showUpdateStackForm : Maybe RecentChangeId
, recentChangesAlerts : List App.Types.Alert
, recentChanges : RemoteData.WebData (List RecentChange)
, deleteTreesConfirm : Bool
, deleteError : Maybe String
, showUpdateLog : Maybe Int
, formUpdateLog : Form.Form () updateLogForm
}
type Msg
= NavigateTo Route
| GetTreesResult (RemoteData.WebData Trees)
| GetTreeResult (RemoteData.WebData Tree)
| GetTreeLogs String Bool
| GetTreeLogsResult (RemoteData.WebData TreeLogs)
| GetTreeLogsAllResult (RemoteData.WebData TreeLogs)
| GetRecentChangesResult (RemoteData.WebData (List RecentChange))
| FormAddTreeMsg Form.Msg
| FormAddTreeResult (WebData String)
| FormUpdateTreesMsg Form.Msg
| FormUpdateTreesResult (WebData String)
| SelectAllTrees
| SelectTree String
| UnselectAllTrees
| UnselectTree String
| DeleteTrees
| DeleteTreesConfirmToggle
| DeleteTreesResult (WebData String)
| RevertChange Int
| DiscardChange Int
| RecentChangeResult (WebData String)
| UpdateStackShow Int
| FormUpdateStackMsg Form.Msg
| FormUpdateStackResult (WebData String)
| UpdateLogShow Int
| FormUpdateLogMsg Form.Msg
| FormUpdateLogResult (WebData String)
possibleTreeStatuses : List ( String, String )
possibleTreeStatuses =
[ ( "open", "Open" )
, ( "approval required", "Approval required" )
, ( "closed", "Closed" )
]
possibleTreeTags : List ( String, String )
possibleTreeTags =
[ ( "checkin-compilation", "Check-in compilation failure" )
, ( "checkin-test", "Check-in test failure" )
, ( "infra", "Infrastructure related" )
, ( "backlog", "Job backlog" )
, ( "planned", "Planned closure" )
, ( "merges", "Merges" )
, ( "waiting-for-coverage", "Waiting for coverage" )
, ( "other", "Other" )
]

Просмотреть файл

@ -1,869 +0,0 @@
module App.TreeStatus.View exposing (..)
import App.Form
import App.TreeStatus.Form
import App.TreeStatus.Types
import App.UserScopes
import App.Utils
import Dict
import Form
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import RemoteData
import String
import TaskclusterLogin
import Utils
hasScope : String -> List String -> Bool
hasScope scope scopes =
App.UserScopes.hasScope scopes ("project:releng:treestatus/" ++ scope)
onClickGoTo : App.TreeStatus.Types.Route -> Attribute App.TreeStatus.Types.Msg
onClickGoTo route =
Utils.onClick (App.TreeStatus.Types.NavigateTo route)
bugzillaBugAsLink : String -> List (Html a)
bugzillaBugAsLink text_ =
let
words =
String.words text_
previousWords =
Nothing :: List.map Just words
in
List.map2 (\x y -> ( x, y )) previousWords words
|> List.filter (\( _, word ) -> word /= "Bug")
|> List.map
(\( previousWord, number ) ->
if previousWord == Just "Bug" then
a [ href ("https://bugzilla.mozilla.org/show_bug.cgi?id=" ++ number) ]
[ text ("Bug " ++ number) ]
else
text (" " ++ number ++ " ")
)
viewRecentChangeTree : String -> App.TreeStatus.Types.RecentChangeTree -> Html App.TreeStatus.Types.Msg
viewRecentChangeTree status tree =
li []
[ em [] [ text tree.tree ]
, span [] [ text " from " ]
, span
[ class ("badge badge-" ++ App.Utils.treeStatusLevel tree.last_state.status) ]
[ text tree.last_state.status ]
, span [] [ text " to " ]
, span
[ class ("badge badge-" ++ App.Utils.treeStatusLevel status) ]
[ text status ]
]
viewRecentChange :
List String
-> Maybe App.TreeStatus.Types.RecentChangeId
-> Form.Form () App.TreeStatus.Form.UpdateStack
-> App.TreeStatus.Types.RecentChange
-> List (Html App.TreeStatus.Types.Msg)
viewRecentChange scopes showUpdateStackForm formUpdateStack recentChange =
let
( reason, category ) =
recentChange.trees
|> List.head
|> Maybe.map
(\x ->
( x.last_state.current_reason
, x.last_state.current_tags
|> List.head
|> Maybe.withDefault ""
)
)
|> Maybe.withDefault ( "", "" )
possibleTreeTags =
Dict.fromList App.TreeStatus.Types.possibleTreeTags
parseTimestamp timestamp =
timestamp
|> String.split "T"
|> List.drop 1
|> List.take 1
|> List.append
(timestamp
|> String.split "T"
|> List.take 1
)
|> String.join " "
|> String.split "."
|> List.head
|> Maybe.withDefault timestamp
buttons =
if showUpdateStackForm == Just recentChange.id then
[]
else
[ div
[ class "btn-group" ]
[ button
[ type_ "button"
, class "btn btn-sm btn-outline-success"
, Utils.onClick (App.TreeStatus.Types.RevertChange recentChange.id)
]
[ text "Restore" ]
, button
[ type_ "button"
, class "btn btn-sm btn-outline-info"
, Utils.onClick (App.TreeStatus.Types.UpdateStackShow recentChange.id)
]
[ text "Update" ]
, button
[ type_ "button"
, class "btn btn-sm btn-outline-warning"
, Utils.onClick (App.TreeStatus.Types.DiscardChange recentChange.id)
]
[ text "Discard" ]
]
]
in
if hasScope "recent_changes/revert" scopes then
[ div
[ class "list-group-item justify-content-between" ]
([ div
[]
([ p
[]
[ text "At "
, em [] [ text (parseTimestamp recentChange.when) ]
, b [] [ text (" " ++ TaskclusterLogin.shortUsername recentChange.who) ]
, text " changed trees:"
]
, ul [] (List.map (viewRecentChangeTree recentChange.status) recentChange.trees)
]
|> App.Utils.appendItems
(if showUpdateStackForm == Just recentChange.id then
[ App.TreeStatus.Form.viewUpdateStack recentChange formUpdateStack
|> Html.map App.TreeStatus.Types.FormUpdateStackMsg
]
else
[ p []
[ div []
[ text "Reason category: "
, span [ class "badge badge-default" ]
[ text
(possibleTreeTags
|> Dict.get category
|> Maybe.withDefault category
)
]
]
, div [] [ text "Reason: ", b [] (bugzillaBugAsLink reason) ]
]
]
)
)
]
|> App.Utils.appendItems buttons
)
]
else
[]
viewRecentChanges :
List String
-> Maybe App.TreeStatus.Types.RecentChangeId
-> Form.Form () App.TreeStatus.Form.UpdateStack
-> RemoteData.WebData (List App.TreeStatus.Types.RecentChange)
-> List (Html App.TreeStatus.Types.Msg)
viewRecentChanges scopes showUpdateStackForm formUpdateStack recentChanges =
case recentChanges of
RemoteData.Success data ->
let
title =
if List.isEmpty data then
[]
else
[ h2 [] [ text "Recent Changes" ] ]
recentChanges =
data
|> List.map (viewRecentChange scopes showUpdateStackForm formUpdateStack)
|> List.concat
in
[ div
[ id "treestatus-recentchanges"
, class "list-group"
]
([]
|> App.Utils.appendItems title
|> App.Utils.appendItems recentChanges
)
]
_ ->
[]
viewTreesCategoryItem :
List String
-> List String
-> { a | reason : String, status : String, name : String, tags : List String }
-> Html App.TreeStatus.Types.Msg
viewTreesCategoryItem scopes treesSelected tree =
let
isChecked =
List.member tree.name treesSelected
checking checked =
case checked of
True ->
App.TreeStatus.Types.SelectTree tree.name
False ->
App.TreeStatus.Types.UnselectTree tree.name
openTree =
App.TreeStatus.Types.ShowTreeRoute tree.name
|> App.TreeStatus.Types.NavigateTo
treeTagClass =
"float-xs-right badge badge-" ++ App.Utils.treeStatusLevel tree.status
checkboxItem =
if hasScope "trees/update" scopes || hasScope "trees/delete" scopes then
[ label
[ class "custom-control custom-checkbox" ]
[ input
[ type_ "checkbox"
, class "custom-control-input"
, checked isChecked
, onCheck checking
]
[]
, span
[ class "custom-control-indicator" ]
[]
]
]
else
[]
itemClass =
if hasScope "trees/update" scopes || hasScope "trees/delete" scopes then
"list-group-item list-group-item-with-checkbox"
else
"list-group-item"
treeItem =
a
[ href "#"
, class "list-group-item-action"
, Utils.onClick openTree
]
([]
|> List.append
(if tree.reason == "" then
[]
else
[ p [ class "list-group-item-text" ]
[ text tree.reason ]
]
)
|> List.append
[ h5 [ class "list-group-item-heading" ]
[ text tree.name
, span [ style [ ( "margin-left", "1em" ) ] ]
(List.map
(\tag ->
span
[ class "badge badge-default" ]
[ App.TreeStatus.Types.possibleTreeTags
|> List.filterMap
(\( x, y ) ->
if x == tag then
Just y
else
Nothing
)
|> List.head
|> Maybe.withDefault tag
|> text
]
)
tree.tags
)
, span [ class treeTagClass ]
[ text tree.status ]
]
]
)
in
div [ class itemClass ]
(List.append checkboxItem [ treeItem ])
type Category
= Development
| ReleaseStabilization
| Try
| CommRepositories
| Other
categoryTitle : Category -> String
categoryTitle category =
case category of
Development ->
"Development"
ReleaseStabilization ->
"Release Stabilization"
Try ->
"Try"
CommRepositories ->
"Comm Repositories"
Other ->
"Other"
categorizeTrees :
App.TreeStatus.Types.Trees
-> List ( Category, App.TreeStatus.Types.Trees )
categorizeTrees trees =
let
( developmentTrees, developmentTreesOther ) =
List.partition
(\tree ->
List.member tree.name
[ "autoland"
, "mozilla-inbound"
, "mozilla-central"
]
)
trees
( releaseStabilizationTrees, releaseStabilizationTreesOther ) =
List.partition
(\tree ->
List.member tree.name
[ "mozilla-beta"
, "mozilla-release"
, "mozilla-esr68"
, "mozilla-esr60"
, "mozilla-esr52"
]
)
developmentTreesOther
( tryTrees, tryTreesOther ) =
List.partition
(\tree ->
List.member tree.name
[ "try"
, "try-comm-central"
]
)
releaseStabilizationTreesOther
( commRepositoriesTrees, otherTrees ) =
List.partition
(\tree ->
List.member tree.name
[ "comm-central-thunderbird"
, "comm-central-seamonkey"
, "comm-beta-thunderbird"
, "comm-beta-seamonkey"
, "comm-release-thunderbird"
, "comm-release-seamonkey"
, "comm-esr68-thunderbird"
, "comm-esr60-thunderbird"
, "comm-esr60-seamonkey"
, "comm-esr52-thunderbird"
, "comm-esr52-seamonkey"
]
)
tryTreesOther
in
[ ( Development, developmentTrees )
, ( ReleaseStabilization, releaseStabilizationTrees )
, ( Try, tryTrees )
, ( CommRepositories, commRepositoriesTrees )
, ( Other, otherTrees )
]
viewTreesCategory :
List String
-> List String
-> ( Category, App.TreeStatus.Types.Trees )
-> List (Html App.TreeStatus.Types.Msg)
viewTreesCategory scopes treesSelected ( category, trees ) =
if List.isEmpty trees then
[]
else
[ h4 [] [ text (categoryTitle category) ]
, div
[ id "treestatus-trees"
, class "list-group"
]
(trees
|> List.sortBy .name
|> List.map (viewTreesCategoryItem scopes treesSelected)
|> (\x ->
[ div
[ id "treestatus-trees"
, class "list-group"
]
x
]
)
)
]
viewTrees :
List String
-> RemoteData.WebData App.TreeStatus.Types.Trees
-> List String
-> List (Html App.TreeStatus.Types.Msg)
viewTrees scopes trees treesSelected =
case trees of
RemoteData.Success trees ->
categorizeTrees trees
|> List.map (viewTreesCategory scopes treesSelected)
|> List.concat
RemoteData.Failure message ->
[ App.Utils.error
(App.TreeStatus.Types.NavigateTo App.TreeStatus.Types.ShowTreesRoute)
(toString message)
]
RemoteData.Loading ->
[ App.Utils.loading ]
RemoteData.NotAsked ->
[]
viewButtons :
App.TreeStatus.Types.Route
-> List String
-> App.TreeStatus.Types.Model App.TreeStatus.Form.AddTree App.TreeStatus.Form.UpdateTree App.TreeStatus.Form.UpdateStack App.TreeStatus.Form.UpdateLog
-> Html App.TreeStatus.Types.Msg
viewButtons route scopes model =
let
allSelected =
List.length (RemoteData.withDefault [] model.trees)
== List.length model.treesSelected
appendIf condition button =
if condition then
App.Utils.appendItem button
else
List.append []
treeRoute =
case route of
App.TreeStatus.Types.ShowTreeRoute name ->
Just name
_ ->
Nothing
in
div
[ id "treestatus-trees-buttons"
, class "btn-group"
]
([]
|> appendIf
(route /= App.TreeStatus.Types.ShowTreesRoute)
(button
[ class "btn btn-outline-info btn-sm btn-pill"
, onClickGoTo App.TreeStatus.Types.ShowTreesRoute
]
[ text "Show All Trees" ]
)
|> appendIf
(route
== App.TreeStatus.Types.ShowTreesRoute
&& (hasScope "trees/delete" scopes || hasScope "trees/update" scopes)
)
(button
[ class "btn btn-outline-info btn-sm btn-pill"
, Utils.onClick
(if allSelected then
App.TreeStatus.Types.UnselectAllTrees
else
App.TreeStatus.Types.SelectAllTrees
)
]
[ text
(if allSelected then
"Unselect all trees"
else
"Select all trees"
)
]
)
|> appendIf
(route
== App.TreeStatus.Types.ShowTreesRoute
&& hasScope "trees/create" scopes
)
(button
[ class "btn btn-outline-success btn-sm btn-pill"
, onClickGoTo App.TreeStatus.Types.AddTreeRoute
]
[ text "Add Tree" ]
)
|> appendIf
((route == App.TreeStatus.Types.ShowTreesRoute || treeRoute /= Nothing)
&& hasScope "trees/update" scopes
)
(button
(if List.isEmpty model.treesSelected then
[ class "btn btn-outline-primary btn-sm btn-pill tooltip2"
, disabled (List.isEmpty model.treesSelected)
, title "You need to select some trees"
]
else
[ class "btn btn-outline-primary btn-sm btn-pill"
, onClickGoTo App.TreeStatus.Types.UpdateTreesRoute
]
)
[ text
("Update "
++ (if treeRoute == Nothing then
"Tree(s)"
else
"Tree"
)
)
]
)
|> appendIf
((route == App.TreeStatus.Types.ShowTreesRoute || treeRoute /= Nothing)
&& hasScope "trees/delete" scopes
)
(button
(if List.isEmpty model.treesSelected then
[ class "btn btn-outline-danger btn-sm btn-pill tooltip2"
, disabled (List.isEmpty model.treesSelected)
, title "You need to select some trees"
]
else
[ class "btn btn-outline-danger btn-sm btn-pill"
, onClickGoTo App.TreeStatus.Types.DeleteTreesRoute
]
)
[ text
("Delete "
++ (if treeRoute == Nothing then
"Tree(s)"
else
"Tree"
)
)
]
)
)
viewConfirmDelete :
Maybe String
-> Bool
-> List String
-> List (Html App.TreeStatus.Types.Msg)
viewConfirmDelete deleteError deleteTreesConfirm treesSelected =
[ div
[ id "treestatus-form" ]
[ div
[]
([]
|> App.Utils.appendItem
(text "You are about to delete the following trees:")
|> App.Utils.appendItem
(treesSelected
|> List.map (\x -> li [] [ text x ])
|> ul []
)
)
, hr [] []
, div
[ class ("form-group " ++ App.Form.errorClass deleteError) ]
([]
|> App.Utils.appendItem
(label
[ class "custom-control custom-checkbox" ]
[ input
[ type_ "checkbox"
, class "custom-control-input"
, checked deleteTreesConfirm
, onCheck (\x -> App.TreeStatus.Types.DeleteTreesConfirmToggle)
]
[]
, span
[ class "custom-control-indicator" ]
[]
, span
[ class "custom-control-description" ]
[ text "I acknowledge this action is irreversible." ]
]
)
)
, hr [] []
, button
[ class "btn btn-outline-danger"
, Utils.onClick App.TreeStatus.Types.DeleteTrees
]
[ text "Delete" ]
, div [ class "clearfix" ] []
]
]
viewTreesTitle : App.TreeStatus.Types.Route -> Html msg
viewTreesTitle route =
case route of
App.TreeStatus.Types.ShowTreesRoute ->
h2 [ class "float-xs-left" ] [ text "Trees" ]
App.TreeStatus.Types.AddTreeRoute ->
h2 [ class "float-xs-left" ] [ text "Add Tree" ]
App.TreeStatus.Types.UpdateTreesRoute ->
h2 [ class "float-xs-left" ] [ text "Update Tree(s)" ]
App.TreeStatus.Types.DeleteTreesRoute ->
h2 [ class "float-xs-left" ] [ text "Delete Tree(s)" ]
App.TreeStatus.Types.ShowTreeRoute name ->
h2 [ class "float-xs-left" ] [ text ("Tree: " ++ name) ]
treeRulesLink : String -> String
treeRulesLink treeName =
case treeName of
"autoland" ->
"https://wiki.mozilla.org/Tree_Rules#autoland.2FLando"
"mozilla-inbound" ->
"https://wiki.mozilla.org/Tree_Rules#mozilla-inbound"
"mozilla-central" ->
"https://wiki.mozilla.org/Tree_Rules#mozilla-central_.28Nightly_channel.29"
"mozilla-beta" ->
"https://wiki.mozilla.org/Tree_Rules#mozilla-beta"
"mozilla-release" ->
"https://wiki.mozilla.org/Tree_Rules#mozilla-release"
_ ->
if String.startsWith "mozilla-esr" treeName then
"https://wiki.mozilla.org/Release_Management/ESR_Landing_Process"
else if String.startsWith "comm-" treeName then
"https://wiki.mozilla.org/Tree_Rules/comm-central"
else
"https://wiki.mozilla.org/Tree_Rules"
viewTreeDetails :
RemoteData.RemoteData a { b | message_of_the_day : String, name : String, status : String }
-> Html App.TreeStatus.Types.Msg
viewTreeDetails remote =
case remote of
RemoteData.Success tree ->
div
[ id "treestatus-tree-details" ]
[ span
[]
[ text tree.name ]
, text " status is "
, span
[ class ("badge badge-" ++ App.Utils.treeStatusLevel tree.status) ]
[ text tree.status ]
, p [ class "lead" ] (bugzillaBugAsLink tree.message_of_the_day)
, p [] [ a [ href (treeRulesLink tree.name) ] [ text "Tree rules" ] ]
]
RemoteData.Failure message ->
App.Utils.error (App.TreeStatus.Types.NavigateTo App.TreeStatus.Types.ShowTreesRoute) (toString message)
RemoteData.Loading ->
App.Utils.loading
RemoteData.NotAsked ->
text ""
viewTreeLog :
List String
-> Maybe Int
-> Form.Form () App.TreeStatus.Form.UpdateLog
-> App.TreeStatus.Types.TreeLog
-> Html App.TreeStatus.Types.Msg
viewTreeLog scopes showUpdateLog formUpdateLog log =
let
who2 =
if String.startsWith "human:" log.who then
log.who
|> String.dropLeft 6
else
log.who
who =
who2
|> String.split "@"
|> List.head
|> Maybe.withDefault who2
possibleTreeTags =
Dict.fromList App.TreeStatus.Types.possibleTreeTags
category =
log.tags
|> List.head
|> Maybe.map
(\tag ->
possibleTreeTags
|> Dict.get tag
|> Maybe.withDefault tag
)
|> Maybe.withDefault ""
view =
[ p []
[ div []
[ text "Reason category: "
, span [ class "badge badge-default" ] [ text category ]
]
, div [] [ text "Reason: ", b [] (bugzillaBugAsLink log.reason) ]
]
, if hasScope "trees/update" scopes then
button
[ type_ "button"
, class "btn btn-sm btn-outline-info"
, style [ ( "float", "left" ) ]
, Utils.onClick (App.TreeStatus.Types.UpdateLogShow log.id)
]
[ text "Update" ]
else
span [] []
, div [ class "clearfix" ] []
]
form =
[ App.TreeStatus.Form.viewUpdateLog log.status formUpdateLog
|> Html.map App.TreeStatus.Types.FormUpdateLogMsg
]
in
div [ class "timeline-item" ]
--TODO: show status in hover of the badge
[ div [ class <| "timeline-badge badge-" ++ App.Utils.treeStatusLevel log.status ]
[ text " " ]
, div [ class "timeline-panel" ]
[ div [ class "timeline-time" ]
[ text log.when ]
, h5 [] [ text who ]
, div []
(if showUpdateLog == Just log.id then
form
else
view
)
]
]
viewTreeLogs :
List String
-> String
-> RemoteData.WebData App.TreeStatus.Types.TreeLogs
-> RemoteData.WebData App.TreeStatus.Types.TreeLogs
-> Maybe Int
-> Form.Form () App.TreeStatus.Form.UpdateLog
-> Html App.TreeStatus.Types.Msg
viewTreeLogs scopes name treeLogs_ treeLogsAll_ showUpdateLog formUpdateLog =
let
( moreButton, treeLogsAll ) =
case treeLogsAll_ of
RemoteData.Success treeLogs ->
( []
, List.drop 5 treeLogs
)
RemoteData.Failure message ->
( [ App.Utils.error (App.TreeStatus.Types.NavigateTo App.TreeStatus.Types.ShowTreesRoute) (toString message) ]
, []
)
RemoteData.Loading ->
( [ button
[ class "btn btn-secondary"
, Utils.onClick <| App.TreeStatus.Types.GetTreeLogs name True
]
[ text "Loading"
, i [ class "fa fa-circle-o-notch fa-spin" ] []
]
]
, []
)
RemoteData.NotAsked ->
( [ button
[ class "btn btn-secondary"
, Utils.onClick <| App.TreeStatus.Types.GetTreeLogs name True
]
[ text "Load more" ]
]
, []
)
in
case treeLogs_ of
RemoteData.Success treeLogs ->
div [ class "timeline" ]
(List.append
(List.append
(List.map (viewTreeLog scopes showUpdateLog formUpdateLog) treeLogs)
(List.map (viewTreeLog scopes showUpdateLog formUpdateLog) treeLogsAll)
)
[ div [ class "timeline-item timeline-more" ]
[ div [ class "timeline-panel" ] moreButton ]
]
)
RemoteData.Failure message ->
App.Utils.error (App.TreeStatus.Types.NavigateTo App.TreeStatus.Types.ShowTreesRoute) (toString message)
RemoteData.Loading ->
App.Utils.loading
RemoteData.NotAsked ->
text ""
viewTree :
List String
-> RemoteData.WebData App.TreeStatus.Types.Tree
-> RemoteData.WebData App.TreeStatus.Types.TreeLogs
-> RemoteData.WebData App.TreeStatus.Types.TreeLogs
-> Maybe Int
-> Form.Form () App.TreeStatus.Form.UpdateLog
-> String
-> List (Html App.TreeStatus.Types.Msg)
viewTree scopes tree treeLogs treeLogsAll showUpdateLog formUpdateLog name =
[ div
[ id "treestatus-form" ]
[ viewTreeDetails tree
, hr [] []
, viewTreeLogs scopes name treeLogs treeLogsAll showUpdateLog formUpdateLog
]
]

Просмотреть файл

@ -1,32 +0,0 @@
module App.Types exposing (..)
import UrlParser
type alias Page a b =
{ title : String
, description : String
, matcher : UrlParser.Parser (a -> b) b
}
type alias ResponseError =
{ type_ : String
, detail : String
, status : Int
, title : String
}
type AlertType
= AlertSuccess
| AlertInfo
| AlertWarning
| AlertDanger
type alias Alert =
{ type_ : AlertType
, title : String
, text : String
}

Просмотреть файл

@ -1,112 +0,0 @@
module App.UserScopes exposing (..)
import Hawk
import Http
import Json.Decode as JsonDecode
import RemoteData exposing (WebData)
import String
import Task
import Time
import Utils
type alias Model =
{ timestamp : Time.Time
, scopes : List String
}
type Msg
= FetchScopes
| CacheScopes Time.Time
| FetchedScopes (WebData String)
decoderScopes : JsonDecode.Decoder (List String)
decoderScopes =
JsonDecode.at [ "scopes" ] (JsonDecode.list JsonDecode.string)
init : Model
init =
{ timestamp = 0.0
, scopes = []
}
update :
Msg
-> Model
-> ( Model, Cmd Msg, Maybe Hawk.Request )
update msg model =
case msg of
FetchScopes ->
( model
, Task.perform CacheScopes Time.now
, Nothing
)
CacheScopes currentTime ->
let
headers =
[ Http.header "Accept" "application/json" ]
url =
"https://auth.taskcluster.net/v1/scopes/current"
request =
Hawk.Request "FetchedScopes" "GET" url headers Http.emptyBody
( newModel, hawkCmd ) =
if (model.timestamp + 15000) > currentTime then
( model, Nothing )
else
( { model | scopes = [] }
, Just request
)
in
( newModel, Cmd.none, hawkCmd )
FetchedScopes result ->
let
scopes =
Utils.decodeJsonString decoderScopes result
|> RemoteData.withDefault []
in
( { model | scopes = scopes }
, Cmd.none
, Nothing
)
hawkResponse :
Cmd (WebData String)
-> String
-> Cmd Msg
hawkResponse response route =
case route of
"FetchedScopes" ->
Cmd.map FetchedScopes response
_ ->
Cmd.none
hasScope : List String -> String -> Bool
hasScope existing scope =
existing
|> List.map
(\x ->
if String.endsWith "*" x then
String.startsWith (String.dropRight 1 x) scope
else
scope == x
)
|> List.any Basics.identity
hasScopes : List String -> List String -> Bool
hasScopes existing scopes =
scopes
|> List.map (hasScope existing)
|> List.all Basics.identity

Просмотреть файл

@ -1,204 +0,0 @@
module App.Utils exposing (..)
import App.Types
import Html exposing (..)
import Html.Attributes exposing (..)
import Http
import Json.Decode as JsonDecode
import RemoteData exposing (RemoteData(..), WebData)
import Utils
import VirtualDom
dropdown :
(String -> a)
-> List { b | name : String }
-> Maybe String
-> Html a
dropdown event items selected =
div [ class "btn-group btn-dropdown" ]
[ span
[ type_ "button"
, class "btn btn-secondary dropdown-toggle"
, attribute "data-toggle" "dropdown"
, attribute "aria-haspopup" "true"
, attribute "aria-expanded" "false"
]
[ text <| Maybe.withDefault "Select a value..." selected
]
, span
[ type_ "button"
, class "btn btn-secondary dropdown-toggle"
, attribute "data-toggle" "dropdown"
, attribute "aria-haspopup" "true"
, attribute "aria-expanded" "false"
]
[ span [ class "sr-only" ] [ text "Toggle Dropdown" ]
]
, div [ class "dropdown-menu" ]
(List.map
(\x ->
a
[ Utils.onClick (event x.name)
, class "dropdown-item"
]
[ text x.name ]
)
items
)
]
loading : VirtualDom.Node a
loading =
div
[ class "progress"
]
[ div
[ class "progress-bar progress-bar-striped progress-bar-animated"
, attribute "role" "progressbar"
, attribute "aria-valuenow" "100"
, attribute "aria-valuemin" "0"
, attribute "aria-valuemax" "100"
, style [ ( "width", "100%" ) ]
]
[ text "Loading ..." ]
]
error : a -> String -> Html a
error event message =
div
[ class "alert alert-danger"
, attribute "role" "alert"
]
[ i [ class "fa fa-exclamation-triangle" ] []
, strong [] [ text " Error: " ]
, span [] [ text message ]
, a
[ Utils.onClick event
, class "alert-link"
]
[ text " Click to retry." ]
]
success : a -> String -> Html a
success event message =
div [ class "alert alert-success", attribute "role" "alert" ]
[ i [ class "fa fa-check" ] []
, strong [] [ text " Success: " ]
, span [] [ text message ]
, a
[ Utils.onClick event
, class "alert-link"
]
[ text " Click to remove." ]
]
getAlerts : WebData String -> List App.Types.Alert
getAlerts response =
let
decoderError =
JsonDecode.map4 App.Types.ResponseError
(JsonDecode.field "type" JsonDecode.string)
(JsonDecode.field "detail" JsonDecode.string)
(JsonDecode.field "status" JsonDecode.int)
(JsonDecode.field "title" JsonDecode.string)
handleError error =
[ App.Types.Alert
App.Types.AlertDanger
"Error!"
(case error of
Http.BadUrl url ->
"Bad Url: " ++ url
Http.Timeout ->
"Request Timeout"
Http.NetworkError ->
"A network error occured"
Http.BadPayload details response ->
"Bad payload: " ++ details
Http.BadStatus response ->
case JsonDecode.decodeString decoderError response.body of
Ok obj ->
obj.detail
Err error ->
error
)
]
in
case response of
Success r ->
[]
Loading ->
[]
NotAsked ->
[]
Failure e ->
handleError e
viewAlerts :
List App.Types.Alert
-> Html a
viewAlerts alerts =
let
getAlertTypeAsString alert =
case alert.type_ of
App.Types.AlertSuccess ->
"success"
App.Types.AlertInfo ->
"info"
App.Types.AlertWarning ->
"warning"
App.Types.AlertDanger ->
"danger"
createAlert alert =
div [ class ("alert alert-" ++ getAlertTypeAsString alert) ]
[ strong [] [ text alert.title ]
, text alert.text
]
in
alerts
|> List.map createAlert
|> div []
appendItem : a -> List a -> List a
appendItem item items =
List.append items [ item ]
appendItems : List a -> List a -> List a
appendItems items1 items2 =
List.append items2 items1
treeStatusLevel : String -> String
treeStatusLevel status =
case status of
"closed" ->
"danger"
"open" ->
"success"
"approval required" ->
"warning"
_ ->
"default"

Просмотреть файл

@ -1 +0,0 @@
../../../lib/frontend_common/Hawk.elm

Просмотреть файл

@ -1,305 +0,0 @@
module Main exposing (..)
import App
import App.Home
import App.Layout
import App.Tokens
import App.ToolTool
import App.TreeStatus
import App.TreeStatus.Api
import App.TreeStatus.Types
import App.UserScopes
import Hawk
import Html exposing (..)
import Navigation
import String
import TaskclusterLogin
import Utils
main : Program App.Flags App.Model App.Msg
main =
Navigation.programWithFlags App.UrlChange
{ init = init
, view = App.Layout.view viewRoute
, update = update
, subscriptions = subscriptions
}
init : App.Flags -> Navigation.Location -> ( App.Model, Cmd App.Msg )
init flags location =
let
route =
App.parseLocation location
( user, userCmd ) =
TaskclusterLogin.init flags.treestatusUrl flags.auth0
model =
{ history = [ location ]
, route = route
, docsUrl = flags.docsUrl
, version = flags.version
, user = user
, userScopes = App.UserScopes.init
, tokens = App.Tokens.init
, tooltool = App.ToolTool.init
, treestatus = App.TreeStatus.init flags.treestatusUrl
}
( model_, appCmd ) =
initRoute model route
in
( model_
, Cmd.batch
[ appCmd
, Cmd.map App.TaskclusterLoginMsg userCmd
]
)
initRoute : App.Model -> App.Route -> ( App.Model, Cmd App.Msg )
initRoute model route =
case route of
App.NotFoundRoute ->
model ! []
App.HomeRoute ->
{ model
| tokens = App.Tokens.init
, tooltool = App.ToolTool.init
, treestatus =
App.TreeStatus.init model.treestatus.baseUrl
}
! [ Utils.performMsg (App.UserScopesMsg App.UserScopes.FetchScopes) ]
App.LoginRoute code state ->
let
loginCmd =
case TaskclusterLogin.convertUrlParametersToCode code state of
Just code_ ->
TaskclusterLogin.Logging code_
|> App.TaskclusterLoginMsg
|> Utils.performMsg
Nothing ->
Cmd.none
in
model
! [ loginCmd
, App.navigateTo App.HomeRoute
]
App.LogoutRoute ->
model
! [ Utils.performMsg (App.TaskclusterLoginMsg TaskclusterLogin.Logout)
-- TODO: we should be redirecting to the url that we were loging in from
, Utils.performMsg (App.NavigateTo App.HomeRoute)
]
App.TokensRoute ->
model ! []
App.ToolToolRoute ->
model ! []
App.TreeStatusRoute route ->
model
! [ Utils.performMsg (App.TreeStatusMsg (App.TreeStatus.Types.NavigateTo route))
, Utils.performMsg (App.UserScopesMsg App.UserScopes.FetchScopes)
]
update : App.Msg -> App.Model -> ( App.Model, Cmd App.Msg )
update msg model =
case msg of
--
-- ROUTING
--
App.UrlChange location ->
{ model
| history = location :: model.history
, route = App.parseLocation location
}
! []
App.NavigateTo route ->
let
( newModel, newCmd ) =
initRoute model route
in
( newModel
, Cmd.batch
[ App.navigateTo route
, newCmd
]
)
--
-- LOGIN / LOGOUT
--
App.TaskclusterLoginMsg userMsg ->
let
( newUser, userCmd ) =
TaskclusterLogin.update userMsg model.user
fetchScopes =
case userMsg of
TaskclusterLogin.LoadedTaskclusterCredentials response ->
[ Utils.performMsg (App.UserScopesMsg App.UserScopes.FetchScopes) ]
_ ->
[]
in
( { model | user = newUser }
, [ Cmd.map App.TaskclusterLoginMsg userCmd ]
|> List.append fetchScopes
|> Cmd.batch
)
--
-- HAWK REQUESTS
--
App.HawkMsg hawkMsg ->
let
( requestId, cmd, response ) =
Hawk.update hawkMsg
routeHawkMsg route =
if String.startsWith "TreeStatus" route then
route
|> String.dropLeft (String.length "TreeStatus")
|> App.TreeStatus.Api.hawkResponse response
|> Cmd.map App.TreeStatusMsg
else if String.startsWith "UserScopes" route then
route
|> String.dropLeft (String.length "UserScopes")
|> App.UserScopes.hawkResponse response
|> Cmd.map App.UserScopesMsg
else
Cmd.none
appCmd =
requestId
|> Maybe.map routeHawkMsg
|> Maybe.withDefault Cmd.none
in
( model
, Cmd.batch
[ Cmd.map App.HawkMsg cmd
, appCmd
]
)
App.UserScopesMsg msg_ ->
let
( newModel, newCmd, hawkCmd ) =
App.UserScopes.update msg_ model.userScopes
in
( { model | userScopes = newModel }
, hawkCmd
|> Maybe.map (\req -> [ hawkSend model.user "UserScopes" req ])
|> Maybe.withDefault []
|> List.append [ Cmd.map App.UserScopesMsg newCmd ]
|> Cmd.batch
)
App.TokensMsg msg_ ->
let
( newModel, newCmd ) =
App.Tokens.update msg_ model.tokens
in
( { model | tokens = newModel }
, Cmd.map App.TokensMsg newCmd
)
App.ToolToolMsg msg_ ->
let
( newModel, newCmd ) =
App.ToolTool.update msg_ model.tooltool
in
( { model | tooltool = newModel }
, Cmd.map App.ToolToolMsg newCmd
)
App.TreeStatusMsg msg_ ->
let
route =
case model.route of
App.TreeStatusRoute x ->
x
_ ->
App.TreeStatus.Types.ShowTreesRoute
( newModel, newCmd, hawkCmd ) =
App.TreeStatus.update route msg_ model.treestatus
in
( { model | treestatus = newModel }
, hawkCmd
|> Maybe.map (\req -> [ hawkSend model.user "TreeStatus" req ])
|> Maybe.withDefault []
|> List.append [ Cmd.map App.TreeStatusMsg newCmd ]
|> Cmd.batch
)
hawkSend :
TaskclusterLogin.Model
-> String
-> Hawk.Request
-> Cmd App.Msg
hawkSend user page request =
let
pagedRequest =
{ request | id = page ++ request.id }
in
case user.credentials of
Just credentials ->
Hawk.send pagedRequest credentials
|> Cmd.map App.HawkMsg
Nothing ->
Cmd.none
viewRoute : App.Model -> Html App.Msg
viewRoute model =
case model.route of
App.NotFoundRoute ->
App.Layout.viewNotFound model
App.HomeRoute ->
App.Home.view model
App.LoginRoute _ _ ->
-- TODO: this should be already a view on TaskclusterLogin
text "Logging you in ..."
App.LogoutRoute ->
-- TODO: this should be already a view on TaskclusterLogin
text "Logging you out ..."
App.TokensRoute ->
Html.map App.TokensMsg (App.Tokens.view model.tokens)
App.ToolToolRoute ->
Html.map App.ToolToolMsg (App.ToolTool.view model.tooltool)
App.TreeStatusRoute route ->
App.TreeStatus.view
route
model.user
model.userScopes
model.treestatus
|> Html.map App.TreeStatusMsg
subscriptions : App.Model -> Sub App.Msg
subscriptions model =
Sub.batch
[ TaskclusterLogin.subscriptions App.TaskclusterLoginMsg
, Hawk.subscriptions App.HawkMsg
]

Просмотреть файл

@ -1 +0,0 @@
../../../lib/frontend_common/Redirect.elm

Просмотреть файл

@ -1 +0,0 @@
../../../lib/frontend_common/TaskclusterLogin.elm

Просмотреть файл

@ -1 +0,0 @@
../../../lib/frontend_common/Title.elm

Просмотреть файл

@ -1 +0,0 @@
../../../lib/frontend_common/Utils.elm

Просмотреть файл

@ -1 +0,0 @@
../../../lib/frontend_common/hawk.js

Просмотреть файл

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<meta name="description" content="Collection of Mozilla Release Engineering Services" />
<meta name="author" content="Mozilla RelEng Team" />
<title>Mozilla RelEng Services</title>
</head>
<body>
</body>
</html>

Просмотреть файл

@ -1,66 +0,0 @@
'use strict';
require('expose-loader?jQuery!jquery');
require('expose-loader?Tether!tether');
require('bootstrap');
require('./scss/index.scss');
var url;
var getData = function(name, _default) {
url = document.body.getAttribute('data-' + name);
if (url === null) {
url = _default;
}
if (url === undefined) {
throw Error('You need to set `data-' + name + '`');
}
return url;
};
var title = require('./title');
var redirect = require('./redirect');
var localstorage = require('./localstorage');
var hawk = require('./hawk');
var release_version = getData('release-version', process.env.RELEASE_VERSION)
var release_channel = getData('release-channel', process.env.RELEASE_CHANNEL);
var AUTH_KEY = 'auth'; // do not change this key
var init = function() {
// Start the ELM application
var app = require('./Main.elm').Main.fullscreen({
auth0: localstorage.load_item(AUTH_KEY),
treestatusUrl: getData('treestatus-api-url', process.env.TREESTATUS_API_URL),
docsUrl: getData('docs-url', process.env.DOCS_URL),
version: release_version,
});
// Setup ports
localstorage.init(app, AUTH_KEY);
hawk(app);
redirect(app);
title(app);
};
// Setup logging
var Raven = require('raven-js');
var sentry_dsn = document.body.getAttribute('data-sentry-dsn');
if (sentry_dsn != null) {
Raven
.config(
sentry_dsn,
{
debug: true,
release: release_version,
environment: release_channel,
tags: {
server_name: 'mozilla/release-services',
site: 'releng-frontend'
}
})
.install()
.context(init);
} else {
init();
}

Просмотреть файл

@ -1 +0,0 @@
../../../lib/frontend_common/localstorage.js

Просмотреть файл

@ -1 +0,0 @@
../../../lib/frontend_common/redirect.js

Просмотреть файл

@ -1 +0,0 @@
../../../../lib/frontend_common/scss/fira

Просмотреть файл

@ -1 +0,0 @@
../../../../lib/frontend_common/scss/font-awesome/

Просмотреть файл

@ -1 +0,0 @@
../../../../lib/frontend_common/scss/fonts.scss

Просмотреть файл

@ -1,430 +0,0 @@
// Mozilla colors
// https://github.com/mozilla/mozmaker/blob/master/src/scss/custom/_colors.scss
$charcoal: #4d4f53;
$white: #fff;
$black: #000;
$dino-red: #d24735;
$firefox-orange: #f26c23;
$market-orange: #f89c24;
$flame-yellow: #ffcd02;
$gecko-green: #7dc14c;
$summit-teal: #3bba99;
$mobile-blue: #16afe5;
$developer-blue: #4383bf;
$nightly-blue: #5a6ba4;
$aurora-purple: #ab5da4;
$bikeshed-magenta: #e14164;
$border-color: #adadad;
// Mozilla fonts
$font-family-sans-serif: 'Fira Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
$font-family-serif: 'Fira Sans', Georgia, 'Times New Roman', Times, serif;
$font-family-monospace: 'Fira Sans', Menlo, Monaco, Consolas, 'Courier New', monospace;
$font-family-base: 'Fira Sans', $font-family-sans-serif;
$font-size-root: 16px;
$headings-font-weight: 300;
// Fonts
@import url(./font-awesome/font-awesome.css);
@import url(./fira/fira.css);
// Bootstrap
@import "~bootstrap/scss/bootstrap";
// navigation bar
#navbar {
background-color: $charcoal;
font-size: 14px;
padding: 1rem;
.navbar-toggler {
color: $white;
font-size: 14px;
border: 0;
padding: 0;
float: right;
text-align: right;
}
.navbar-toggler-icon {
color: $white;
}
#navbarNavDropdown {
justify-content: flex-end;
}
.nav-item a.nav-link {
color: darken($white, 20%);
padding: 0;
}
.nav-item.active a.nav-link {
color: $white !important;
}
.navbar-brand {
color: $white;
font-size: 14px;
font-weight: 600;
letter-spacing: 1px;
padding: 0 1em 0 0;
text-transform: uppercase;
float: left;
}
.navbar-nav {
flex-direction: row-reverse;
width: 100%;
}
.dropdown-menu-right {
right: 0.1em;
}
.dropdown-menu {
border-radius: 0;
top: 120%;
}
}
#banner-empty {
background-color: $developer-blue;
height: 10px;
}
#banner {
background-color: $developer-blue;
min-height: 200px;
color: $white;
.container {
font-size: 34px;
padding: 73px 0;
margin: 0 auto;
text-align: center;
line-height: 54px;
}
}
#content {
.container {
margin: 1.25em auto 0 auto;
}
}
footer {
color: $charcoal;
font-size: 12px;
line-height: 1.6em;
margin: 2em 0;
text-align: center;
ul li {
display: inline-block;
margin: 0 1em;
}
ul {
list-style: none;
margin: 0;
padding: 0;
}
div a {
color: $charcoal;
border-bottom: 1px dotted $charcoal;
&:hover {
text-decoration: none;
border-bottom: 1px dotted $charcoal;
}
}
}
.btn-dropdown {
width: 100%;
display: flex;
.btn:nth-child(1) {
width: 100%;
text-align: left;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
&.open .btn:nth-child(1) {
border-bottom-left-radius: 0;
}
&.open .btn, &.open .btn:hover, .btn:hover {
background-color: $white;
border-color: $border-color;
}
.btn:nth-child(1)::after {
display: none;
}
.btn:nth-child(2) {
order: 1;
}
&.open .btn:nth-child(2) {
border-bottom-right-radius: 0;
}
.dropdown-menu {
margin-top: 0;
width: 100%;
border-color: $border-color;
border-top: 0;
border-top-left-radius: 0;
border-top-right-radius: 0;
& > a {
padding-left: 16px;
padding-right: 16px;
};
}
}
.clobberer-submit-description {
font-size: 0.8em;
margin: 1em 0;
ul {
margin: 0.5em 0 1rem 0;
border: 1px solid $border-color;
li {
margin: 0.5em 0;
}
}
}
#page-home .card {
margin-bottom: 1.25em;
&:hover {
text-decoration: none;
cursor: pointer;
}
.card-title {
color: #333;
font-weight: bold;
}
.card-text {
color: #333;
}
}
#treestatus table tr:hover {
cursor: pointer;
}
.timeline {
position: relative;
padding: 2em 0;
margin-top: 2em;
margin-bottom: 2em;
margin-left: 1em;
&::before {
/* this is the vertical line */
content: '';
position: absolute;
top: 0;
left: 9px;
height: 100%;
width: 2px;
background: $border-color;
}
}
.timeline-item {
position: relative;
margin: 2em 0;
&::first-child {
margin-top: 0;
}
&::last-child {
margin-bottom: 0;
}
&::before {
content: "";
display: table;
clear: both;
}
}
.timeline-badge {
position: absolute;
top: 0;
left: 0;
width: 20px;
height: 20px;
border-radius: 50%;
box-shadow: 0 0 0 4px white;
}
.timeline-panel {
position: relative;
margin-left: 2em;
.badge { margin-right: 0.5em; }
}
.timeline-time {
font-weight: bold;
}
.timeline-more {
margin-bottom: -1em;
}
.float-xs-right {
float: right;
}
.tooltip2 {
position: relative;
}
.tooltip2:hover:after{
background: rgba(0,0,0,.8);
border-radius: 4px;
bottom: -2.5em;
color: #fff;
content: attr(title);
right: 0;
padding: 0.5em;
position: absolute;
z-index: 98;
}
#page-treestatus .radio-inline {
margin-right: 1em;
}
#page-treestatus .radio-inline > input {
margin-right: 0.5em;
}
#page-treestatus h2 {
margin-top: 1em;
}
#page-treestatus h4 {
margin-top: 2em;
}
#treestatus-trees {
clear: both;
}
#treestatus-trees .list-group-item {
position: relative;
}
#treestatus-trees .list-group-item-action:hover {
background: transparent;
}
#treestatus-trees .list-group-item h5 {
margin: 0;
}
#treestatus-trees .list-group-item-with-checkbox {
padding-left: 3em;
}
#treestatus-trees .custom-checkbox {
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 1em;
height: 1em;
margin: 0;
padding: 0;
}
#treestatus-trees .custom-checkbox input {
margin: 0;
padding: 0;
height: 1.6em;
width: 1.6em;
margin-left: -0.3em;
}
#treestatus-trees-buttons {
float: right;
margin-top: 0.3em;
margin-left: 1em;
margin-bottom: 0.5em;
}
#treestatus-recentchanges {
margin-bottom: 2em;
}
#treestatus-recentchanges .btn-group {
margin-left: 0.5em;
margin-bottom: 0.5em;
}
#treestatus-form {
padding: 2em 1em 1em 1em;
border: 1px solid #ddd;
border-radius: 0.25rem;
clear: both;
}
#treestatus-form button {
float: right;
}
#treestatus-form label {
font-weight: bold;
}
#treestatus-form label.custom-checkbox {
font-weight: normal;
margin: 0 1em 0 0;
}
#treestatus-form small p {
margin: 0;
}
#treestatus-tree-details {
text-align: center;
}
#treestatus-tree-details span:first-child {
font-weight: bold;
}
#treestatus-tree-details p {
margin-top: 1em;
}
@include media-breakpoint-down(xs) {
.container {
margin: 0em;
}
#navbar .navbar-brand {
float: none;
margin: 0 0 1em 0;
padding: 0;
width: 100%;
text-align: center;
};
#navbar .dropdown-menu {
background: transparent;
border: 0;
}
#navbar .dropdown-menu a {
color: #ccc;
text-align: center;
}
#navbar .dropdown-menu a:hover {
color: #292b2c;
}
#navbar .nav-item {
width: 100%;
text-align: center;
}
}

Просмотреть файл

@ -1,137 +0,0 @@
.alertify,
.alertify-show,
.alertify-log {
-webkit-transition: all 500ms cubic-bezier(0.175, 0.885, 0.320, 1.275);
-moz-transition: all 500ms cubic-bezier(0.175, 0.885, 0.320, 1.275);
-ms-transition: all 500ms cubic-bezier(0.175, 0.885, 0.320, 1.275);
-o-transition: all 500ms cubic-bezier(0.175, 0.885, 0.320, 1.275);
transition: all 500ms cubic-bezier(0.175, 0.885, 0.320, 1.275); /* easeOutBack */
}
.alertify-hide {
-webkit-transition: all 250ms cubic-bezier(0.600, -0.280, 0.735, 0.045);
-moz-transition: all 250ms cubic-bezier(0.600, -0.280, 0.735, 0.045);
-ms-transition: all 250ms cubic-bezier(0.600, -0.280, 0.735, 0.045);
-o-transition: all 250ms cubic-bezier(0.600, -0.280, 0.735, 0.045);
transition: all 250ms cubic-bezier(0.600, -0.280, 0.735, 0.045); /* easeInBack */
}
.alertify-log-hide {
-webkit-transition: all 500ms cubic-bezier(0.600, -0.280, 0.735, 0.045);
-moz-transition: all 500ms cubic-bezier(0.600, -0.280, 0.735, 0.045);
-ms-transition: all 500ms cubic-bezier(0.600, -0.280, 0.735, 0.045);
-o-transition: all 500ms cubic-bezier(0.600, -0.280, 0.735, 0.045);
transition: all 500ms cubic-bezier(0.600, -0.280, 0.735, 0.045); /* easeInBack */
}
.alertify-cover {
position: fixed; z-index: 99999;
top: 0; right: 0; bottom: 0; left: 0;
background-color:white;
filter:alpha(opacity=0);
opacity:0;
}
.alertify-cover-hidden {
display: none;
}
.alertify {
position: fixed; z-index: 99999;
top: 50px; left: 50%;
width: 550px;
margin-left: -275px;
opacity: 1;
}
.alertify-hidden {
-webkit-transform: translate(0,-150px);
-moz-transform: translate(0,-150px);
-ms-transform: translate(0,-150px);
-o-transform: translate(0,-150px);
transform: translate(0,-150px);
opacity: 0;
display: none;
}
/* overwrite display: none; for everything except IE6-8 */
:root *> .alertify-hidden {
display: block;
visibility: hidden;
}
.alertify-logs {
position: fixed;
z-index: 5000;
bottom: 10px;
right: 10px;
width: 300px;
}
.alertify-logs-hidden {
display: none;
}
.alertify-log {
display: block;
margin-top: 10px;
position: relative;
right: -300px;
opacity: 0;
}
.alertify-log-show {
right: 0;
opacity: 1;
}
.alertify-log-hide {
-webkit-transform: translate(300px, 0);
-moz-transform: translate(300px, 0);
-ms-transform: translate(300px, 0);
-o-transform: translate(300px, 0);
transform: translate(300px, 0);
opacity: 0;
}
.alertify-dialog {
padding: 25px;
}
.alertify-resetFocus {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
.alertify-inner {
text-align: center;
}
.alertify-text {
margin-bottom: 15px;
width: 100%;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
font-size: 100%;
}
.alertify-buttons {
}
.alertify-button,
.alertify-button:hover,
.alertify-button:active,
.alertify-button:visited {
background: none;
text-decoration: none;
border: none;
/* line-height and font-size for input button */
line-height: 1.5;
font-size: 100%;
display: inline-block;
cursor: pointer;
margin-left: 5px;
}
@media only screen and (max-width: 680px) {
.alertify,
.alertify-logs {
width: 90%;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.alertify {
left: 5%;
margin: 0;
}
}

Просмотреть файл

@ -1,81 +0,0 @@
/**
* Default Look and Feel
*/
.alertify,
.alertify-log {
font-family: sans-serif;
}
.alertify {
background: #FFF;
border: 10px solid #333; /* browsers that don't support rgba */
border: 10px solid rgba(0,0,0,.7);
border-radius: 8px;
box-shadow: 0 3px 3px rgba(0,0,0,.3);
-webkit-background-clip: padding; /* Safari 4? Chrome 6? */
-moz-background-clip: padding; /* Firefox 3.6 */
background-clip: padding-box; /* Firefox 4, Safari 5, Opera 10, IE 9 */
}
.alertify-text {
border: 1px solid #CCC;
padding: 10px;
border-radius: 4px;
}
.alertify-button {
border-radius: 4px;
color: #FFF;
font-weight: bold;
padding: 6px 15px;
text-decoration: none;
text-shadow: 1px 1px 0 rgba(0,0,0,.5);
box-shadow: inset 0 1px 0 0 rgba(255,255,255,.5);
background-image: -webkit-linear-gradient(top, rgba(255,255,255,.3), rgba(255,255,255,0));
background-image: -moz-linear-gradient(top, rgba(255,255,255,.3), rgba(255,255,255,0));
background-image: -ms-linear-gradient(top, rgba(255,255,255,.3), rgba(255,255,255,0));
background-image: -o-linear-gradient(top, rgba(255,255,255,.3), rgba(255,255,255,0));
background-image: linear-gradient(top, rgba(255,255,255,.3), rgba(255,255,255,0));
}
.alertify-button:hover,
.alertify-button:focus {
outline: none;
background-image: -webkit-linear-gradient(top, rgba(0,0,0,.1), rgba(0,0,0,0));
background-image: -moz-linear-gradient(top, rgba(0,0,0,.1), rgba(0,0,0,0));
background-image: -ms-linear-gradient(top, rgba(0,0,0,.1), rgba(0,0,0,0));
background-image: -o-linear-gradient(top, rgba(0,0,0,.1), rgba(0,0,0,0));
background-image: linear-gradient(top, rgba(0,0,0,.1), rgba(0,0,0,0));
}
.alertify-button:focus {
box-shadow: 0 0 15px #2B72D5;
}
.alertify-button:active {
position: relative;
box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);
}
.alertify-button-cancel,
.alertify-button-cancel:hover,
.alertify-button-cancel:focus {
background-color: #FE1A00;
border: 1px solid #D83526;
}
.alertify-button-ok,
.alertify-button-ok:hover,
.alertify-button-ok:focus {
background-color: #5CB811;
border: 1px solid #3B7808;
}
.alertify-log {
background: #1F1F1F;
background: rgba(0,0,0,.9);
padding: 15px;
border-radius: 4px;
color: #FFF;
text-shadow: -1px -1px 0 rgba(0,0,0,.5);
}
.alertify-log-error {
background: #FE1A00;
background: rgba(254,26,0,.9);
}
.alertify-log-success {
background: #5CB811;
background: rgba(92,184,17,.9);
}

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -1 +0,0 @@
"format global";"deps angular";"deps moment";!function(){"use strict";function a(a,b){return a.module("angularMoment",[]).constant("angularMomentConfig",{preprocess:null,timezone:"",format:null}).constant("moment",b).constant("amTimeAgoConfig",{withoutSuffix:!1,serverTime:null,titleFormat:null}).directive("amTimeAgo",["$window","moment","amMoment","amTimeAgoConfig","angularMomentConfig",function(b,c,d,e,f){return function(g,h,i){function j(){var a;if(e.serverTime){var b=(new Date).getTime(),d=b-u+e.serverTime;a=c(d)}else a=c();return a}function k(){q&&(b.clearTimeout(q),q=null)}function l(a){if(h.text(a.from(j(),s)),t&&!h.attr("title")&&h.attr("title",a.local().format(t)),!x){var c=Math.abs(j().diff(a,"minute")),d=3600;1>c?d=1:60>c?d=30:180>c&&(d=300),q=b.setTimeout(function(){l(a)},1e3*d)}}function m(a){y&&h.attr("datetime",a)}function n(){if(k(),o){var a=d.preprocessDate(o,v,r);l(a),m(a.toISOString())}}var o,p,q=null,r=f.format,s=e.withoutSuffix,t=e.titleFormat,u=(new Date).getTime(),v=f.preprocess,w=i.amTimeAgo.replace(/^::/,""),x=0===i.amTimeAgo.indexOf("::"),y="TIME"===h[0].nodeName.toUpperCase();p=g.$watch(w,function(a){return"undefined"==typeof a||null===a||""===a?(k(),void(o&&(h.text(""),m(""),o=null))):(o=a,n(),void(void 0!==a&&x&&p()))}),a.isDefined(i.amWithoutSuffix)&&g.$watch(i.amWithoutSuffix,function(a){"boolean"==typeof a?(s=a,n()):s=e.withoutSuffix}),i.$observe("amFormat",function(a){"undefined"!=typeof a&&(r=a,n())}),i.$observe("amPreprocess",function(a){v=a,n()}),g.$on("$destroy",function(){k()}),g.$on("amMoment:localeChanged",function(){n()})}}]).service("amMoment",["moment","$rootScope","$log","angularMomentConfig",function(b,c,d,e){var f=this;this.preprocessors={utc:b.utc,unix:b.unix},this.changeLocale=function(d){var e=(b.locale||b.lang)(d);return a.isDefined(d)&&(c.$broadcast("amMoment:localeChanged"),c.$broadcast("amMoment:languageChange")),e},this.changeLanguage=function(a){return d.warn("angular-moment: Usage of amMoment.changeLanguage() is deprecated. Please use changeLocale()"),f.changeLocale(a)},this.preprocessDate=function(c,f,g){return a.isUndefined(f)&&(f=e.preprocess),this.preprocessors[f]?this.preprocessors[f](c,g):(f&&d.warn("angular-moment: Ignoring unsupported value for preprocess: "+f),!isNaN(parseFloat(c))&&isFinite(c)?b(parseInt(c,10)):b(c,g))},this.applyTimezone=function(a){var b=e.timezone;return a&&b&&(a.tz?a=a.tz(b):d.warn("angular-moment: timezone specified but moment.tz() is undefined. Did you forget to include moment-timezone.js?")),a}}]).filter("amCalendar",["moment","amMoment",function(a,b){return function(c,d){if("undefined"==typeof c||null===c)return"";c=b.preprocessDate(c,d);var e=a(c);return e.isValid()?b.applyTimezone(e).calendar():""}}]).filter("amDateFormat",["moment","amMoment",function(a,b){return function(c,d,e){if("undefined"==typeof c||null===c)return"";c=b.preprocessDate(c,e);var f=a(c);return f.isValid()?b.applyTimezone(f).format(d):""}}]).filter("amDurationFormat",["moment",function(a){return function(b,c,d){return"undefined"==typeof b||null===b?"":a.duration(b,c).humanize(d)}}]).filter("amTimeAgo",["moment","amMoment",function(a,b){return function(c,d,e){if("undefined"==typeof c||null===c)return"";c=b.preprocessDate(c,d);var f=a(c);return f.isValid()?b.applyTimezone(f).fromNow(e):""}}])}"function"==typeof define&&define.amd?define("angular-moment",["angular","moment"],a):"undefined"!=typeof module&&module&&module.exports?a(angular,require("moment")):a(angular,window.moment)}();

292
src/releng_frontend/src/static/angular.min.js поставляемый
Просмотреть файл

@ -1,292 +0,0 @@
/*
AngularJS v1.4.5
(c) 2010-2015 Google, Inc. http://angularjs.org
License: MIT
*/
(function(N,W,u){'use strict';function G(b){return function(){var a=arguments[0],c;c="["+(b?b+":":"")+a+"] http://errors.angularjs.org/1.4.5/"+(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 Da(b){if(null==b||Ya(b))return!1;var a="length"in Object(b)&&b.length;
return b.nodeType===pa&&a?!0:H(b)||K(b)||0===a||"number"===typeof a&&0<a&&a-1 in b}function n(b,a,c){var d,e;if(b)if(B(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(K(b)||Da(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!==n)b.forEach(a,c,b);else if(lc(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)Na.call(b,d)&&a.call(c,b[d],d,b);return b}function mc(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 nc(b){return function(a,c){b(c,a)}}function Ud(){return++mb}function oc(b,a){a?b.$$hashKey=a:delete b.$$hashKey}function Mb(b,a,c){for(var d=b.$$hashKey,e=0,f=a.length;e<f;++e){var g=a[e];if(D(g)||B(g))for(var h=Object.keys(g),l=0,k=h.length;l<k;l++){var m=h[l],q=g[m];c&&D(q)?ca(q)?b[m]=new Date(q.valueOf()):Oa(q)?
b[m]=new RegExp(q):(D(b[m])||(b[m]=K(q)?[]:{}),Mb(b[m],[q],!0)):b[m]=q}}oc(b,d);return b}function Q(b){return Mb(b,xa.call(arguments,1),!1)}function Vd(b){return Mb(b,xa.call(arguments,1),!0)}function Y(b){return parseInt(b,10)}function Nb(b,a){return Q(Object.create(b),a)}function v(){}function Za(b){return b}function qa(b){return function(){return b}}function pc(b){return B(b.toString)&&b.toString!==Object.prototype.toString}function y(b){return"undefined"===typeof b}function x(b){return"undefined"!==
typeof b}function D(b){return null!==b&&"object"===typeof b}function lc(b){return null!==b&&"object"===typeof b&&!qc(b)}function H(b){return"string"===typeof b}function X(b){return"number"===typeof b}function ca(b){return"[object Date]"===sa.call(b)}function B(b){return"function"===typeof b}function Oa(b){return"[object RegExp]"===sa.call(b)}function Ya(b){return b&&b.window===b}function $a(b){return b&&b.$evalAsync&&b.$watch}function ab(b){return"boolean"===typeof b}function rc(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 I(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(Ya(b)||$a(b))throw Ea("cpws");if(sc.test(sa.call(a)))throw Ea("cpta");if(a){if(b===a)throw Ea("cpi");c=c||[];d=d||[];D(b)&&(c.push(b),d.push(a));var e;if(K(b))for(e=a.length=0;e<b.length;e++)a.push(fa(b[e],null,c,d));else{var f=a.$$hashKey;K(a)?
a.length=0:n(a,function(b,c){delete a[c]});if(lc(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)Na.call(b,e)&&(a[e]=fa(b[e],null,c,d));oc(a,f)}}else if(a=b,D(b)){if(c&&-1!==(f=c.indexOf(b)))return d[f];if(K(b))return fa(b,[],c,d);if(sc.test(sa.call(b)))a=new b.constructor(b);else if(ca(b))a=new Date(b.getTime());else if(Oa(b))a=new RegExp(b.source,b.toString().match(/[^\/]*$/)[0]),a.lastIndex=
b.lastIndex;else return e=Object.create(qc(b)),fa(b,e,c,d);d&&(c.push(b),d.push(a))}return a}function ia(b,a){if(K(b)){a=a||[];for(var c=0,d=b.length;c<d;c++)a[c]=b[c]}else if(D(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(K(b)){if(!K(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(ca(b))return ca(a)?
ka(b.getTime(),a.getTime()):!1;if(Oa(b))return Oa(a)?b.toString()==a.toString():!1;if($a(b)||$a(a)||Ya(b)||Ya(a)||K(a)||ca(a)||Oa(a))return!1;c=ga();for(d in b)if("$"!==d.charAt(0)&&!B(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]===u||B(a[d])))return!1;return!0}return!1}function cb(b,a,c){return b.concat(xa.call(a,c))}function tc(b,a){var c=2<arguments.length?xa.call(arguments,2):[];return!B(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=u:Ya(a)?c="$WINDOW":a&&W===a?c="$DOCUMENT":$a(a)&&(c="$SCOPE");return c}function db(b,a){if("undefined"===typeof b)return u;X(a)||(a=a?2:null);return JSON.stringify(b,Xd,a)}function uc(b){return H(b)?JSON.parse(b):b}function vc(b,a){var c=Date.parse("Jan 01, 1970 00:00:00 "+b)/6E4;return isNaN(c)?a:c}function Ob(b,
a,c){c=c?-1:1;var d=vc(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=z(b).clone();try{b.empty()}catch(a){}var c=z("<div>").append(b).html();try{return b[0].nodeType===Pa?I(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+I(b)})}catch(d){return I(c)}}function wc(b){try{return decodeURIComponent(b)}catch(a){}}function xc(b){var a={};n((b||"").split("&"),function(b){var d,e,f;b&&(e=
b=b.replace(/\+/g,"%20"),d=b.indexOf("="),-1!==d&&(e=b.substring(0,d),f=b.substring(d+1)),e=wc(e),x(e)&&(f=x(f)?wc(f):!0,Na.call(a,e)?K(a[e])?a[e].push(f):a[e]=[a[e],f]:a[e]=f))});return a}function Pb(b){var a=[];n(b,function(b,d){K(b)?n(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 nb(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=Qa.length;for(d=0;d<e;++d)if(c=Qa[d]+a,H(c=b.getAttribute(c)))return c;return null}function Zd(b,a){var c,d,e={};n(Qa,function(a){a+="app";!c&&b.hasAttribute&&b.hasAttribute(a)&&(c=b,d=b.getAttribute(a))});n(Qa,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 yc(b,a,c){D(c)||(c={});c=Q({strictDi:!1},c);var d=function(){b=z(b);if(b.injector()){var d=b[0]===W?"document":ua(b);throw Ea("btstrpd",d.replace(/</,"&lt;").replace(/>/,"&gt;"));}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!/;N&&e.test(N.name)&&(c.debugInfoEnabled=!0,N.name=N.name.replace(e,""));if(N&&!f.test(N.name))return d();N.name=N.name.replace(f,"");aa.resumeBootstrap=function(b){n(b,function(b){a.push(b)});return d()};B(aa.resumeDeferredBootstrap)&&aa.resumeDeferredBootstrap()}function $d(){N.name="NG_ENABLE_DEBUG_INFO!"+N.name;N.location.reload()}function ae(b){b=aa.element(b).injector();if(!b)throw Ea("test");return b.get("$$testability")}
function zc(b,a){a=a||"_";return b.replace(be,function(b,d){return(d?a:"")+b.toLowerCase()})}function ce(){var b;if(!Ac){var a=ob();la=N.jQuery;x(a)&&(la=null===a?u:N[a]);la&&la.fn.on?(z=la,Q(la.fn,{scope:Ra.scope,isolateScope:Ra.isolateScope,controller:Ra.controller,injector:Ra.injector,inheritedData:Ra.inheritedData}),b=la.cleanData,la.cleanData=function(a){var d;if(Qb)Qb=!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)}):z=R;aa.element=
z;Ac=!0}}function pb(b,a,c){if(!b)throw Ea("areq",a||"?",c||"required");return b}function Sa(b,a,c){c&&K(b)&&(b=b[b.length-1]);pb(B(b),a,"not a function, got "+(b&&"object"===typeof b?b.constructor.name||"Object":typeof b));return b}function Ta(b,a){if("hasOwnProperty"===b)throw Ea("badname",a);}function Bc(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&&B(b)?tc(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 z(c)}function ga(){return Object.create(null)}function de(b){function a(a,b,c){return a[b]||(a[b]=c())}var c=G("$injector"),d=G("ng");b=a(b,"angular",Object);b.$$minErr=b.$$minErr||G;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 E}}function b(a,c){return function(b,
e){e&&B(e)&&(e.$$moduleName=f);d.push([a,c,arguments]);return E}}if(!g)throw c("nomod",f);var d=[],e=[],s=[],t=a("$injector","invoke","push",e),E={_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:t,run:function(a){s.push(a);return this}};h&&t(h);return E})}})}function ee(b){Q(b,{bootstrap:yc,copy:fa,extend:Q,merge:Vd,equals:ka,element:z,forEach:n,injector:eb,noop:v,bind:tc,toJson:db,fromJson:uc,identity:Za,isUndefined:y,isDefined:x,isString:H,isFunction:B,isObject:D,isNumber:X,isElement:rc,isArray:K,version:fe,isDate:ca,lowercase:I,uppercase:rb,callbacks:{counter:0},getTestability:ae,$$minErr:G,$$csp:Fa,reloadWithDebugInfo:$d});
Rb=de(N);Rb("ng",["ngLocale"],["$provide",function(a){a.provider({$$sanitizeUri:ge});a.provider("$compile",Cc).directive({a:he,input:Dc,textarea:Dc,form:ie,script:je,select:ke,style:le,option:me,ngBind:ne,ngBindHtml:oe,ngBindTemplate:pe,ngClass:qe,ngClassEven:re,ngClassOdd:se,ngCloak:te,ngController:ue,ngForm:ve,ngHide:we,ngIf:xe,ngInclude:ye,ngInit:ze,ngNonBindable:Ae,ngPluralize:Be,ngRepeat:Ce,ngShow:De,ngStyle:Ee,ngSwitch:Fe,ngSwitchWhen:Ge,ngSwitchDefault:He,ngOptions:Ie,ngTransclude:Je,ngModel:Ke,
ngList:Le,ngChange:Me,pattern:Ec,ngPattern:Ec,required:Fc,ngRequired:Fc,minlength:Gc,ngMinlength:Gc,maxlength:Hc,ngMaxlength:Hc,ngValue:Ne,ngModelOptions:Oe}).directive({ngInclude:Pe}).directive(sb).directive(Ic);a.provider({$anchorScroll:Qe,$animate:Re,$animateCss:Se,$$animateQueue:Te,$$AnimateRunner:Ue,$browser:Ve,$cacheFactory:We,$controller:Xe,$document:Ye,$exceptionHandler:Ze,$filter:Jc,$$forceReflow:$e,$interpolate:af,$interval:bf,$http:cf,$httpParamSerializer:df,$httpParamSerializerJQLike:ef,
$httpBackend:ff,$location:gf,$log:hf,$parse:jf,$rootScope:kf,$q:lf,$$q:mf,$sce:nf,$sceDelegate:of,$sniffer:pf,$templateCache:qf,$templateRequest:rf,$$testability:sf,$timeout:tf,$window:uf,$$rAF:vf,$$jqLite:wf,$$HashMap:xf,$$cookieReader:yf})}])}function fb(b){return b.replace(zf,function(a,b,d,e){return e?d.toUpperCase():d}).replace(Af,"Moz$1")}function Kc(b){b=b.nodeType;return b===pa||!b||9===b}function Lc(b,a){var c,d,e=a.createDocumentFragment(),f=[];if(Sb.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="";n(f,function(a){e.appendChild(a)});return e}function R(b){if(b instanceof R)return b;var a;H(b)&&(b=T(b),a=!0);if(!(this instanceof R)){if(a&&"<"!=b.charAt(0))throw Tb("nosel");return new R(b)}if(a){a=W;var c;b=(c=Df.exec(b))?[a.createElement(c[1])]:
(c=Lc(b,a))?c.childNodes:[]}Mc(this,b)}function Ub(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 Nc(b,a,c,d){if(x(d))throw Tb("offargs");var e=(d=vb(b))&&d.events,f=d&&d.handle;if(f)if(a)n(a.split(" "),function(a){if(x(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&&gb[c];d&&(a?delete d.data[a]:(d.handle&&(d.events.$destroy&&d.handle({},"$destroy"),Nc(b)),delete gb[c],b.ng339=u))}function vb(b,a){var c=b.ng339,c=c&&gb[c];a&&!c&&(b.ng339=c=++Ef,c=gb[c]={events:{},data:{},handle:u});return c}function Vb(b,a,c){if(Kc(b)){var d=x(c),e=!d&&a&&!D(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];Q(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&&n(a.split(" "),function(a){b.setAttribute("class",T((" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").replace(" "+T(a)+" "," ")))})}function yb(b,a){if(a&&b.setAttribute){var c=(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ");n(a.split(" "),function(a){a=T(a);-1===c.indexOf(" "+a+" ")&&(c+=a+" ")});b.setAttribute("class",T(c))}}function Mc(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 Oc(b,a){return zb(b,"$"+(a||"ngController")+"Controller")}function zb(b,a,c){9==b.nodeType&&(b=b.documentElement);for(a=K(a)?a:[a];b;){for(var d=0,e=a.length;d<e;d++)if((c=z.data(b,a[d]))!==u)return c;b=b.parentNode||11===b.nodeType&&b.host}}function Pc(b){for(tb(b,!0);b.firstChild;)b.removeChild(b.firstChild)}function Wb(b,a){a||tb(b);var c=b.parentNode;c&&c.removeChild(b)}function Ff(b,a){a=a||N;if("complete"===a.document.readyState)a.setTimeout(b);
else z(a).on("load",b)}function Qc(b,a){var c=Ab[a.toLowerCase()];return c&&Rc[ta(b)]&&c}function Gf(b,a){var c=b.nodeName;return("INPUT"===c||"TEXTAREA"===c)&&Sc[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(y(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 Q(R,{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 Ua(b,a){if(a){var c=0;this.nextUid=function(){return++c}}n(b,this.put,this)}function If(b){return(b=b.toString().replace(Tc,"").match(Uc))?"function("+(b[1]||"").replace(/[\s\r\n]+/," ")+")":"fn"}function eb(b,a){function c(a){return function(b,c){if(D(b))n(b,nc(a));else return a(b,c)}}function d(a,b){Ta(a,"service");if(B(b)||K(b))b=s.instantiate(b);if(!b.$get)throw Ha("pget",a);return q[a+"Provider"]=b}function e(a,b){return function(){var c=
E.invoke(b,this);if(y(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){pb(y(a)||K(a),"modulesToLoad","not an array");var b=[],c;n(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(!m.get(a)){m.put(a,!0);try{H(a)?(c=Rb(a),b=b.concat(g(c.requires)).concat(c._runBlocks),d(c._invokeQueue),d(c._configBlocks)):B(a)?b.push(s.invoke(a)):K(a)?b.push(s.invoke(a)):Sa(a,"module")}catch(e){throw K(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,m;s=0;for(l=k.length;s<l;s++){m=k[s];if("string"!==
typeof m)throw Ha("itkn",m);h.push(f&&f.hasOwnProperty(m)?f[m]:d(m,g))}K(b)&&(b=b[l]);return b.apply(c,h)}return{invoke:e,instantiate:function(a,b,c){var d=Object.create((K(a)?a[a.length-1]:a).prototype||null);a=e(a,d,b,c);return D(a)||B(a)?a:d},get:d,annotate:eb.$$annotate,has:function(a){return q.hasOwnProperty(a+"Provider")||b.hasOwnProperty(a)}}}a=!0===a;var l={},k=[],m=new Ua([],!0),q={$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,qa(b),!1)}),constant:c(function(a,b){Ta(a,"constant");q[a]=b;t[a]=b}),decorator:function(a,b){var c=s.get(a+"Provider"),d=c.$get;c.$get=function(){var a=E.invoke(d,c);return E.invoke(b,null,{$delegate:a})}}}},s=q.$injector=h(q,function(a,b){aa.isString(b)&&k.push(b);throw Ha("unpr",k.join(" <- "));}),t={},E=t.$injector=h(t,function(a,b){var c=s.get(a+"Provider",b);return E.invoke(c.$get,c,u,a)});n(g(b),function(a){a&&E.invoke(a)});return E}function Qe(){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;B(c)?c=c():rc(c)?(c=c[0],c="fixed"!==a.getComputedStyle(c).position?0:c.getBoundingClientRect().bottom):X(c)||(c=0);c&&(b=b.getBoundingClientRect().top,a.scrollBy(0,b-c))}else a.scrollTo(0,0)}function g(a){a=H(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 hb(b,a){if(!b&&!a)return"";if(!b)return a;if(!a)return b;K(b)&&(b=b.join(" "));K(a)&&(a=a.join(" "));return b+" "+a}function Jf(b){H(b)&&(b=b.split(" "));var a=ga();n(b,function(b){b.length&&(a[b]=!0)});return a}function Ia(b){return D(b)?b:{}}function Kf(b,a,c,d){function e(a){try{a.apply(null,xa.call(arguments,
1))}finally{if(E--,0===E)for(;L.length;)try{L.pop()()}catch(b){c.error(b)}}}function f(){g();h()}function g(){a:{try{w=m.state;break a}catch(a){}w=void 0}w=y(w)?null:w;ka(w,F)&&(w=F);F=w}function h(){if(A!==l.url()||p!==w)A=l.url(),p=w,n(O,function(a){a(l.url(),w)})}var l=this,k=b.location,m=b.history,q=b.setTimeout,s=b.clearTimeout,t={};l.isMock=!1;var E=0,L=[];l.$$completeOutstandingRequest=e;l.$$incOutstandingRequestCount=function(){E++};l.notifyWhenNoOutstandingRequests=function(a){0===E?a():
L.push(a)};var w,p,A=k.href,r=a.find("base"),M=null;g();p=w;l.url=function(a,c,e){y(e)&&(e=null);k!==b.location&&(k=b.location);m!==b.history&&(m=b.history);if(a){var f=p===e;if(A===a&&(!d.history||f))return l;var h=A&&Ja(A)===Ja(a);A=a;p=e;if(!d.history||h&&f){if(!h||M)M=a;c?k.replace(a):h?(c=k,e=a.indexOf("#"),a=-1===e?"":a.substr(e),c.hash=a):k.href=a}else m[c?"replaceState":"pushState"](e,"",a),g(),p=w;return l}return M||k.href.replace(/%27/g,"'")};l.state=function(){return w};var O=[],J=!1,F=
null;l.onUrlChange=function(a){if(!J){if(d.history)z(b).on("popstate",f);z(b).on("hashchange",f);J=!0}O.push(a);return a};l.$$applicationDestroyed=function(){z(b).off("hashchange popstate",f)};l.$$checkUrlChange=h;l.baseHref=function(){var a=r.attr("href");return a?a.replace(/^(https?\:)?\/\/[^\/]*/,""):""};l.defer=function(a,b){var c;E++;c=q(function(){delete t[c];e(a)},b||0);t[c]=!0;return c};l.defer.cancel=function(a){return t[a]?(delete t[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!=q&&(s?s==a&&(s=a.n):s=a,f(a.n,a.p),f(a,q),q=a,q.n=null)}function f(a,b){a!=b&&(a&&(a.p=b),b&&(b.n=a))}if(b in a)throw G("$cacheFactory")("iid",b);var g=0,h=Q({},d,{id:b}),l={},k=d&&d.capacity||Number.MAX_VALUE,m={},q=null,s=null;return a[b]={put:function(a,b){if(!y(b)){if(k<Number.MAX_VALUE){var c=m[a]||(m[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=m[a];if(!b)return;e(b)}return l[a]},remove:function(a){if(k<Number.MAX_VALUE){var b=m[a];if(!b)return;b==q&&(q=b.p);b==s&&(s=b.n);f(b.n,b.p);delete m[a]}delete l[a];g--},removeAll:function(){l={};g=0;m={};q=s=null},destroy:function(){m=h=l=null;delete a[b]},info:function(){return Q({},h,{size:g})}}}var a={};b.info=function(){var b={};n(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return a[b]};return b}}function qf(){this.$get=
["$cacheFactory",function(b){return b("templates")}]}function Cc(b,a){function c(a,b,c){var d=/^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/,e={};n(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!==I(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){Ta(a,"directive");H(a)?(d(a),pb(f,"directiveFactory"),e.hasOwnProperty(a)||(e[a]=[],b.factory(a+"Directive",["$injector","$exceptionHandler",function(b,d){var f=[];n(e[a],function(e,g){try{var h=b.invoke(e);B(h)?h={compile:qa(h)}:!h.compile&&h.link&&(h.compile=qa(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 l=h,k=h,s=h.name,m={isolateScope:null,bindToController:null};D(k.scope)&&(!0===k.bindToController?(m.bindToController=c(k.scope,s,!0),m.isolateScope={}):m.isolateScope=c(k.scope,s,!1));D(k.bindToController)&&(m.bindToController=c(k.bindToController,s,!0));if(D(m.bindToController)){var S=k.controller,E=k.controllerAs;if(!S)throw ea("noctrl",s);var ha;a:if(E&&H(E))ha=E;else{if(H(S)){var n=Vc.exec(S);if(n){ha=n[3];break a}}ha=void 0}if(!ha)throw ea("noident",
s);}var r=l.$$bindings=m;D(r.isolateScope)&&(h.$$isolateBindings=r.isolateScope);h.$$moduleName=e.$$moduleName;f.push(h)}catch(u){d(u)}});return f}])),e[a].push(f)):n(a,nc(s));return this};this.aHrefSanitizationWhitelist=function(b){return x(b)?(a.aHrefSanitizationWhitelist(b),this):a.aHrefSanitizationWhitelist()};this.imgSrcSanitizationWhitelist=function(b){return x(b)?(a.imgSrcSanitizationWhitelist(b),this):a.imgSrcSanitizationWhitelist()};var m=!0;this.debugInfoEnabled=function(a){return x(a)?
(m=a,this):m};this.$get=["$injector","$interpolate","$exceptionHandler","$templateRequest","$parse","$controller","$rootScope","$document","$sce","$animate","$$sanitizeUri",function(a,b,c,d,w,p,A,r,M,O,J){function F(a,b){try{a.addClass(b)}catch(c){}}function V(a,b,c,d,e){a instanceof z||(a=z(a));n(a,function(b,c){b.nodeType==Pa&&b.nodeValue.match(/\S+/)&&(a[c]=z(b).wrap("<span></span>").parent()[0])});var f=S(a,b,a,c,d,e);V.$$addScopeClass(a);var g=null;return function(b,c,d){pb(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?z(Xb(g,z("<div>").append(a).html())):c?Ra.clone.call(a):a;if(h)for(var k in h)d.data("$"+k+"Controller",h[k].instance);V.$$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,m,s,t,O;if(p)for(O=Array(c.length),m=0;m<
h.length;m+=3)f=h[m],O[f]=c[f];else O=c;m=0;for(s=h.length;m<s;)if(k=O[h[m++]],c=h[m++],f=h[m++],c){if(c.scope){if(l=a.$new(),V.$$addScopeInfo(z(k),l),t=c.$$destroyBindings)c.$$destroyBindings=null,l.$on("$destroyed",t)}else l=a;t=c.transcludeOnThisElement?P(a,c.transclude,e):!c.templateOnThisElement&&e?e:!e&&b?P(a,b):null;c(f,l,k,d,t,c)}else f&&f(a,k.childNodes,u,e)}for(var h=[],k,l,m,s,p,t=0;t<a.length;t++){k=new aa;l=ha(a[t],[],k,0===t?d:u,e);(f=l.length?C(l,a[t],k,b,c,null,[],[],f):null)&&f.scope&&
V.$$addScopeClass(k.$$element);k=f&&f.terminal||!(m=a[t].childNodes)||!m.length?null:S(m,f?(f.transcludeOnThisElement||!f.templateOnThisElement)&&f.transclude:b);if(f||k)h.push(t,f,k),s=!0,p=p||f;f=null}return s?g:null}function P(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 pa:x(b,va(ta(a)),"E",d,e);for(var l,m,s,p=a.attributes,
t=0,O=p&&p.length;t<O;t++){var L=!1,J=!1;l=p[t];k=l.name;m=T(l.value);l=va(k);if(s=ia.test(l))k=k.replace(Xc,"").substr(8).replace(/_(.)/g,function(a,b){return b.toUpperCase()});var S=l.replace(/(Start|End)$/,"");G(S)&&l===S+"Start"&&(L=k,J=k.substr(0,k.length-5)+"end",k=k.substr(0,k.length-6));l=va(k.toLowerCase());h[l]=k;if(s||!c.hasOwnProperty(l))c[l]=m,Qc(a,l)&&(c[l]=!0);X(a,b,m,l,s);x(b,l,"A",d,e,L,J)}a=a.className;D(a)&&(a=a.animVal);if(H(a)&&""!==a)for(;k=g.exec(a);)l=va(k[2]),x(b,l,"C",d,
e)&&(c[l]=T(k[3])),a=a.substr(k.index+k[0].length);break;case Pa:if(11===Va)for(;a.parentNode&&a.nextSibling&&a.nextSibling.nodeType===Pa;)a.nodeValue+=a.nextSibling.nodeValue,a.parentNode.removeChild(a.nextSibling);wa(b,a.nodeValue);break;case 8:try{if(k=f.exec(a.nodeValue))l=va(k[1]),x(b,l,"M",d,e)&&(c[l]=T(k[2]))}catch(E){}}b.sort(za);return b}function ya(a,b,c){var d=[],e=0;if(b&&a.hasAttribute&&a.hasAttribute(b)){do{if(!a)throw ea("uterdir",b,c);a.nodeType==pa&&(a.hasAttribute(b)&&e++,a.hasAttribute(c)&&
e--);d.push(a);a=a.nextSibling}while(0<e)}else d.push(a);return z(d)}function Wc(a,b,c){return function(d,e,f,g,h){e=ya(e[0],b,c);return a(d,e,f,g,h)}}function C(a,b,d,e,f,g,h,k,m){function s(a,b,c,d){if(a){c&&(a=Wc(a,c,d));a.require=C.require;a.directiveName=x;if(P===C||C.$$isolateScope)a=Z(a,{isolateScope:!0});h.push(a)}if(b){c&&(b=Wc(b,c,d));b.require=C.require;b.directiveName=x;if(P===C||C.$$isolateScope)b=Z(b,{isolateScope:!0});k.push(b)}}function t(a,b,c,d){var e;if(H(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(K(b))for(e=[],g=0,f=b.length;g<f;g++)e[g]=t(a,b[g],c,d);return e||null}function O(a,b,c,d,e,f){var g=ga(),h;for(h in d){var k=d[h],l={$scope:k===P||k.$$isolateScope?e:f,$element:a,$attrs:b,$transclude:c},m=k.controller;"@"==m&&(m=b[k.name]);l=p(m,l,!0,k.controllerAs);g[k.name]=l;r||a.data("$"+k.name+
"Controller",l.instance)}return g}function L(a,c,e,f,g,l){function m(a,b,c){var d;$a(a)||(c=b,b=a,a=u);r&&(d=A);c||(c=r?ja.parent():ja);return g(a,b,d,c,ya)}var s,p,J,E,A,ha,ja;b===e?(f=d,ja=d.$$element):(ja=z(e),f=new aa(ja,d));P&&(E=c.$new(!0));g&&(ha=m,ha.$$boundTransclude=g);w&&(A=O(ja,f,ha,w,E,c));P&&(V.$$addScopeInfo(ja,E,!0,!(F&&(F===P||F===P.$$originalDirective))),V.$$addScopeClass(ja,!0),E.$$isolateBindings=P.$$isolateBindings,Y(c,f,E,E.$$isolateBindings,P,E));if(A){var n=P||S,M;n&&A[n.name]&&
(p=n.$$bindings.bindToController,(J=A[n.name])&&J.identifier&&p&&(M=J,l.$$destroyBindings=Y(c,f,J.instance,p,n)));for(s in A){J=A[s];var C=J();C!==J.instance&&(J.instance=C,ja.data("$"+s+"Controller",C),J===M&&(l.$$destroyBindings(),l.$$destroyBindings=Y(c,f,C,p,n)))}}s=0;for(l=h.length;s<l;s++)p=h[s],$(p,p.isolateScope?E:c,ja,f,p.require&&t(p.directiveName,p.require,ja,A),ha);var ya=c;P&&(P.template||null===P.templateUrl)&&(ya=E);a&&a(ya,e.childNodes,u,g);for(s=k.length-1;0<=s;s--)p=k[s],$(p,p.isolateScope?
E:c,ja,f,p.require&&t(p.directiveName,p.require,ja,A),ha)}m=m||{};for(var J=-Number.MAX_VALUE,S=m.newScopeDirective,w=m.controllerDirectives,P=m.newIsolateScopeDirective,F=m.templateDirective,A=m.nonTlbTranscludeDirective,n=!1,M=!1,r=m.hasElementTranscludeDirective,ba=d.$$element=z(b),C,x,v,y=e,za,wa=0,G=a.length;wa<G;wa++){C=a[wa];var Bb=C.$$start,I=C.$$end;Bb&&(ba=ya(b,Bb,I));v=u;if(J>C.priority)break;if(v=C.scope)C.templateUrl||(D(v)?(N("new/isolated scope",P||S,C,ba),P=C):N("new/isolated scope",
P,C,ba)),S=S||C;x=C.name;!C.templateUrl&&C.controller&&(v=C.controller,w=w||ga(),N("'"+x+"' controller",w[x],C,ba),w[x]=C);if(v=C.transclude)n=!0,C.$$tlb||(N("transclusion",A,C,ba),A=C),"element"==v?(r=!0,J=C.priority,v=ba,ba=d.$$element=z(W.createComment(" "+x+": "+d[x]+" ")),b=ba[0],U(f,xa.call(v,0),b),y=V(v,e,J,g&&g.name,{nonTlbTranscludeDirective:A})):(v=z(Ub(b)).contents(),ba.empty(),y=V(v,e));if(C.template)if(M=!0,N("template",F,C,ba),F=C,v=B(C.template)?C.template(ba,d):C.template,v=fa(v),
C.replace){g=C;v=Sb.test(v)?Yc(Xb(C.templateNamespace,T(v))):[];b=v[0];if(1!=v.length||b.nodeType!==pa)throw ea("tplrt",x,"");U(f,ba,b);G={$attr:{}};v=ha(b,[],G);var Q=a.splice(wa+1,a.length-(wa+1));P&&Zc(v);a=a.concat(v).concat(Q);$c(d,G);G=a.length}else ba.html(v);if(C.templateUrl)M=!0,N("template",F,C,ba),F=C,C.replace&&(g=C),L=Lf(a.splice(wa,a.length-wa),ba,d,f,n&&y,h,k,{controllerDirectives:w,newScopeDirective:S!==C&&S,newIsolateScopeDirective:P,templateDirective:F,nonTlbTranscludeDirective:A}),
G=a.length;else if(C.compile)try{za=C.compile(ba,d,y),B(za)?s(null,za,Bb,I):za&&s(za.pre,za.post,Bb,I)}catch(R){c(R,ua(ba))}C.terminal&&(L.terminal=!0,J=Math.max(J,C.priority))}L.scope=S&&!0===S.scope;L.transcludeOnThisElement=n;L.templateOnThisElement=M;L.transclude=y;m.hasElementTranscludeDirective=r;return L}function Zc(a){for(var b=0,c=a.length;b<c;b++)a[b]=Nb(a[b],{$$isolateScope:!0})}function x(b,d,f,g,h,k,l){if(d===h)return null;h=null;if(e.hasOwnProperty(d)){var m;d=a.get(d+"Directive");for(var p=
0,t=d.length;p<t;p++)try{m=d[p],(g===u||g>m.priority)&&-1!=m.restrict.indexOf(f)&&(k&&(m=Nb(m,{$$start:k,$$end:l})),b.push(m),h=m)}catch(J){c(J)}}return h}function G(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 $c(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;n(a,function(d,e){"$"!=e.charAt(0)&&(b[e]&&b[e]!==d&&(d+=("style"===e?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});n(b,function(b,f){"class"==f?(F(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 Lf(a,b,c,e,f,g,h,k){var l=[],m,s,p=b[0],t=a.shift(),J=Nb(t,{templateUrl:null,transclude:null,replace:null,$$originalDirective:t}),O=B(t.templateUrl)?t.templateUrl(b,c):t.templateUrl,E=t.templateNamespace;b.empty();d(O).then(function(d){var L,w;d=fa(d);if(t.replace){d=Sb.test(d)?Yc(Xb(E,T(d))):[];L=d[0];if(1!=d.length||L.nodeType!==
pa)throw ea("tplrt",t.name,O);d={$attr:{}};U(e,b,L);var A=ha(L,[],d);D(t.scope)&&Zc(A);a=A.concat(a);$c(c,d)}else L=p,b.html(d);a.unshift(J);m=C(a,L,c,f,b,t,g,h,k);n(e,function(a,c){a==L&&(e[c]=b[0])});for(s=S(b[0].childNodes,f);l.length;){d=l.shift();w=l.shift();var M=l.shift(),V=l.shift(),A=b[0];if(!d.$$destroyed){if(w!==p){var ya=w.className;k.hasElementTranscludeDirective&&t.replace||(A=Ub(L));U(M,z(w),A);F(z(A),ya)}w=m.transcludeOnThisElement?P(d,m.transclude,V):V;m(s,d,A,e,w,m)}}l=null});return function(a,
b,c,d,e){a=e;b.$$destroyed||(l?l.push(b,c,d,a):(m.transcludeOnThisElement&&(a=P(b,m.transclude,e)),m(s,b,c,d,a,m)))}}function za(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 N(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 wa(a,c){var d=b(c,!0);d&&a.push({priority:0,compile:function(a){a=a.parent();var b=!!a.length;b&&V.$$addBindingClass(a);
return function(a,c){var e=c.parent();b||V.$$addBindingClass(e);V.$$addBindingInfo(e,d.expressions);a.$watch(d,function(a){c[0].nodeValue=a})}}})}function Xb(a,b){a=I(a||"html");switch(a){case "svg":case "math":var c=W.createElement("div");c.innerHTML="<"+a+">"+b+"</"+a+">";return c.childNodes[0].childNodes;default:return b}}function R(a,b){if("srcdoc"==b)return M.HTML;var c=ta(a);if("xlinkHref"==b||"form"==c&&"action"==b||"img"!=c&&("src"==b||"ngSrc"==b))return M.RESOURCE_URL}function X(a,c,d,e,
f){var g=R(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 m=h[e];m!==d&&(l=m&&b(m,!0,g,f),d=m);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 U(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=W.createDocumentFragment();a.appendChild(d);z.hasData(d)&&(z(c).data(z(d).data()),la?(Qb=!0,la.cleanData([d])):delete z.cache[d[z.expando]]);d=1;for(e=b.length;d<e;d++)f=b[d],z(f).remove(),a.appendChild(f),delete b[d];b[0]=c;b.length=1}function Z(a,b){return Q(function(){return a.apply(null,
arguments)},a,b)}function $(a,b,d,e,f,g){try{a(b,d,e,f,g)}catch(h){c(h,ua(d))}}function Y(a,c,d,e,f,g){var h;n(e,function(e,g){var k=e.attrName,l=e.optional,m,s,p,L;switch(e.mode){case "@":l||Na.call(c,k)||(d[g]=c[k]=void 0);c.$observe(k,function(a){H(a)&&(d[g]=a)});c.$$observers[k].$$scope=a;H(c[k])&&(d[g]=b(c[k])(a));break;case "=":if(!Na.call(c,k)){if(l)break;c[k]=void 0}if(l&&!c[k])break;s=w(c[k]);L=s.literal?ka:function(a,b){return a===b||a!==a&&b!==b};p=s.assign||function(){m=d[g]=s(a);throw ea("nonassign",
c[k],f.name);};m=d[g]=s(a);l=function(b){L(b,d[g])||(L(b,m)?p(a,b=d[g]):d[g]=b);return m=b};l.$stateful=!0;l=e.collection?a.$watchCollection(c[k],l):a.$watch(w(c[k],l),null,s.literal);h=h||[];h.push(l);break;case "&":s=c.hasOwnProperty(k)?w(c[k]):v;if(s===v&&l)break;d[g]=function(b){return s(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:va,$addClass:function(a){a&&0<a.length&&O.addClass(this.$$element,a)},$removeClass:function(a){a&&0<a.length&&O.removeClass(this.$$element,a)},$updateClass:function(a,b){var c=ad(a,b);c&&c.length&&O.addClass(this.$$element,c);(c=ad(b,a))&&c.length&&O.removeClass(this.$$element,c)},$set:function(a,b,d,e){var f=this.$$element[0],g=Qc(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=zc(a,"-"));g=ta(this.$$element);if("a"===g&&"href"===a||"img"===g&&"src"===a)this[a]=b=J(b,"src"===a);else if("img"===g&&"srcset"===a){for(var g="",h=T(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 m=2*l,g=g+J(T(h[m]),!0),g=g+(" "+T(h[m+1]));h=T(h[2*l]).split(/\s/);g+=J(T(h[0]),!0);2===h.length&&(g+=" "+T(h[1]));this[a]=b=g}!1!==d&&(null===b||b===u?this.$$element.removeAttr(e):this.$$element.attr(e,b));(a=this.$$observers)&&
n(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);A.$evalAsync(function(){e.$$inter||!c.hasOwnProperty(a)||y(c[a])||b(c[a])});return function(){bb(e,b)}}};var ca=b.startSymbol(),da=b.endSymbol(),fa="{{"==ca||"}}"==da?Za:function(a){return a.replace(/\{\{/g,ca).replace(/}}/g,da)},ia=/^ngAttr[A-Z]/;V.$$addBindingInfo=m?function(a,b){var c=a.data("$binding")||[];K(b)?c=c.concat(b):c.push(b);a.data("$binding",
c)}:v;V.$$addBindingClass=m?function(a){F(a,"ng-binding")}:v;V.$$addScopeInfo=m?function(a,b,c,d){a.data(c?d?"$isolateScopeNoTemplate":"$isolateScope":"$scope",b)}:v;V.$$addScopeClass=m?function(a,b){F(a,b?"ng-isolate-scope":"ng-scope")}:v;return V}]}function va(b){return fb(b.replace(Xc,""))}function ad(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 Yc(b){b=z(b);var a=b.length;
if(1>=a)return b;for(;a--;)8===b[a].nodeType&&Mf.call(b,a,1);return b}function Xe(){var b={},a=!1;this.register=function(a,d){Ta(a,"controller");D(a)?Q(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||!D(a.$scope))throw G("$controller")("noscp",d,b);a.$scope[b]=c}return function(f,g,h,l){var k,m,q;h=!0===h;l&&H(l)&&(q=l);if(H(f)){l=f.match(Vc);if(!l)throw Nf("ctrlfmt",f);m=l[1];q=q||l[3];f=b.hasOwnProperty(m)?b[m]:Bc(g.$scope,
m,!0)||(a?Bc(d,m,!0):u);Sa(f,m,!0)}if(h)return h=(K(f)?f[f.length-1]:f).prototype,k=Object.create(h||null),q&&e(g,q,k,m||f.name),Q(function(){var a=c.invoke(f,k,g,m);a!==k&&(D(a)||B(a))&&(k=a,q&&e(g,q,k,m||f.name));return k},{instance:k,identifier:q});k=c.instantiate(f,g,m);q&&e(g,q,k,m||f.name);return k}}]}function Ye(){this.$get=["$window",function(b){return z(b.document)}]}function Ze(){this.$get=["$log",function(b){return function(a,c){b.error.apply(b,arguments)}}]}function Yb(b){return D(b)?
ca(b)?b.toISOString():db(b):b}function df(){this.$get=function(){return function(b){if(!b)return"";var a=[];mc(b,function(b,d){null===b||y(b)||(K(b)?n(b,function(b,c){a.push(ma(d)+"="+ma(Yb(b)))}):a.push(ma(d)+"="+ma(Yb(b))))});return a.join("&")}}}function ef(){this.$get=function(){return function(b){function a(b,e,f){null===b||y(b)||(K(b)?n(b,function(b,c){a(b,e+"["+(D(b)?c:"")+"]")}):D(b)&&!ca(b)?mc(b,function(b,c){a(b,e+(f?"":"[")+c+(f?"":"]"))}):c.push(ma(e)+"="+ma(Yb(b))))}if(!b)return"";var c=
[];a(b,"",!0);return c.join("&")}}}function Zb(b,a){if(H(b)){var c=b.replace(Of,"").trim();if(c){var d=a("Content-Type");(d=d&&0===d.indexOf(bd))||(d=(d=c.match(Pf))&&Qf[d[0]].test(c));d&&(b=uc(c))}}return b}function cd(b){var a=ga(),c;H(b)?n(b.split("\n"),function(b){c=b.indexOf(":");var e=I(T(b.substr(0,c)));b=T(b.substr(c+1));e&&(a[e]=a[e]?a[e]+", "+b:b)}):D(b)&&n(b,function(b,c){var f=I(c),g=T(b);f&&(a[f]=a[f]?a[f]+", "+g:g)});return a}function dd(b){var a;return function(c){a||(a=cd(b));return c?
(c=a[I(c)],void 0===c&&(c=null),c):a}}function ed(b,a,c,d){if(B(d))return d(b,a,c);n(d,function(d){b=d(b,a,c)});return b}function cf(){var b=this.defaults={transformResponse:[Zb],transformRequest:[function(a){return D(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($b),put:ia($b),patch:ia($b)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",paramSerializer:"$httpParamSerializer"},
a=!1;this.useApplyAsync=function(b){return x(b)?(a=!!b,this):a};var c=!0;this.useLegacyPromiseExtensions=function(a){return x(a)?(c=!!a,this):c};var d=this.interceptors=[];this.$get=["$httpBackend","$$cookieReader","$cacheFactory","$rootScope","$q","$injector",function(e,f,g,h,l,k){function m(a){function d(a){var b=Q({},a);b.data=a.data?ed(a.data,a.headers,a.status,f.transformResponse):a.data;a=a.status;return 200<=a&&300>a?b:l.reject(b)}function e(a,b){var c,d={};n(a,function(a,e){B(a)?(c=a(b),null!=
c&&(d[e]=c)):d[e]=a});return d}if(!aa.isObject(a))throw G("$http")("badreq",a);var f=Q({method:"get",transformRequest:b.transformRequest,transformResponse:b.transformResponse,paramSerializer:b.paramSerializer},a);f.headers=function(a){var c=b.headers,d=Q({},a.headers),f,g,h,c=Q({},c.common,c[I(a.method)]);a:for(f in c){g=I(f);for(h in d)if(I(h)===g)continue a;d[f]=c[f]}return e(d,ia(a))}(a);f.method=rb(f.method);f.paramSerializer=H(f.paramSerializer)?k.get(f.paramSerializer):f.paramSerializer;var g=
[function(a){var c=a.headers,e=ed(a.data,dd(c),u,a.transformRequest);y(e)&&n(c,function(a,b){"content-type"===I(b)&&delete c[b]});y(a.withCredentials)&&!y(b.withCredentials)&&(a.withCredentials=b.withCredentials);return q(a,e).then(d,d)},u],h=l.when(f);for(n(E,function(a){(a.request||a.requestError)&&g.unshift(a.request,a.requestError);(a.response||a.responseError)&&g.push(a.response,a.responseError)});g.length;){a=g.shift();var m=g.shift(),h=h.then(a,m)}c?(h.success=function(a){Sa(a,"fn");h.then(function(b){a(b.data,
b.status,b.headers,f)});return h},h.error=function(a){Sa(a,"fn");h.then(null,function(b){a(b.data,b.status,b.headers,f)});return h}):(h.success=fd("success"),h.error=fd("error"));return h}function q(c,d){function g(b,c,d,e){function f(){k(c,b,d,e)}F&&(200<=b&&300>b?F.put(P,[b,c,cd(d),e]):F.remove(P));a?h.$applyAsync(f):(f(),h.$$phase||h.$apply())}function k(a,b,d,e){b=Math.max(b,0);(200<=b&&300>b?O.resolve:O.reject)({data:a,status:b,headers:dd(d),config:c,statusText:e})}function q(a){k(a.data,a.status,
ia(a.headers()),a.statusText)}function E(){var a=m.pendingRequests.indexOf(c);-1!==a&&m.pendingRequests.splice(a,1)}var O=l.defer(),J=O.promise,F,n,S=c.headers,P=s(c.url,c.paramSerializer(c.params));m.pendingRequests.push(c);J.then(E,E);!c.cache&&!b.cache||!1===c.cache||"GET"!==c.method&&"JSONP"!==c.method||(F=D(c.cache)?c.cache:D(b.cache)?b.cache:t);F&&(n=F.get(P),x(n)?n&&B(n.then)?n.then(q,q):K(n)?k(n[1],n[0],ia(n[2]),n[3]):k(n,200,{},"OK"):F.put(P,J));y(n)&&((n=gd(c.url)?f()[c.xsrfCookieName||
b.xsrfCookieName]:u)&&(S[c.xsrfHeaderName||b.xsrfHeaderName]=n),e(c.method,P,d,g,S,c.timeout,c.withCredentials,c.responseType));return J}function s(a,b){0<b.length&&(a+=(-1==a.indexOf("?")?"?":"&")+b);return a}var t=g("$http");b.paramSerializer=H(b.paramSerializer)?k.get(b.paramSerializer):b.paramSerializer;var E=[];n(d,function(a){E.unshift(H(a)?k.get(a):k.invoke(a))});m.pendingRequests=[];(function(a){n(arguments,function(a){m[a]=function(b,c){return m(Q({},c||{},{method:a,url:b}))}})})("get","delete",
"head","jsonp");(function(a){n(arguments,function(a){m[a]=function(b,c,d){return m(Q({},d||{},{method:a,url:b,data:c}))}})})("post","put","patch");m.defaults=b;return m}]}function Rf(){return new N.XMLHttpRequest}function ff(){this.$get=["$browser","$window","$document",function(b,a,c){return Sf(b,Rf,b.defer,a.angular.callbacks,c[0])}]}function Sf(b,a,c,d,e){function f(a,b,c){var f=e.createElement("script"),m=null;f.type="text/javascript";f.src=a;f.async=!0;m=function(a){f.removeEventListener("load",
m,!1);f.removeEventListener("error",m,!1);e.body.removeChild(f);f=null;var g=-1,t="unknown";a&&("load"!==a.type||d[b].called||(a={type:"error"}),t=a.type,g="error"===a.type?404:200);c&&c(g,t)};f.addEventListener("load",m,!1);f.addEventListener("error",m,!1);e.body.appendChild(f);return m}return function(e,h,l,k,m,q,s,t){function E(){p&&p();A&&A.abort()}function L(a,d,e,f,g){M!==u&&c.cancel(M);p=A=null;a(d,e,f,g);b.$$completeOutstandingRequest(v)}b.$$incOutstandingRequestCount();h=h||b.url();if("jsonp"==
I(e)){var w="_"+(d.counter++).toString(36);d[w]=function(a){d[w].data=a;d[w].called=!0};var p=f(h.replace("JSON_CALLBACK","angular.callbacks."+w),w,function(a,b){L(k,a,d[w].data,"",b);d[w]=v})}else{var A=a();A.open(e,h,!0);n(m,function(a,b){x(a)&&A.setRequestHeader(b,a)});A.onload=function(){var a=A.statusText||"",b="response"in A?A.response:A.responseText,c=1223===A.status?204:A.status;0===c&&(c=b?200:"file"==Aa(h).protocol?404:0);L(k,c,b,A.getAllResponseHeaders(),a)};e=function(){L(k,-1,null,null,
"")};A.onerror=e;A.onabort=e;s&&(A.withCredentials=!0);if(t)try{A.responseType=t}catch(r){if("json"!==t)throw r;}A.send(l)}if(0<q)var M=c(E,q);else q&&B(q.then)&&q.then(E)}}function af(){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(m,b).replace(q,a)}function h(f,h,m,q){function w(a){try{var b=a;a=m?e.getTrusted(m,
b):e.valueOf(b);var c;if(q&&!x(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))}}q=!!q;for(var p,n,r=0,M=[],O=[],J=f.length,F=[],V=[];r<J;)if(-1!=(p=f.indexOf(b,r))&&-1!=(n=f.indexOf(a,p+l)))r!==p&&F.push(g(f.substring(r,p))),r=f.substring(p+l,n),M.push(r),O.push(c(r,w)),r=n+k,V.push(F.length),F.push("");else{r!==J&&F.push(g(f.substring(r)));break}m&&1<F.length&&Ka.throwNoconcat(f);if(!h||M.length){var S=
function(a){for(var b=0,c=M.length;b<c;b++){if(q&&y(a[b]))return;F[V[b]]=a[b]}return F.join("")};return Q(function(a){var b=0,c=M.length,e=Array(c);try{for(;b<c;b++)e[b]=O[b](a);return S(e)}catch(g){d(Ka.interr(f,g))}},{exp:f,expressions:M,$$watchDelegate:function(a,b){var c;return a.$watchGroup(O,function(d,e){var f=S(d);B(b)&&b.call(this,f,d!==e?c:f,a);c=f})}})}}var l=b.length,k=a.length,m=new RegExp(b.replace(/./g,f),"g"),q=new RegExp(a.replace(/./g,f),"g");h.startSymbol=function(){return b};h.endSymbol=
function(){return a};return h}]}function bf(){this.$get=["$rootScope","$window","$q","$$q",function(b,a,c,d){function e(e,h,l,k){var m=4<arguments.length,q=m?xa.call(arguments,4):[],s=a.setInterval,t=a.clearInterval,E=0,L=x(k)&&!k,w=(L?d:c).defer(),p=w.promise;l=x(l)?l:0;p.then(null,null,m?function(){e.apply(null,q)}:e);p.$$intervalId=s(function(){w.notify(E++);0<l&&E>=l&&(w.resolve(E),t(p.$$intervalId),delete f[p.$$intervalId]);L||b.$apply()},h);f[p.$$intervalId]=w;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 ac(b){b=b.split("/");for(var a=b.length;a--;)b[a]=nb(b[a]);return b.join("/")}function hd(b,a){var c=Aa(b);a.$$protocol=c.protocol;a.$$host=c.hostname;a.$$port=Y(c.port)||Tf[c.protocol]||null}function id(b,a){var c="/"!==b.charAt(0);c&&(b="/"+b);var d=Aa(b);a.$$path=decodeURIComponent(c&&"/"===d.pathname.charAt(0)?d.pathname.substring(1):d.pathname);a.$$search=
xc(d.search);a.$$hash=decodeURIComponent(d.hash);a.$$path&&"/"!=a.$$path.charAt(0)&&(a.$$path="/"+a.$$path)}function ra(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 Cb(b){return b.replace(/(#.+)|#$/,"$1")}function bc(b,a,c){this.$$html5=!0;c=c||"";hd(b,this);this.$$parse=function(b){var c=ra(a,b);if(!H(c))throw Db("ipthprfx",b,a);id(c,this);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var b=
Pb(this.$$search),c=this.$$hash?"#"+nb(this.$$hash):"";this.$$url=ac(this.$$path)+(b?"?"+b:"")+c;this.$$absUrl=a+this.$$url.substr(1)};this.$$parseLinkUrl=function(d,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;(f=ra(b,d))!==u?(g=f,g=(f=ra(c,f))!==u?a+(ra("/",f)||f):b+g):(f=ra(a,d))!==u?g=a+f:a==d+"/"&&(g=a);g&&this.$$parse(g);return!!g}}function cc(b,a,c){hd(b,this);this.$$parse=function(d){var e=ra(b,d)||ra(a,d),f;y(e)||"#"!==e.charAt(0)?this.$$html5?f=e:(f="",y(e)&&(b=d,this.replace())):
(f=ra(c,e),y(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 a=Pb(this.$$search),e=this.$$hash?"#"+nb(this.$$hash):"";this.$$url=ac(this.$$path)+(a?"?"+a:"")+e;this.$$absUrl=b+(this.$$url?c+this.$$url:"")};this.$$parseLinkUrl=function(a,c){return Ja(b)==Ja(a)?(this.$$parse(a),!0):!1}}function jd(b,a,c){this.$$html5=!0;cc.apply(this,arguments);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=ra(a,d))?f=b+c+g:a===d+"/"&&(f=a);f&&this.$$parse(f);return!!f};this.$$compose=function(){var a=Pb(this.$$search),e=this.$$hash?"#"+nb(this.$$hash):"";this.$$url=ac(this.$$path)+(a?"?"+a:"")+e;this.$$absUrl=b+c+this.$$url}}function Eb(b){return function(){return this[b]}}function kd(b,a){return function(c){if(y(c))return this[b];this[b]=a(c);this.$$compose();return this}}function gf(){var b="",a={enabled:!1,requireBase:!0,
rewriteLinks:!0};this.hashPrefix=function(a){return x(a)?(b=a,this):b};this.html5Mode=function(b){return ab(b)?(a.enabled=b,this):D(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,m;m=d.baseHref();var q=d.url(),s;if(a.enabled){if(!m&&a.requireBase)throw Db("nobase");s=q.substring(0,q.indexOf("/",q.indexOf("//")+2))+(m||"/");m=e.history?bc:jd}else s=Ja(q),m=cc;var t=s.substr(0,Ja(s).lastIndexOf("/")+1);k=new m(s,t,"#"+b);k.$$parseLinkUrl(q,q);k.$$state=d.state();var E=/^\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=
z(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");D(h)&&"[object SVGAnimatedString]"===h.toString()&&(h=Aa(h.animVal).href);E.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))}});Cb(k.absUrl())!=Cb(q)&&d.url(k.absUrl(),!0);var L=!0;d.onUrlChange(function(a,b){y(ra(t,a))?g.location.href=a:(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)):(L=!1,l(d,e)))}),c.$$phase||c.$digest())});c.$watch(function(){var a=Cb(d.url()),b=Cb(k.absUrl()),f=d.state(),g=k.$$replace,m=a!==b||k.$$html5&&e.history&&f!==k.$$state;if(L||m)L=!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):(m&&h(b,g,f===k.$$state?null:k.$$state),l(a,f)))});k.$$replace=!1});return k}]}function hf(){var b=!0,a=this;this.debugEnabled=function(a){return x(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=
[];n(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 Wa(b,a){if("__defineGetter__"===b||"__defineSetter__"===b||"__lookupGetter__"===b||"__lookupSetter__"===b||"__proto__"===b)throw da("isecfld",a);return b}function Ba(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===Uf||b===Vf||b===Wf)throw da("isecff",a);}}function Xf(b,a){return"undefined"!==typeof b?b:a}function md(b,a){return"undefined"===typeof b?a:"undefined"===typeof a?b:b+a}function U(b,a){var c,d;switch(b.type){case r.Program:c=!0;n(b.body,function(b){U(b.expression,a);c=c&&b.expression.constant});b.constant=
c;break;case r.Literal:b.constant=!0;b.toWatch=[];break;case r.UnaryExpression:U(b.argument,a);b.constant=b.argument.constant;b.toWatch=b.argument.toWatch;break;case r.BinaryExpression:U(b.left,a);U(b.right,a);b.constant=b.left.constant&&b.right.constant;b.toWatch=b.left.toWatch.concat(b.right.toWatch);break;case r.LogicalExpression:U(b.left,a);U(b.right,a);b.constant=b.left.constant&&b.right.constant;b.toWatch=b.constant?[]:[b];break;case r.ConditionalExpression:U(b.test,a);U(b.alternate,a);U(b.consequent,
a);b.constant=b.test.constant&&b.alternate.constant&&b.consequent.constant;b.toWatch=b.constant?[]:[b];break;case r.Identifier:b.constant=!1;b.toWatch=[b];break;case r.MemberExpression:U(b.object,a);b.computed&&U(b.property,a);b.constant=b.object.constant&&(!b.computed||b.property.constant);b.toWatch=[b];break;case r.CallExpression:c=b.filter?!a(b.callee.name).$stateful:!1;d=[];n(b.arguments,function(b){U(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 r.AssignmentExpression:U(b.left,a);U(b.right,a);b.constant=b.left.constant&&b.right.constant;b.toWatch=[b];break;case r.ArrayExpression:c=!0;d=[];n(b.elements,function(b){U(b,a);c=c&&b.constant;b.constant||d.push.apply(d,b.toWatch)});b.constant=c;b.toWatch=d;break;case r.ObjectExpression:c=!0;d=[];n(b.properties,function(b){U(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 r.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:u}}function od(b){return b.type===r.Identifier||b.type===r.MemberExpression}function pd(b){if(1===b.body.length&&od(b.body[0].expression))return{type:r.AssignmentExpression,left:b.body[0].expression,right:{type:r.NGValueParameter},operator:"="}}function qd(b){return 0===b.body.length||1===b.body.length&&(b.body[0].expression.type===r.Literal||b.body[0].expression.type===r.ArrayExpression||
b.body[0].expression.type===r.ObjectExpression)}function rd(b,a){this.astBuilder=b;this.$filter=a}function sd(b,a){this.astBuilder=b;this.$filter=a}function Fb(b){return"constructor"==b}function dc(b){return B(b.valueOf)?b.valueOf():Yf.call(b)}function jf(){var b=ga(),a=ga();this.$get=["$filter",function(c){function d(a,b){return null==a||null==b?a===b:"object"===typeof a&&(a=dc(a),"object"===typeof a)?!1:a===b||a!==a&&b!==b}function e(a,b,c,e,f){var g=e.inputs,h;if(1===g.length){var k=d,g=g[0];return a.$watch(function(a){var b=
g(a);d(b,k)||(h=e(a,u,u,[b]),k=b&&dc(b));return h},b,c,f)}for(var l=[],m=[],q=0,n=g.length;q<n;q++)l[q]=d,m[q]=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=!d(k,l[c])))m[c]=k,l[c]=k&&dc(k)}b&&(h=e(a,u,u,m));return h},b,c,f)}function f(a,b,c,d){var e,f;return e=a.$watch(function(a){return d(a)},function(a,c,d){f=a;B(b)&&b.apply(this,arguments);x(a)&&d.$$postDigest(function(){x(f)&&e()})},c)}function g(a,b,c,d){function e(a){var b=!0;n(a,function(a){x(a)||
(b=!1)});return b}var f,g;return f=a.$watch(function(a){return d(a)},function(a,c,d){g=a;B(b)&&b.call(this,a,c,d);e(a)&&d.$$postDigest(function(){e(g)&&f()})},c)}function h(a,b,c,d){var e;return e=a.$watch(function(a){return d(a)},function(a,c,d){B(b)&&b.apply(this,arguments);e()},c)}function l(a,b){if(!b)return a;var c=a.$$watchDelegate,c=c!==g&&c!==f?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 x(e)?c:e};a.$$watchDelegate&&a.$$watchDelegate!==
e?c.$$watchDelegate=a.$$watchDelegate:b.$stateful||(c.$$watchDelegate=e,c.inputs=a.inputs?a.inputs:[a]);return c}var k=Fa().noUnsafeEval,m={csp:k,expensiveChecks:!1},q={csp:k,expensiveChecks:!0};return function(d,k,E){var n,w,p;switch(typeof d){case "string":p=d=d.trim();var r=E?a:b;n=r[p];n||(":"===d.charAt(0)&&":"===d.charAt(1)&&(w=!0,d=d.substring(2)),E=E?q:m,n=new ec(E),n=(new fc(n,c,E)).parse(d),n.constant?n.$$watchDelegate=h:w?n.$$watchDelegate=n.literal?g:f:n.inputs&&(n.$$watchDelegate=e),
r[p]=n);return l(n,k);case "function":return l(d,k);default:return v}}}]}function lf(){this.$get=["$rootScope","$exceptionHandler",function(b,a){return td(function(a){b.$evalAsync(a)},a)}]}function mf(){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=u;for(var f=0,g=e.length;f<g;++f){d=e[f][0];b=e[f][c.status];try{B(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=G("$q",TypeError);Q(d.prototype,{then:function(a,b,c){if(y(a)&&
y(b)&&y(c))return this;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)}});Q(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(D(b)||B(b))d=b&&b.then;B(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(B(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{B(c)&&(d=c())}catch(e){return l(e,!1)}return d&&B(d.then)?d.then(function(){return l(a,b)},function(a){return l(a,!1)}):l(a,b)},m=function(a,b,c,d){var e=new g;e.resolve(a);return e.promise.then(b,c,d)},q=function t(a){if(!B(a))throw h("norslvr",a);if(!(this instanceof t))return new t(a);var b=new g;
a(function(a){b.resolve(a)},function(a){b.reject(a)});return b.promise};q.defer=function(){return new g};q.reject=function(a){var b=new g;b.reject(a);return b.promise};q.when=m;q.resolve=m;q.all=function(a){var b=new g,c=0,d=K(a)?[]:{};n(a,function(a,e){c++;m(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 q}function vf(){this.$get=["$window","$timeout",function(b,a){var c=b.requestAnimationFrame||
b.webkitRequestAnimationFrame,d=b.cancelAnimationFrame||b.webkitCancelAnimationFrame||b.webkitCancelRequestAnimationFrame,e=!!c,f=e?function(a){var b=c(a);return function(){d(b)}}:function(b){var c=a(b,16.66,!1);return function(){a.cancel(c)}};f.supported=e;return f}]}function kf(){function b(a){function b(){this.$$watchers=this.$$nextSibling=this.$$childHead=this.$$childTail=null;this.$$listeners={};this.$$listenerCount={};this.$$watchersCount=0;this.$id=++mb;this.$$ChildScope=null}b.prototype=a;
return b}var a=10,c=G("$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 m(){this.$id=++mb;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 q(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 t(a,b,c){do a.$$listenerCount[c]-=b,0===a.$$listenerCount[c]&&delete a.$$listenerCount[c];while(a=a.$parent)}function r(){}function L(){for(;M.length;)try{M.shift()()}catch(a){g(a)}e=null}function w(){null===e&&(e=l.defer(function(){p.$apply(L)}))}m.prototype={constructor:m,$new:function(a,c){var d;c=c||this;a?(d=new m,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:r,get:f,exp:e||a,eq:!!c};d=null;B(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)});n(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(!y(e)){if(D(e))if(Da(e))for(f!==q&&(f=q,t=f.length=0,l++),a=e.length,t!==a&&(l++,f.length=t=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={},t=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)):(t++,f[b]=g,l++));if(t>a)for(b in l++,f)e.hasOwnProperty(b)||(t--,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,m=h(a,c),q=[],s={},p=!0,t=0;return this.$watch(m,
function(){p?(p=!1,b(e,e,d)):b(e,g,d);if(k)if(D(e))if(Da(e)){g=Array(e.length);for(var a=0;a<e.length;a++)g[a]=e[a]}else for(a in g={},e)Na.call(e,a)&&(g[a]=e[a]);else g=e})},$digest:function(){var b,f,h,k,m,s,t=a,n,w=[],C,M;q("$digest");l.$$checkUrlChange();this===p&&null!==e&&(l.defer.cancel(e),L());d=null;do{s=!1;for(n=this;u.length;){try{M=u.shift(),M.scope.$eval(M.expression,M.locals)}catch(v){g(v)}d=null}a:do{if(k=n.$$watchers)for(m=k.length;m--;)try{if(b=k[m])if((f=b.get(n))!==(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===r?f:h,n),5>t&&(C=4-t,w[C]||(w[C]=[]),w[C].push({msg:B(b.exp)?"fn: "+(b.exp.name||b.exp.toString()):b.exp,newVal:f,oldVal:h}));else if(b===d){s=!1;break a}}catch(y){g(y)}if(!(k=n.$$watchersCount&&n.$$childHead||n!==this&&n.$$nextSibling))for(;n!==this&&!(k=n.$$nextSibling);)n=n.$parent}while(n=k);if((s||u.length)&&!t--)throw p.$$phase=null,c("infdig",a,w);}while(s||u.length);for(p.$$phase=
null;x.length;)try{x.shift()()}catch(z){g(z)}},$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)t(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||u.length||l.defer(function(){u.length&&p.$digest()});u.push({scope:this,expression:a,locals:b})},$$postDigest:function(a){x.push(a)},
$apply:function(a){try{q("$apply");try{return this.$eval(a)}finally{p.$$phase=null}}catch(b){g(b)}finally{try{p.$digest()}catch(c){throw g(c),c;}}},$applyAsync:function(a){function b(){c.$eval(a)}var c=this;a&&M.push(b);w()},$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,t(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,m;do{d=e.$$listeners[a]||c;h.currentScope=e;l=0;for(m=d.length;l<m;l++)if(d[l])try{d[l].apply(null,k)}catch(q){g(q)}else d.splice(l,1),l--,m--;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 m,u=p.$$asyncQueue=[],x=p.$$postDigestQueue=[],M=p.$$applyAsyncQueue=[];return p}]}function ge(){var b=
/^\s*(https?|ftp|mailto|tel|file):/,a=/^\s*((https?|ftp|file|blob):|data:image\/)/;this.aHrefSanitizationWhitelist=function(a){return x(a)?(b=a,this):b};this.imgSrcSanitizationWhitelist=function(b){return x(b)?(a=b,this):a};this.$get=function(){return function(c,d){var e=d?a:b,f;f=Aa(c).href;return""===f||f.match(e)?c:"unsafe:"+f}}}function Zf(b){if("self"===b)return b;if(H(b)){if(-1<b.indexOf("***"))throw Ca("iwcard",b);b=ud(b).replace("\\*\\*",".*").replace("\\*","[^:/.?&;]*");return new RegExp("^"+
b+"$")}if(Oa(b))return new RegExp("^"+b.source+"$");throw Ca("imatcher");}function vd(b){var a=[];x(b)&&n(b,function(b){a.push(Zf(b))});return a}function of(){this.SCE_CONTEXTS=oa;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 Ca("unsafe");};c.has("$sanitize")&&(f=c.get("$sanitize"));var g=e(),h={};h[oa.HTML]=e(g);h[oa.CSS]=e(g);h[oa.URL]=e(g);h[oa.JS]=e(g);h[oa.RESOURCE_URL]=e(h[oa.URL]);return{trustAs:function(a,b){var c=h.hasOwnProperty(a)?h[a]:null;if(!c)throw Ca("icontext",a,b);if(null===b||b===
u||""===b)return b;if("string"!==typeof b)throw Ca("itype",a);return new c(b)},getTrusted:function(c,e){if(null===e||e===u||""===e)return e;var g=h.hasOwnProperty(c)?h[c]:null;if(g&&e instanceof g)return e.$$unwrapTrustedValue();if(c===oa.RESOURCE_URL){var g=Aa(e.toString()),q,s,t=!1;q=0;for(s=b.length;q<s;q++)if(d(b[q],g)){t=!0;break}if(t)for(q=0,s=a.length;q<s;q++)if(d(a[q],g)){t=!1;break}if(t)return e;throw Ca("insecurl",e.toString());}if(c===oa.HTML)return f(e);throw Ca("unsafe");},valueOf:function(a){return a instanceof
g?a.$$unwrapTrustedValue():a}}}]}function nf(){var b=!0;this.enabled=function(a){arguments.length&&(b=!!a);return b};this.$get=["$parse","$sceDelegate",function(a,c){if(b&&8>Va)throw Ca("iequirks");var d=ia(oa);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=Za);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;n(oa,function(a,b){var c=I(b);d[fb("parse_as_"+c)]=function(b){return e(a,b)};d[fb("get_trusted_"+c)]=function(b){return f(a,b)};d[fb("trust_as_"+c)]=function(b){return g(a,b)}});return d}]}function pf(){this.$get=["$window","$document",function(b,a){var c={},d=Y((/android (\d+)/.exec(I((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,m=!1;if(l){for(var q in l)if(k=
h.exec(q)){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);m=!!("animation"in l||g+"Animation"in l);!d||k&&m||(k=H(l.webkitTransition),m=H(l.webkitAnimation))}return{history:!(!b.history||!b.history.pushState||4>d||e),hasEvent:function(a){if("input"===a&&11>=Va)return!1;if(y(c[a])){var b=f.createElement("div");c[a]="on"+a in b}return c[a]},csp:Fa(),vendorPrefix:g,transitions:k,animations:m,android:d}}]}function rf(){this.$get=
["$templateCache","$http","$q","$sce",function(b,a,c,d){function e(f,g){e.totalPendingRequests++;H(f)&&b.get(f)||(f=d.getTrustedResourceUrl(f));var h=a.defaults&&a.defaults.transformResponse;K(h)?h=h.filter(function(a){return a!==Zb}):h===Zb&&(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 sf(){this.$get=["$rootScope","$browser","$location",function(b,a,c){return{findBindings:function(a,b,c){a=a.getElementsByClassName("ng-binding");var g=[];n(a,function(a){var d=aa.element(a).data("$binding");d&&n(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 tf(){this.$get=["$rootScope","$browser","$q","$$q","$exceptionHandler",function(b,a,c,d,e){function f(f,l,k){B(f)||(k=l,l=f,f=v);var m=xa.call(arguments,3),q=x(k)&&!k,s=(q?d:c).defer(),t=s.promise,n;n=a.defer(function(){try{s.resolve(f.apply(null,m))}catch(a){s.reject(a),e(a)}finally{delete g[t.$$timeoutId]}q||
b.$apply()},l);t.$$timeoutId=n;g[n]=s;return t}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 Aa(b){Va&&(Z.setAttribute("href",b),b=Z.href);Z.setAttribute("href",b);return{href:Z.href,protocol:Z.protocol?Z.protocol.replace(/:$/,""):"",host:Z.host,search:Z.search?Z.search.replace(/^\?/,""):"",hash:Z.hash?Z.hash.replace(/^#/,""):"",hostname:Z.hostname,port:Z.port,pathname:"/"===
Z.pathname.charAt(0)?Z.pathname:"/"+Z.pathname}}function gd(b){b=H(b)?Aa(b):b;return b.protocol===wd.protocol&&b.host===wd.host}function uf(){this.$get=qa(N)}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]===u&&(d[k]=a(g.substring(l+1))));return d}}function yf(){this.$get=xd}function Jc(b){function a(c,
d){if(D(c)){var e={};n(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",$f);a("json",ag);a("limitTo",bg);a("lowercase",cg);a("number",Ad);a("orderBy",Bd);a("uppercase",dg)}function $f(){return function(b,a,c){if(!Da(b)){if(null==b)return b;throw G("filter")("notarray",b);}var d;switch(gc(a)){case "function":break;case "boolean":case "null":case "number":case "string":d=
!0;case "object":a=eg(a,c,d);break;default:return b}return Array.prototype.filter.call(b,a)}}function eg(b,a,c){var d=D(b)&&"$"in b;!0===a?a=ka:B(a)||(a=function(a,b){if(y(a))return!1;if(null===a||null===b)return a===b;if(D(b)||D(a)&&!pc(a))return!1;a=I(""+a);b=I(""+b);return-1!==a.indexOf(b)});return function(e){return d&&!D(e)?La(e,b.$,a,!1):La(e,b,a,c)}}function La(b,a,c,d,e){var f=gc(b),g=gc(a);if("string"===g&&"!"===a.charAt(0))return!La(b,a.substring(1),c,d);if(K(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],!B(e)&&!y(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 gc(b){return null===b?"null":typeof b}function yd(b){var a=b.NUMBER_FORMATS;return function(b,d,e){y(d)&&(d=a.CURRENCY_SYM);y(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(D(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,m=[];g&&(l="\u221e");if(!g&&-1!==h.indexOf("e")){var q=h.match(/([\d\.]+)e(-?)(\d+)/);q&&"-"==q[2]&&q[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;
y(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]||"",q=0,s=a.lgSize,t=a.gSize;if(h.length>=s+t)for(q=h.length-s,k=0;k<q;k++)0===(q-k)%t&&0!==k&&(l+=c),l+=h.charAt(k);for(k=q;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);m.push(f?a.negPre:a.posPre,l,f?a.negSuf:a.posSuf);return m.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 $(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 hc(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=Y(b[9]+b[10]),g=Y(b[9]+b[11]));h.call(a,Y(b[1]),Y(b[2])-1,Y(b[3]));f=Y(b[4]||0)-f;g=Y(b[5]||0)-g;h=Y(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;H(c)&&(c=fg.test(c)?Y(c):a(c));X(c)&&(c=new Date(c));if(!ca(c)||!isFinite(c.getTime()))return c;for(;e;)(k=gg.exec(e))?(h=cb(h,k,1),e=h.pop()):(h.push(e),e=null);var m=c.getTimezoneOffset();f&&(m=vc(f,c.getTimezoneOffset()),c=Ob(c,f,!0));n(h,function(a){l=hg[a];g+=l?l(c,b.DATETIME_FORMATS,m):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function ag(){return function(b,a){y(a)&&(a=2);return db(b,a)}}function bg(){return function(b,
a,c){a=Infinity===Math.abs(Number(a))?Number(a):Y(a);if(isNaN(a))return b;X(b)&&(b=b.toString());if(!K(b)&&!H(b))return b;c=!c||isNaN(c)?0:Y(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=Za;if(B(a))h=a;else if(H(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(!Da(b))return b;K(e)||(e=[e]);0===e.length&&(e=["+"]);var g=a(e,f);g.push({get:function(){return{}},descending:f?-1:1});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(pc(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],t=0;c.type===f.type?c.value!==f.value&&(t=c.value<f.value?-1:1):t=c.type<f.type?-1:1;if(c=t*g[d].descending)break}return c});return b=b.map(function(a){return a.value})}}function Ma(b){B(b)&&(b={link:b});b.restrict=b.restrict||"AC";return qa(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=u;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(){n(g,function(a){a.$rollbackViewValue()})};f.$commitViewValue=function(){n(g,function(a){a.$commitViewValue()})};f.$addControl=function(a){Ta(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];n(f.$pending,function(b,c){f.$setValidity(c,null,a)});n(f.$error,function(b,c){f.$setValidity(c,null,a)});n(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,Xa);d.addClass(b,Jb);
f.$dirty=!0;f.$pristine=!1;h.$setDirty()};f.$setPristine=function(){d.setClass(b,Xa,Jb+" ng-submitted");f.$dirty=!1;f.$pristine=!0;f.$submitted=!1;n(g,function(a){a.$setPristine()})};f.$setUntouched=function(){n(g,function(a){a.$setUntouched()})};f.$setSubmitted=function(){d.addClass(b,"ng-submitted");f.$submitted=!0;h.$setSubmitted()}}function ic(b){b.$formatters.push(function(a){return b.$isEmpty(a)?a:a.toString()})}function ib(b,a,c,d,e,f){var g=I(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=T(e));(d.$viewValue!==e||""===e&&d.$$hasNativeValidators)&&d.$setViewValue(e,b)}};if(e.hasEvent("input"))a.on("input",l);else{var k,m=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||m(a,this,this.value)});if(e.hasEvent("paste"))a.on("paste cut",
m)}a.on("change",l);d.$render=function(){var b=d.$isEmpty(d.$viewValue)?"":d.$viewValue;a.val()!==b&&a.val(b)}}function Kb(b,a){return function(c,d){var e,f;if(ca(c))return c;if(H(c)){'"'==c.charAt(0)&&'"'==c.charAt(c.length-1)&&(c=c.substring(1,c.length-1));if(ig.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},n(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 jb(b,a,c,d){return function(e,f,g,h,l,k,m){function q(a){return a&&!(a.getTime&&a.getTime()!==a.getTime())}function s(a){return x(a)?ca(a)?a:c(a):u}Id(e,f,g,h);ib(e,f,g,h,l,k);var t=h&&h.$options&&h.$options.timezone,n;h.$$parserName=b;h.$parsers.push(function(b){return h.$isEmpty(b)?null:a.test(b)?(b=c(b,n),t&&(b=Ob(b,t)),b):u});h.$formatters.push(function(a){if(a&&
!ca(a))throw kb("datefmt",a);if(q(a))return(n=a)&&t&&(n=Ob(n,t,!0)),m("date")(a,d,t);n=null;return""});if(x(g.min)||g.ngMin){var r;h.$validators.min=function(a){return!q(a)||y(r)||c(a)>=r};g.$observe("min",function(a){r=s(a);h.$validate()})}if(x(g.max)||g.ngMax){var w;h.$validators.max=function(a){return!q(a)||y(w)||c(a)<=w};g.$observe("max",function(a){w=s(a);h.$validate()})}}}function Id(b,a,c,d){(d.$$hasNativeValidators=D(a[0].validity))&&d.$parsers.push(function(b){var c=a.prop("validity")||{};
return c.badInput&&!c.typeMismatch?u:b})}function Jd(b,a,c,d,e){if(x(d)){b=b(d);if(!b.constant)throw kb("constexpr",c,d);return b(a)}return e}function jc(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],m=0;m<b.length;m++)if(e==b[m])continue a;c.push(e)}return c}function e(a){var b=[];return K(a)?(n(a,function(a){b=b.concat(e(a))}),b):H(a)?a.split(" "):D(a)?(n(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=[];n(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(!m){var n=l(k,1);h.$addClass(n)}else if(!ka(b,m)){var r=e(m),n=d(k,r),k=d(r,k),n=l(n,1),k=l(k,-1);n&&n.length&&c.addClass(g,n);k&&k.length&&c.removeClass(g,k)}}m=ia(b)}var m;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?"-"+zc(b,"-"):"";a(lb+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[lb]=e.hasClass(lb));d.$setValidity=function(b,e,f){e===u?(d.$pending||(d.$pending={}),g(d.$pending,
b,f)):(d.$pending&&h(d.$pending,b,f),Ld(d.$pending)&&(d.$pending=u));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=u,c("",null)):(a(Md,!1),d.$valid=Ld(d.$error),d.$invalid=!d.$valid,c("",d.$valid));e=d.$pending&&d.$pending[b]?u: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 jg=/^\/(.+)\/([a-z]*)$/,
I=function(b){return H(b)?b.toLowerCase():b},Na=Object.prototype.hasOwnProperty,rb=function(b){return H(b)?b.toUpperCase():b},Va,z,la,xa=[].slice,Mf=[].splice,kg=[].push,sa=Object.prototype.toString,qc=Object.getPrototypeOf,Ea=G("ng"),aa=N.angular||(N.angular={}),Rb,mb=0;Va=W.documentMode;v.$inject=[];Za.$inject=[];var K=Array.isArray,sc=/^\[object (Uint8(Clamped)?)|(Uint16)|(Uint32)|(Int8)|(Int16)|(Int32)|(Float(32)|(64))Array\]$/,T=function(b){return H(b)?b.trim():b},ud=function(b){return b.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,
"\\$1").replace(/\x08/g,"\\x08")},Fa=function(){if(!x(Fa.rules)){var b=W.querySelector("[ng-csp]")||W.querySelector("[data-ng-csp]");if(b){var a=b.getAttribute("ng-csp")||b.getAttribute("data-ng-csp");Fa.rules={noUnsafeEval:!a||-1!==a.indexOf("no-unsafe-eval"),noInlineStyle:!a||-1!==a.indexOf("no-inline-style")}}else{b=Fa;try{new Function(""),a=!1}catch(c){a=!0}b.rules={noUnsafeEval:a,noInlineStyle:!1}}}return Fa.rules},ob=function(){if(x(ob.name_))return ob.name_;var b,a,c=Qa.length,d,e;for(a=0;a<
c;++a)if(d=Qa[a],b=W.querySelector("["+d.replace(":","\\:")+"jq]")){e=b.getAttribute(d+"jq");break}return ob.name_=e},Qa=["ng-","data-ng-","ng:","x-ng-"],be=/[A-Z]/g,Ac=!1,Qb,pa=1,Pa=3,fe={full:"1.4.5",major:1,minor:4,dot:5,codeName:"permanent-internship"};R.expando="ng339";var gb=R.cache={},Ef=1;R._data=function(b){return this.cache[b[this.expando]]||{}};var zf=/([\:\-\_]+(.))/g,Af=/^moz([A-Z])/,lg={mouseleave:"mouseout",mouseenter:"mouseover"},Tb=G("jqLite"),Df=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,Sb=/<|&#?\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 Ra=R.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=
!1;"complete"===W.readyState?setTimeout(a):(this.on("DOMContentLoaded",a),R(N).on("load",a))},toString:function(){var b=[];n(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return 0<=b?z(this[b]):z(this[this.length+b])},length:0,push:kg,sort:[].sort,splice:[].splice},Ab={};n("multiple selected checked disabled readOnly required open".split(" "),function(b){Ab[I(b)]=b});var Rc={};n("input select option textarea button form details".split(" "),function(b){Rc[b]=!0});var Sc=
{ngMinlength:"minlength",ngMaxlength:"maxlength",ngMin:"min",ngMax:"max",ngPattern:"pattern"};n({data:Vb,removeData:ub,hasData:function(b){for(var a in gb[b.ng339])return!0;return!1}},function(b,a){R[a]=b});n({data:Vb,inheritedData:zb,scope:function(b){return z.data(b,"$scope")||zb(b.parentNode||b,["$isolateScope","$scope"])},isolateScope:function(b){return z.data(b,"$isolateScope")||z.data(b,"$isolateScopeNoTemplate")},controller:Oc,injector:function(b){return zb(b,"$injector")},removeAttr:function(b,
a){b.removeAttribute(a)},hasClass:wb,css:function(b,a,c){a=fb(a);if(x(c))b.style[a]=c;else return b.style[a]},attr:function(b,a,c){var d=b.nodeType;if(d!==Pa&&2!==d&&8!==d)if(d=I(a),Ab[d])if(x(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:u;else if(x(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),null===b?u:b},prop:function(b,a,c){if(x(c))b[a]=c;else return b[a]},text:function(){function b(a,
b){if(y(b)){var d=a.nodeType;return d===pa||d===Pa?a.textContent:""}a.textContent=b}b.$dv="";return b}(),val:function(b,a){if(y(a)){if(b.multiple&&"select"===ta(b)){var c=[];n(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(y(a))return b.innerHTML;tb(b,!0);b.innerHTML=a},empty:Pc},function(b,a){R.prototype[a]=function(a,d){var e,f,g=this.length;if(b!==Pc&&(2==b.length&&b!==wb&&b!==Oc?a:d)===u){if(D(a)){for(e=0;e<
g;e++)if(b===Vb)b(this[e],a);else for(f in a)b(this[e],f,a[f]);return this}e=b.$dv;g=e===u?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}});n({removeData:ub,on:function a(c,d,e,f){if(x(f))throw Tb("onargs");if(Kc(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,lg[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:Nc,one:function(a,c,d){a=z(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);n(new R(c),function(c){d?e.insertBefore(c,d.nextSibling):e.replaceChild(c,a);d=c})},children:function(a){var c=[];n(a.childNodes,function(a){a.nodeType===pa&&c.push(a)});return c},contents:function(a){return a.contentDocument||a.childNodes||
[]},append:function(a,c){var d=a.nodeType;if(d===pa||11===d){c=new R(c);for(var d=0,e=c.length;d<e;d++)a.appendChild(c[d])}},prepend:function(a,c){if(a.nodeType===pa){var d=a.firstChild;n(new R(c),function(c){a.insertBefore(c,d)})}},wrap:function(a,c){c=z(c).eq(0).clone()[0];var d=a.parentNode;d&&d.replaceChild(c,a);c.appendChild(a)},remove:Wb,detach:function(a){Wb(a,!0)},after:function(a,c){var d=a,e=a.parentNode;c=new R(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&&n(c.split(" "),function(c){var f=d;y(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:Ub,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=Q(e,c)),c=ia(h),f=d?[e].concat(d):[e],n(c,function(c){e.isImmediatePropagationStopped()||c.apply(a,f)})}},function(a,c){R.prototype[c]=function(c,e,f){for(var g,h=0,l=this.length;h<l;h++)y(g)?(g=a(this[h],c,e,f),x(g)&&(g=z(g))):Mc(g,a(this[h],c,e,f));return x(g)?g:this};R.prototype.bind=
R.prototype.on;R.prototype.unbind=R.prototype.off});Ua.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 Ua}]}],Uc=/^[^\(]*\(\s*([^\)]*)\)/m,mg=/,/,ng=/^\s*(_?)(\S+?)\1\s*$/,Tc=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Ha=G("$injector");eb.$$annotate=function(a,c,d){var e;if("function"===typeof a){if(!(e=a.$inject)){e=[];if(a.length){if(c)throw H(d)&&
d||(d=a.name||If(a)),Ha("strictdi",d);c=a.toString().replace(Tc,"");c=c.match(Uc);n(c[1].split(mg),function(a){a.replace(ng,function(a,c,d){e.push(d)})})}a.$inject=e}}else K(a)?(c=a.length-1,Sa(a[c],"fn"),e=a.slice(0,c)):Sa(a,"fn",!0);return e};var Nd=G("$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 Ua,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));d=function(a,c){var d=!1;a&&(a=H(a)?a.split(" "):K(a)?a:[],n(a,function(a){a&&(d=!0,k[a]=c)}));return d};f=d(f,!0);l=d(l,!1);!f&&!l||1<c.length||e.$$postDigest(function(){n(c,function(c){var d=a.get(c);if(d){var e=Jf(c.attr("class")),f="",g="";n(d,function(a,c){a!==!!e[c]&&(a?f+=(f.length?" ":"")+c:g+=(g.length?" ":"")+c)});n(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}}}]},Re=["$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&&z(g);h=h&&z(h);g=g||h.parent();c(f,g,h);return a.push(f,"enter",Ia(l))},move:function(f,g,h,l){g=g&&z(g);h=h&&z(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=hb(h.addclass,e);return a.push(c,"addClass",h)},removeClass:function(c,e,h){h=Ia(h);h.removeClass=hb(h.removeClass,e);return a.push(c,"removeClass",h)},setClass:function(c,e,h,l){l=Ia(l);l.addClass=hb(l.addClass,
e);l.removeClass=hb(l.removeClass,h);return a.push(c,"setClass",l)},animate:function(c,e,h,l,k){k=Ia(k);k.from=k.from?Q(k.from,e):e;k.to=k.to?Q(k.to,h):h;k.tempClasses=hb(k.tempClasses,l||"ng-inline-animate");return a.push(c,"animate",k)}}}]}],Se=function(){this.$get=["$$rAF","$q",function(a,c){var d=function(){};d.prototype={done:function(a){this.defer&&this.defer[!0===a?"reject":"resolve"]()},end:function(){this.done()},cancel:function(){this.done(!0)},getPromise:function(){this.defer||(this.defer=
c.defer());return this.defer.promise},then:function(a,c){return this.getPromise().then(a,c)},"catch":function(a){return this.getPromise()["catch"](a)},"finally":function(a){return this.getPromise()["finally"](a)}};return function(c,f){function g(){a(function(){f.addClass&&(c.addClass(f.addClass),f.addClass=null);f.removeClass&&(c.removeClass(f.removeClass),f.removeClass=null);f.to&&(c.css(f.to),f.to=null);h||l.done();h=!0});return l}f.from&&(c.css(f.from),f.from=null);var h,l=new d;return{start:g,
end:g}}}]},ea=G("$compile");Cc.$inject=["$provide","$$sanitizeUriProvider"];var Xc=/^((?:x|data)[\:\-_])/i,Nf=G("$controller"),Vc=/^(\S+)(\s+as\s+(\w+))?$/,$e=function(){this.$get=["$document",function(a){return function(c){c?!c.nodeType&&c instanceof z&&(c=c[0]):c=a[0].body;return c.offsetWidth+1}}]},bd="application/json",$b={"Content-Type":bd+";charset=utf-8"},Pf=/^\[|^\{(?!\{)/,Qf={"[":/]$/,"{":/}$/},Of=/^\)\]\}',?\n/,og=G("$http"),fd=function(a){return function(){throw og("legacy",a);}},Ka=aa.$interpolateMinErr=
G("$interpolate");Ka.throwNoconcat=function(a){throw Ka("noconcat",a);};Ka.interr=function(a,c){return Ka("interr",a,c.toString())};var pg=/^([^\?#]*)(\?([^#]*))?(#(.*))?$/,Tf={http:80,https:443,ftp:21},Db=G("$location"),qg={$$html5:!1,$$replace:!1,absUrl:Eb("$$absUrl"),url:function(a){if(y(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:Eb("$$protocol"),host:Eb("$$host"),
port:Eb("$$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(H(a)||X(a))a=a.toString(),this.$$search=xc(a);else if(D(a))a=fa(a,{}),n(a,function(c,e){null==c&&delete a[e]}),this.$$search=a;else throw Db("isrcharg");break;default:y(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}};n([jd,cc,bc],function(a){a.prototype=Object.create(qg);a.prototype.state=function(c){if(!arguments.length)return this.$$state;if(a!==bc||!this.$$html5)throw Db("nostate");this.$$state=y(c)?null:c;return this}});var da=G("$parse"),Uf=Function.prototype.call,Vf=Function.prototype.apply,Wf=Function.prototype.bind,Lb=ga();n("+ - * / % === !== == != < > <= >= && || ! = |".split(" "),function(a){Lb[a]=!0});var rg={n:"\n",f:"\f",r:"\r",t:"\t",v:"\v",
"'":"'",'"':'"'},ec=function(a){this.options=a};ec.prototype={constructor:ec,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=Lb[c],f=Lb[d];Lb[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=x(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=I(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 r=function(a,c){this.lexer=a;this.options=c};r.Program="Program";r.ExpressionStatement="ExpressionStatement";r.AssignmentExpression="AssignmentExpression";r.ConditionalExpression="ConditionalExpression";r.LogicalExpression="LogicalExpression";r.BinaryExpression="BinaryExpression";r.UnaryExpression="UnaryExpression";r.CallExpression="CallExpression";r.MemberExpression="MemberExpression";r.Identifier="Identifier";r.Literal=
"Literal";r.ArrayExpression="ArrayExpression";r.Property="Property";r.ObjectExpression="ObjectExpression";r.ThisExpression="ThisExpression";r.NGValueParameter="NGValueParameter";r.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:r.Program,
body:a}},expressionStatement:function(){return{type:r.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:r.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:r.ConditionalExpression,test:a,alternate:c,consequent:d}):a},logicalOR:function(){for(var a=this.logicalAND();this.expect("||");)a={type:r.LogicalExpression,operator:"||",left:a,right:this.logicalAND()};return a},logicalAND:function(){for(var a=this.equality();this.expect("&&");)a={type:r.LogicalExpression,operator:"&&",left:a,right:this.equality()};return a},equality:function(){for(var a=this.relational(),c;c=this.expect("==","!=","===","!==");)a={type:r.BinaryExpression,
operator:c.text,left:a,right:this.relational()};return a},relational:function(){for(var a=this.additive(),c;c=this.expect("<",">","<=",">=");)a={type:r.BinaryExpression,operator:c.text,left:a,right:this.additive()};return a},additive:function(){for(var a=this.multiplicative(),c;c=this.expect("+","-");)a={type:r.BinaryExpression,operator:c.text,left:a,right:this.multiplicative()};return a},multiplicative:function(){for(var a=this.unary(),c;c=this.expect("*","/","%");)a={type:r.BinaryExpression,operator:c.text,
left:a,right:this.unary()};return a},unary:function(){var a;return(a=this.expect("+","-","!"))?{type:r.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:r.CallExpression,callee:a,arguments:this.parseArguments()},this.consume(")")):"["===c.text?(a={type:r.MemberExpression,object:a,property:this.expression(),computed:!0},this.consume("]")):"."===c.text?a={type:r.MemberExpression,object:a,property:this.identifier(),computed:!1}:this.throwError("IMPOSSIBLE");return a},filter:function(a){a=[a];for(var c={type:r.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:r.Identifier,name:a.text}},constant:function(){return{type:r.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:r.ArrayExpression,elements:a}},object:function(){var a=[],c;if("}"!==this.peekToken().text){do{if(this.peek("}"))break;c={type:r.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:r.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:r.Literal,value:!0},"false":{type:r.Literal,value:!1},"null":{type:r.Literal,value:null},undefined:{type:r.Literal,value:u},"this":{type:r.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:[]};U(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";n(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,Wa,Ba,ld,Xf,md,a);this.state=this.stage=u;f.literal=qd(e);f.constant=e.constant;return f},USE:"use",STRICT:"strict",watchFns:function(){var a=[],c=this.state.inputs,d=this;n(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;n(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,m,q;e=e||v;if(!g&&x(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 r.Program:n(a.body,function(c,d){k.recurse(c.expression,u,u,function(a){l=a});d!==a.body.length-1?k.current().body.push(l,";"):k.return_(l)});break;case r.Literal:q=this.escape(a.value);this.assign(c,q);e(q);break;case r.UnaryExpression:this.recurse(a.argument,u,u,function(a){l=a});q=a.operator+"("+this.ifDefined(l,0)+")";this.assign(c,q);e(q);break;case r.BinaryExpression:this.recurse(a.left,
u,u,function(a){h=a});this.recurse(a.right,u,u,function(a){l=a});q="+"===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,q);e(q);break;case r.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 r.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 r.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);Wa(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 r.MemberExpression:h=d&&(d.context=this.nextId())||this.nextId();c=c||this.nextId();k.recurse(a.object,h,u,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),"{}")),q=k.ensureSafeObject(k.computedMember(h,l)),k.assign(c,q),d&&(d.computed=!0,d.name=l);else{Wa(a.property.name);
f&&1!==f&&k.if_(k.not(k.nonComputedMember(h,a.property.name)),k.lazyAssign(k.nonComputedMember(h,a.property.name),"{}"));q=k.nonComputedMember(h,a.property.name);if(k.state.expensiveChecks||Fb(a.property.name))q=k.ensureSafeObject(q);k.assign(c,q);d&&(d.computed=!1,d.name=a.property.name)}},function(){k.assign(c,"undefined")});e(c)},!!f);break;case r.CallExpression:c=c||this.nextId();a.filter?(l=k.filter(a.callee.name),m=[],n(a.arguments,function(a){var c=k.nextId();k.recurse(a,c);m.push(c)}),q=l+
"("+m.join(",")+")",k.assign(c,q),e(c)):(l=k.nextId(),h={},m=[],k.recurse(a.callee,l,h,function(){k.if_(k.notNull(l),function(){k.addEnsureSafeFunction(l);n(a.arguments,function(a){k.recurse(a,k.nextId(),u,function(a){m.push(k.ensureSafeObject(a))})});h.name?(k.state.expensiveChecks||k.addEnsureSafeObject(h.context),q=k.member(h.context,h.name,h.computed)+"("+m.join(",")+")"):q=l+"("+m.join(",")+")";q=k.ensureSafeObject(q);k.assign(c,q)},function(){k.assign(c,"undefined")});e(c)}));break;case r.AssignmentExpression:l=
this.nextId();h={};if(!od(a.left))throw da("lval");this.recurse(a.left,u,h,function(){k.if_(k.notNull(h.context),function(){k.recurse(a.right,l);k.addEnsureSafeObject(k.member(h.context,h.name,h.computed));q=k.member(h.context,h.name,h.computed)+a.operator+l;k.assign(c,q);e(c||q)})},1);break;case r.ArrayExpression:m=[];n(a.elements,function(a){k.recurse(a,k.nextId(),u,function(a){m.push(a)})});q="["+m.join(",")+"]";this.assign(c,q);e(q);break;case r.ObjectExpression:m=[];n(a.properties,function(a){k.recurse(a.value,
k.nextId(),u,function(c){m.push(k.escape(a.key.type===r.Identifier?a.key.name:""+a.key.value)+":"+c)})});q="{"+m.join(",")+"}";this.assign(c,q);e(q);break;case r.ThisExpression:this.assign(c,"s");e("s");break;case r.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(H(a))return"'"+a.replace(this.stringEscapeRegex,this.stringEscapeFn)+"'";if(X(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;U(e,d.$filter);var f,g;if(f=pd(e))g=this.recurse(f);f=nd(e.body);var h;f&&(h=[],n(f,function(a,c){var e=d.recurse(a);
a.input=e;h.push(e);a.watchId=c}));var l=[];n(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;n(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 r.Literal:return this.value(a.value,c);case r.UnaryExpression:return f=
this.recurse(a.argument),this["unary"+a.operator](f,c);case r.BinaryExpression:return e=this.recurse(a.left),f=this.recurse(a.right),this["binary"+a.operator](e,f,c);case r.LogicalExpression:return e=this.recurse(a.left),f=this.recurse(a.right),this["binary"+a.operator](e,f,c);case r.ConditionalExpression:return this["ternary?:"](this.recurse(a.test),this.recurse(a.alternate),this.recurse(a.consequent),c);case r.Identifier:return Wa(a.name,g.expression),g.identifier(a.name,g.expensiveChecks||Fb(a.name),
c,d,g.expression);case r.MemberExpression:return e=this.recurse(a.object,!1,!!d),a.computed||(Wa(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 r.CallExpression:return h=[],n(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 n=
[],t=0;t<h.length;++t)n.push(h[t](a,d,e,g));a=f.apply(u,n,g);return c?{context:u,name:u,value:a}:a}:function(a,d,e,q){var n=f(a,d,e,q),t;if(null!=n.value){Ba(n.context,g.expression);ld(n.value,g.expression);t=[];for(var r=0;r<h.length;++r)t.push(Ba(h[r](a,d,e,q),g.expression));t=Ba(n.value.apply(n.context,t),g.expression)}return c?{value:t}:t};case r.AssignmentExpression:return e=this.recurse(a.left,!0,1),f=this.recurse(a.right),function(a,d,h,q){var n=e(a,d,h,q);a=f(a,d,h,q);Ba(n.value,g.expression);
n.context[n.name]=a;return c?{value:a}:a};case r.ArrayExpression:return h=[],n(a.elements,function(a){h.push(g.recurse(a))}),function(a,d,e,f){for(var g=[],n=0;n<h.length;++n)g.push(h[n](a,d,e,f));return c?{value:g}:g};case r.ObjectExpression:return h=[],n(a.properties,function(a){h.push({key:a.key.type===r.Identifier?a.key.name:""+a.key.value,value:g.recurse(a.value)})}),function(a,d,e,f){for(var g={},n=0;n<h.length;++n)g[h[n].key]=h[n].value(a,d,e,f);return c?{value:g}:g};case r.ThisExpression:return function(a){return c?
{value:a}:a};case r.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=x(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=x(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=(x(l)?l:0)-(x(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:u,name:u,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]:u;c&&Ba(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 m=a(g,h,l,k),q,n;null!=m&&(q=c(g,h,l,k),Wa(q,f),e&&1!==e&&m&&!m[q]&&(m[q]={}),n=m[q],Ba(n,f));return d?{context:m,name:q,value:n}:n}},nonComputedMember:function(a,c,d,e,f,g){return function(h,l,k,m){h=a(h,l,k,m);f&&1!==f&&h&&!h[c]&&(h[c]={});
l=null!=h?h[c]:u;(d||Fb(c))&&Ba(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 fc=function(a,c,d){this.lexer=a;this.$filter=c;this.options=d;this.ast=new r(this.lexer);this.astCompiler=d.csp?new sd(this.ast,c):new rd(this.ast,c)};fc.prototype={constructor:fc,parse:function(a){return this.astCompiler.compile(a,this.options.expensiveChecks)}};ga();ga();var Yf=Object.prototype.valueOf,Ca=G("$sce"),oa={HTML:"html",CSS:"css",URL:"url",
RESOURCE_URL:"resourceUrl",JS:"js"},ea=G("$compile"),Z=W.createElement("a"),wd=Aa(N.location.href);xd.$inject=["$document"];Jc.$inject=["$provide"];yd.$inject=["$locale"];Ad.$inject=["$locale"];var Dd=".",hg={yyyy:$("FullYear",4),yy:$("FullYear",2,0,!0),y:$("FullYear",1),MMMM:Hb("Month"),MMM:Hb("Month",!0),MM:$("Month",2,1),M:$("Month",1,1),dd:$("Date",2),d:$("Date",1),HH:$("Hours",2),H:$("Hours",1),hh:$("Hours",2,-12),h:$("Hours",1,-12),mm:$("Minutes",2),m:$("Minutes",1),ss:$("Seconds",2),s:$("Seconds",
1),sss:$("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:hc,GG:hc,GGG:hc,GGGG:function(a,c){return 0>=a.getFullYear()?c.ERANAMES[0]:c.ERANAMES[1]}},gg=/((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,fg=/^\-?\d+$/;zd.$inject=["$locale"];var cg=qa(I),dg=qa(rb);Bd.$inject=
["$parse"];var he=qa({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={};n(Ab,function(a,c){function d(a,d,f){a.$watch(f[e],function(a){f.$set(c,!!a)})}if("multiple"!=a){var e=va("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}}}});n(Sc,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(jg))){f.$set("ngPattern",new RegExp(e[1],e[2]));return}a.$watch(f[c],function(a){f.$set(c,a)})}}}});n(["src","srcset","href"],function(a){var c=va("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),Va&&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","$parse",function(c,d){function e(a){return""===a?d('this[""]').assign:d(a).assign||v}return{name:"form",restrict:a?"EAC":"E",controller:Gd,compile:function(d,
g){d.addClass(Xa).addClass(lb);var h=g.name?"name":a&&g.ngForm?"ngForm":!1;return{pre:function(a,d,f,g){if(!("action"in f)){var n=function(c){a.$apply(function(){g.$commitViewValue();g.$setSubmitted()});c.preventDefault()};d[0].addEventListener("submit",n,!1);d.on("$destroy",function(){c(function(){d[0].removeEventListener("submit",n,!1)},0,!1)})}var t=g.$$parentForm,r=h?e(g.$name):v;h&&(r(a,g),f.$observe(h,function(c){g.$name!==c&&(r(a,u),t.$$renameControl(g,c),r=e(g.$name),r(a,g))}));d.on("$destroy",
function(){t.$removeControl(g);r(a,u);Q(g,Ib)})}}}}}]},ie=Od(),ve=Od(!0),ig=/\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})?)?$/,kc=/^(\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){ib(a,c,d,e,f,g);ic(e)},date:jb("date",Pd,Kb(Pd,["yyyy","MM","dd"]),"yyyy-MM-dd"),"datetime-local":jb("datetimelocal",Qd,Kb(Qd,"yyyy MM dd HH mm ss sss".split(" ")),"yyyy-MM-ddTHH:mm:ss.sss"),time:jb("time",Sd,Kb(Sd,["HH","mm","ss","sss"]),"HH:mm:ss.sss"),week:jb("week",kc,function(a,c){if(ca(a))return a;if(H(a)){kc.lastIndex=0;var d=kc.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:jb("month",Rd,Kb(Rd,["yyyy","MM"]),"yyyy-MM"),number:function(a,c,d,e,f,g){Id(a,c,d,e);ib(a,c,d,e,f,g);e.$$parserName="number";e.$parsers.push(function(a){return e.$isEmpty(a)?null:ug.test(a)?parseFloat(a):u});e.$formatters.push(function(a){if(!e.$isEmpty(a)){if(!X(a))throw kb("numfmt",a);a=a.toString()}return a});if(x(d.min)||d.ngMin){var h;e.$validators.min=
function(a){return e.$isEmpty(a)||y(h)||a>=h};d.$observe("min",function(a){x(a)&&!X(a)&&(a=parseFloat(a,10));h=X(a)&&!isNaN(a)?a:u;e.$validate()})}if(x(d.max)||d.ngMax){var l;e.$validators.max=function(a){return e.$isEmpty(a)||y(l)||a<=l};d.$observe("max",function(a){x(a)&&!X(a)&&(a=parseFloat(a,10));l=X(a)&&!isNaN(a)?a:u;e.$validate()})}},url:function(a,c,d,e,f,g){ib(a,c,d,e,f,g);ic(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){ib(a,c,d,e,f,g);ic(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){y(d.name)&&c.attr("name",++mb);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),m=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:m})},hidden:v,button:v,submit:v,reset:v,file:v},Dc=["$browser","$sniffer","$filter","$parse",function(a,c,d,e){return{restrict:"E",require:["?ngModel"],link:{pre:function(f,g,h,l){l[0]&&(Td[I(h.type)]||Td.text)(f,g,h,l[0],c,a,d,e)}}}}],vg=/^(true|false|\d+)$/,Ne=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)})}}}},ne=["$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===u?"":a})}}}}],pe=["$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===u?"":a})}}}}],oe=["$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))||"")})}}}}],Me=qa({restrict:"A",require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),
qe=jc("",!0),se=jc("Odd",0),re=jc("Even",1),te=Ma({compile:function(a,c){c.$set("ngCloak",u);a.removeClass("ng-cloak")}}),ue=[function(){return{restrict:"A",scope:!0,controller:"@",priority:500}}],Ic={},wg={blur:!0,focus:!0};n("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),function(a){var c=va("ng-"+a);Ic[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 xe=["$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++]=W.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))})}}}],ye=["$templateRequest","$anchorScroll","$animate",function(a,c,d){return{restrict:"ECA",priority:400,terminal:!0,transclude:"element",controller:aa.noop,compile:function(e,f){var g=f.ngInclude||f.src,h=f.onload||"",l=f.autoscroll;return function(e,f,n,s,r){var u=0,v,w,p,A=function(){w&&(w.remove(),w=null);v&&(v.$destroy(),v=null);p&&(d.leave(p).then(function(){w=null}),w=p,p=null)};e.$watch(g,function(g){var n=function(){!x(l)||l&&!e.$eval(l)||
c()},q=++u;g?(a(g,!0).then(function(a){if(q===u){var c=e.$new();s.template=a;a=r(c,function(a){A();d.enter(a,null,f).then(n)});v=c;p=a;v.$emit("$includeContentLoaded",g);e.$eval(h)}},function(){q===u&&(A(),e.$emit("$includeContentError",g))}),e.$emit("$includeContentRequested",g)):(A(),s.template=null)})}}}}],Pe=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(c,d,e,f){/SVG/.test(d[0].toString())?(d.empty(),a(Lc(f.template,W).childNodes)(c,function(a){d.append(a)},
{futureParentElement:d})):(d.html(f.template),a(d.contents())(c))}}}],ze=Ma({priority:450,compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),Le=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?T(f):f;e.$parsers.push(function(a){if(!y(a)){var c=[];a&&n(a.split(h),function(a){a&&c.push(g?T(a):a)});return c}});e.$formatters.push(function(a){return K(a)?a.join(f):u});e.$isEmpty=function(a){return!a||
!a.length}}}},lb="ng-valid",Kd="ng-invalid",Xa="ng-pristine",Jb="ng-dirty",Md="ng-pending",kb=G("ngModel"),xg=["$scope","$exceptionHandler","$attrs","$element","$parse","$animate","$timeout","$rootScope","$q","$interpolate",function(a,c,d,e,f,g,h,l,k,m){this.$modelValue=this.$viewValue=Number.NaN;this.$$rawModelValue=u;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=u;this.$name=m(d.name||"",!1)(a);var q=f(d.ngModel),s=q.assign,r=q,E=s,L=null,w,p=this;this.$$setOptions=function(a){if((p.$options=a)&&a.getterSetter){var c=f(d.ngModel+"()"),g=f(d.ngModel+"($$$p)");r=function(a){var d=q(a);B(d)&&(d=c(a));return d};E=function(a,c){B(q(a))?g(a,{$$$p:p.$modelValue}):s(a,p.$modelValue)}}else if(!q.assign)throw kb("nonassign",d.ngModel,ua(e));};this.$render=v;this.$isEmpty=function(a){return y(a)||
""===a||null===a||a!==a};var A=e.inheritedData("$formController")||Ib,z=0;Hd({ctrl:this,$element:e,set:function(a,c){a[c]=!0},unset:function(a,c){delete a[c]},parentForm:A,$animate:g});this.$setPristine=function(){p.$dirty=!1;p.$pristine=!0;g.removeClass(e,Jb);g.addClass(e,Xa)};this.$setDirty=function(){p.$dirty=!0;p.$pristine=!1;g.removeClass(e,Xa);g.addClass(e,Jb);A.$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(L);p.$viewValue=p.$$lastCommittedViewValue;p.$render()};this.$validate=function(){if(!X(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:u,p.$modelValue!==d&&p.$$writeModelToScope())})}};this.$$runValidators=
function(a,c,d){function e(){var d=!0;n(p.$validators,function(e,f){var h=e(a,c);d=d&&h;g(f,h)});return d?!0:(n(p.$asyncValidators,function(a,c){g(c,null)}),!1)}function f(){var d=[],e=!0;n(p.$asyncValidators,function(f,h){var k=f(a,c);if(!k||!B(k.then))throw kb("$asyncValidators",k);g(h,u);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===z&&p.$setValidity(a,c)}function h(a){l===z&&d(a)}z++;var l=z;(function(){var a=
p.$$parserName||"parse";if(w===u)g(a,null);else return w||(n(p.$validators,function(a,c){g(c,null)}),n(p.$asyncValidators,function(a,c){g(c,null)})),g(a,w),w;return!0})()?e()?f():h(!1):h(!1)};this.$commitViewValue=function(){var a=p.$viewValue;h.cancel(L);if(p.$$lastCommittedViewValue!==a||""===a&&p.$$hasNativeValidators)p.$$lastCommittedViewValue=a,p.$pristine&&this.$setDirty(),this.$$parseAndValidate()};this.$$parseAndValidate=function(){var c=p.$$lastCommittedViewValue;if(w=y(c)?u:!0)for(var d=
0;d<p.$parsers.length;d++)if(c=p.$parsers[d](c),y(c)){w=!1;break}X(p.$modelValue)&&isNaN(p.$modelValue)&&(p.$modelValue=r(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:u,p.$modelValue!==e&&p.$$writeModelToScope())})};this.$$writeModelToScope=function(){E(a,p.$modelValue);n(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&&x(e.debounce)&&(e=e.debounce,X(e)?d=e:X(e[c])?d=e[c]:X(e["default"])&&(d=e["default"]));h.cancel(L);d?L=h(function(){p.$commitViewValue()},d):l.$$phase?p.$commitViewValue():a.$apply(function(){p.$commitViewValue()})};a.$watch(function(){var c=r(a);if(c!==p.$modelValue&&(p.$modelValue===p.$modelValue||c===c)){p.$modelValue=
p.$$rawModelValue=c;w=u;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})}],Ke=["$rootScope",function(a){return{restrict:"A",require:["ngModel","^?form","^?ngModelOptions"],controller:xg,priority:1,compile:function(c){c.addClass(Xa).addClass("ng-untouched").addClass(lb);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+|$)/,Oe=function(){return{restrict:"A",controller:["$scope","$attrs",function(a,c){var d=this;this.$options=fa(a.$eval(c.ngModelOptions));
this.$options.updateOn!==u?(this.$options.updateOnDefault=!1,this.$options.updateOn=T(this.$options.updateOn.replace(yg,function(){d.$options.updateOnDefault=!0;return" "}))):this.$options.updateOnDefault=!0}]}},Ae=Ma({terminal:!0,priority:1E3}),zg=G("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]+?))?$/,
Ie=["$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 m(a){var c;if(!r&&Da(a))c=a;else{c=[];for(var d in a)a.hasOwnProperty(d)&&"$"!==d.charAt(0)&&c.push(d)}return c}var n=a.match(Ag);if(!n)throw zg("iexp",a,ua(d));var s=n[5]||n[7],r=n[6];a=/ as /.test(n[0])&&n[1];var u=n[9];d=c(n[2]?n[1]:s);var v=a&&c(a)||d,w=u&&c(u),p=u?function(a,c){return w(e,c)}:function(a){return Ga(a)},A=function(a,
c){return p(a,B(a,c))},x=c(n[2]||n[1]),z=c(n[3]||""),y=c(n[4]||""),J=c(n[8]),F={},B=r?function(a,c){F[r]=c;F[s]=a;return F}:function(a){F[s]=a;return F};return{trackBy:u,getTrackByValue:A,getWatchables:c(J,function(a){var c=[];a=a||[];for(var d=m(a),f=d.length,g=0;g<f;g++){var h=a===d?g:d[g],k=B(a[h],h),h=p(a[h],k);c.push(h);if(n[2]||n[1])h=x(e,k),c.push(h);n[4]&&(k=y(e,k),c.push(k))}return c}),getOptions:function(){for(var a=[],c={},d=J(e)||[],g=m(d),h=g.length,n=0;n<h;n++){var q=d===g?n:g[n],s=
B(d[q],q),r=v(e,s),q=p(r,s),t=x(e,s),w=z(e,s),s=y(e,s),r=new f(q,r,t,w,s);a.push(r);c[q]=r}return{items:a,selectValueMap:c,getOptionFromViewValue:function(a){return c[A(a)]},getViewValueFromOption:function(a){return u?aa.copy(a.viewValue):a.viewValue}}}}}var e=W.createElement("option"),f=W.createElement("optgroup");return{restrict:"A",terminal:!0,require:["select","?ngModel"],link:function(c,h,l,k){function m(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 q(a,c,d,e){c&&I(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,Wb(a),a=c}function r(a){var c=p&&p[0],d=J&&J[0];if(c||d)for(;a&&(a===c||a===d);)a=a.nextSibling;return a}function u(){var a=F&&w.readValue();F=D.getOptions();var c={},d=h[0].firstChild;O&&h.prepend(p);d=r(d);F.items.forEach(function(a){var g,k;a.group?(g=c[a.group],g||(g=q(h[0],d,"optgroup",f),d=
g.nextSibling,g.label=a.group,g=c[a.group]={groupElement:g,currentOptionElement:g.firstChild}),k=q(g.groupElement,g.currentOptionElement,"option",e),m(a,k),g.currentOptionElement=k.nextSibling):(k=q(h[0],d,"option",e),m(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=w.readValue();(D.trackBy?ka(a,g):a===g)||(v.$setViewValue(g),v.$render())}}var v=k[1];if(v){var w=k[0];k=l.multiple;for(var p,x=0,y=h.children(),B=y.length;x<
B;x++)if(""===y[x].value){p=y.eq(x);break}var O=!!p,J=z(e.cloneNode(!1));J.val("?");var F,D=d(l.ngOptions,h,c);k?(v.$isEmpty=function(a){return!a||0===a.length},w.writeValue=function(a){F.items.forEach(function(a){a.element.selected=!1});a&&a.forEach(function(a){(a=F.getOptionFromViewValue(a))&&!a.disabled&&(a.element.selected=!0)})},w.readValue=function(){var a=h.val()||[],c=[];n(a,function(a){(a=F.selectValueMap[a])&&!a.disabled&&c.push(F.getViewValueFromOption(a))});return c},D.trackBy&&c.$watchCollection(function(){if(K(v.$viewValue))return v.$viewValue.map(function(a){return D.getTrackByValue(a)})},
function(){v.$render()})):(w.writeValue=function(a){var c=F.getOptionFromViewValue(a);c&&!c.disabled?h[0].value!==c.selectValue&&(J.remove(),O||p.remove(),h[0].value=c.selectValue,c.element.selected=!0,c.element.setAttribute("selected","selected")):null===a||O?(J.remove(),O||h.prepend(p),h.val(""),p.prop("selected",!0),p.attr("selected",!0)):(O||p.remove(),h.prepend(J),h.val("?"),J.prop("selected",!0),J.attr("selected",!0))},w.readValue=function(){var a=F.selectValueMap[h.val()];return a&&!a.disabled?
(O||p.remove(),J.remove(),F.getViewValueFromOption(a)):null},D.trackBy&&c.$watch(function(){return D.getTrackByValue(v.$viewValue)},function(){v.$render()}));O?(p.remove(),a(p)(c),p.removeClass("ng-scope")):p=z(e.cloneNode(!1));u();c.$watchCollection(D.getWatchables,u)}}}}],Be=["$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 m=l.count,q=l.$attr.when&&h.attr(l.$attr.when),s=l.offset||0,r=g.$eval(q)||{},u=
{},x=c.startSymbol(),w=c.endSymbol(),p=x+m+"-"+s+w,A=aa.noop,z;n(l,function(a,c){var d=f.exec(c);d&&(d=(d[1]?"-":"")+I(d[2]),r[d]=h.attr(l.$attr[c]))});n(r,function(a,d){u[d]=c(a.replace(e,p))});g.$watch(m,function(c){var e=parseFloat(c),f=isNaN(e);f||e in r||(e=a.pluralCat(e-s));e===z||f&&X(z)&&isNaN(z)||(A(),f=u[e],y(f)?(null!=c&&d.debug("ngPluralize: no rule defined for '"+e+"' in "+q),A=v,k()):A=g.$watch(f,k),z=e)})}}}],Ce=["$parse","$animate",function(a,c){var d=G("ngRepeat"),e=function(a,c,
d,e,k,m,n){a[d]=e;k&&(a[k]=m);a.$index=c;a.$first=0===c;a.$last=c===n-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=W.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 m=k[1],q=k[2],r=k[3],t=k[4],k=m.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
if(!k)throw d("iidexp",m);var v=k[3]||k[1],x=k[2];if(r&&(!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(r)||/^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(r)))throw d("badident",r);var w,p,A,y,B={$id:Ga};t?w=a(t):(A=function(a,c){return Ga(c)},y=function(a){return a});return function(a,f,g,k,m){w&&(p=function(c,d,e){x&&(B[x]=c);B[v]=d;B.$index=e;return w(a,B)});var t=ga();a.$watchCollection(q,function(g){var k,q,w=f[0],B,D=ga(),F,H,K,G,M,I,N;r&&(a[r]=g);if(Da(g))M=
g,q=p||A;else for(N in q=p||y,M=[],g)g.hasOwnProperty(N)&&"$"!==N.charAt(0)&&M.push(N);F=M.length;N=Array(F);for(k=0;k<F;k++)if(H=g===M?k:M[k],K=g[H],G=q(H,K,k),t[G])I=t[G],delete t[G],D[G]=I,N[k]=I;else{if(D[G])throw n(N,function(a){a&&a.scope&&(t[a.id]=a)}),d("dupes",h,G,K);N[k]={id:G,scope:u,clone:u};D[G]=!0}for(B in t){I=t[B];G=qb(I.clone);c.leave(G);if(G[0].parentNode)for(k=0,q=G.length;k<q;k++)G[k].$$NG_REMOVED=!0;I.scope.$destroy()}for(k=0;k<F;k++)if(H=g===M?k:M[k],K=g[H],I=N[k],I.scope){B=
w;do B=B.nextSibling;while(B&&B.$$NG_REMOVED);I.clone[0]!=B&&c.move(qb(I.clone),null,z(w));w=I.clone[I.clone.length-1];e(I.scope,k,v,K,x,H,F)}else m(function(a,d){I.scope=d;var f=l.cloneNode(!1);a[a.length++]=f;c.enter(a,null,z(w));w=f;I.clone=a;D[I.id]=I;e(I.scope,k,v,K,x,H,F)});t=D})}}}}],De=["$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"})})}}}],we=["$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"})})}}}],Ee=Ma(function(a,c,d){a.$watch(d.ngStyle,function(a,d){d&&a!==d&&n(d,function(a,d){c.css(d,"")});a&&c.css(a)},!0)}),Fe=["$animate",function(a){return{require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(c,d,e,f){var g=[],h=[],l=[],k=[],m=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 r=qb(h[d].clone);k[d].$destroy();(l[d]=a.leave(r)).then(m(l,d))}h.length=0;k.length=0;(g=f.cases["!"+c]||f.cases["?"])&&n(g,function(c){c.transclude(function(d,e){k.push(e);var f=c.element;d[d.length++]=W.createComment(" end ngSwitchWhen: ");h.push({clone:d});a.enter(d,f.parent(),f)})})})}}}],Ge=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})}}),He=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})}}),Je=Ma({restrict:"EAC",link:function(a,c,d,e,f){if(!f)throw G("ngTransclude")("orphan",ua(c));f(function(a){c.empty();c.append(a)})}}),je=["$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 Ua;e.ngModelCtrl=Bg;e.unknownOption=z(W.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){Ta(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=u)):f.put(a,c-1))};e.hasOption=function(a){return!!f.get(a)}}],
ke=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=[];n(c.find("option"),function(c){c.selected&&a.push(c.value)});return a};g.writeValue=function(a){var d=new Ua(a);n(c.find("option"),function(a){a.selected=x(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}}}}}},me=["$interpolate",function(a){function c(a){a[0].hasAttribute("selected")&&(a[0].selected=!0)}return{restrict:"E",priority:100,compile:function(d,e){if(y(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()}))}}}}],le=qa({restrict:"E",terminal:!1}),Fc=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()}))}}},
Ec=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){H(a)&&0<a.length&&(a=new RegExp("^"+a+"$"));if(a&&!a.test)throw G("ngPattern")("noregexp",g,a,ua(c));f=a||u;e.$validate()});e.$validators.pattern=function(a,c){return e.$isEmpty(c)||y(f)||f.test(c)}}}}},Hc=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){if(e){var f=-1;d.$observe("maxlength",function(a){a=Y(a);f=isNaN(a)?-1:a;e.$validate()});
e.$validators.maxlength=function(a,c){return 0>f||e.$isEmpty(c)||c.length<=f}}}}},Gc=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){if(e){var f=0;d.$observe("minlength",function(a){f=Y(a)||0;e.$validate()});e.$validators.minlength=function(a,c){return e.$isEmpty(c)||c.length>=f}}}}};N.angular.bootstrap?console.log("WARNING: Tried to load angular more than once."):(ce(),ee(aa),aa.module("ngLocale",[],["$provide",function(a){function c(a){a+="";var c=a.indexOf(".");return-1==
c?0:a.length-c-1}a.value("$locale",{DATETIME_FORMATS:{AMPMS:["AM","PM"],DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),ERANAMES:["Before Christ","Anno Domini"],ERAS:["BC","AD"],FIRSTDAYOFWEEK:6,MONTH:"January February March April May June July August September October November December".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),WEEKENDRANGE:[5,6],fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",
medium:"MMM d, y h:mm:ss a",mediumDate:"MMM d, y",mediumTime:"h:mm:ss a","short":"M/d/yy h:mm a",shortDate:"M/d/yy",shortTime:"h:mm a"},NUMBER_FORMATS:{CURRENCY_SYM:"$",DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{gSize:3,lgSize:3,maxFrac:3,minFrac:0,minInt:1,negPre:"-",negSuf:"",posPre:"",posSuf:""},{gSize:3,lgSize:3,maxFrac:2,minFrac:2,minInt:1,negPre:"-\u00a4",negSuf:"",posPre:"\u00a4",posSuf:""}]},id:"en-us",pluralCat:function(a,e){var f=a|0,g=e;u===g&&(g=Math.min(c(a),3));Math.pow(10,g);return 1==
f&&0==g?"one":"other"}})}]),z(W).ready(function(){Zd(W,yc)}))})(window,document);!window.angular.$$csp().noInlineStyle&&window.angular.element(document.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>');

Просмотреть файл

@ -1,19 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
$(function() {
var authAjax = function(url, action) {
return $.ajax({
url: url + '?ajax=1',
success: function(res, status, xhr) {
return location.reload(true);
},
error: function(res, status, xhr) {
alertify.error(action + " failure: " + status);
}
});
}
$('button#login').click(function() { authAjax('/userauth/login', 'login'); });
$('button#logout').click(function() { authAjax('/userauth/logout', 'logout'); });
});

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,28 +0,0 @@
{
"name": "Mozilla RelEng Services Frontend",
"description": "Code for",
"repository": {
"url": "https://github.com/mozilla/release-services",
"license": "MPL2"
},
"bugs": {
"open": "https://github.com/mozilla/release-services/issues?q=is%3Aissue+is%3Aopen+label%3A%224.app%3A+releng_frontend%22",
"good-first-bug": "https://github.com/mozilla/release-services/issues?q=is%3Aissue+is%3Aopen+label%3A%224.app%3A+releng_frontend%22"
},
"urls": {
"production": "https://mozilla-releng.net",
"staging": "https://staging.mozilla-releng.net"
},
"participate": {
"docs": "https://docs.mozilla-releng.net",
"irc": "#releng",
"irc-contacts": [ "garbas" ]
},
"keywords": [
"Bootstrap",
"Font-Awesome",
"Fira",
"Elm"
]
}

Двоичные данные
src/releng_frontend/src/static/favicon.ico

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 5.3 KiB

Двоичный файл не отображается.

Просмотреть файл

@ -1,229 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata></metadata>
<defs>
<font id="glyphicons_halflingsregular" horiz-adv-x="1200" >
<font-face units-per-em="1200" ascent="960" descent="-240" />
<missing-glyph horiz-adv-x="500" />
<glyph />
<glyph />
<glyph unicode="&#xd;" />
<glyph unicode=" " />
<glyph unicode="*" d="M100 500v200h259l-183 183l141 141l183 -183v259h200v-259l183 183l141 -141l-183 -183h259v-200h-259l183 -183l-141 -141l-183 183v-259h-200v259l-183 -183l-141 141l183 183h-259z" />
<glyph unicode="+" d="M0 400v300h400v400h300v-400h400v-300h-400v-400h-300v400h-400z" />
<glyph unicode="&#xa0;" />
<glyph unicode="&#x2000;" horiz-adv-x="652" />
<glyph unicode="&#x2001;" horiz-adv-x="1304" />
<glyph unicode="&#x2002;" horiz-adv-x="652" />
<glyph unicode="&#x2003;" horiz-adv-x="1304" />
<glyph unicode="&#x2004;" horiz-adv-x="434" />
<glyph unicode="&#x2005;" horiz-adv-x="326" />
<glyph unicode="&#x2006;" horiz-adv-x="217" />
<glyph unicode="&#x2007;" horiz-adv-x="217" />
<glyph unicode="&#x2008;" horiz-adv-x="163" />
<glyph unicode="&#x2009;" horiz-adv-x="260" />
<glyph unicode="&#x200a;" horiz-adv-x="72" />
<glyph unicode="&#x202f;" horiz-adv-x="260" />
<glyph unicode="&#x205f;" horiz-adv-x="326" />
<glyph unicode="&#x20ac;" d="M100 500l100 100h113q0 47 5 100h-218l100 100h135q37 167 112 257q117 141 297 141q242 0 354 -189q60 -103 66 -209h-181q0 55 -25.5 99t-63.5 68t-75 36.5t-67 12.5q-24 0 -52.5 -10t-62.5 -32t-65.5 -67t-50.5 -107h379l-100 -100h-300q-6 -46 -6 -100h406l-100 -100 h-300q9 -74 33 -132t52.5 -91t62 -54.5t59 -29t46.5 -7.5q29 0 66 13t75 37t63.5 67.5t25.5 96.5h174q-31 -172 -128 -278q-107 -117 -274 -117q-205 0 -324 158q-36 46 -69 131.5t-45 205.5h-217z" />
<glyph unicode="&#x2212;" d="M200 400h900v300h-900v-300z" />
<glyph unicode="&#x25fc;" horiz-adv-x="500" d="M0 0z" />
<glyph unicode="&#x2601;" d="M-14 494q0 -80 56.5 -137t135.5 -57h750q120 0 205 86.5t85 207.5t-85 207t-205 86q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5z" />
<glyph unicode="&#x2709;" d="M0 100l400 400l200 -200l200 200l400 -400h-1200zM0 300v600l300 -300zM0 1100l600 -603l600 603h-1200zM900 600l300 300v-600z" />
<glyph unicode="&#x270f;" d="M-13 -13l333 112l-223 223zM187 403l214 -214l614 614l-214 214zM887 1103l214 -214l99 92q13 13 13 32.5t-13 33.5l-153 153q-15 13 -33 13t-33 -13z" />
<glyph unicode="&#xe001;" d="M0 1200h1200l-500 -550v-550h300v-100h-800v100h300v550z" />
<glyph unicode="&#xe002;" d="M14 84q18 -55 86 -75.5t147 5.5q65 21 109 69t44 90v606l600 155v-521q-64 16 -138 -7q-79 -26 -122.5 -83t-25.5 -111q18 -55 86 -75.5t147 4.5q70 23 111.5 63.5t41.5 95.5v881q0 10 -7 15.5t-17 2.5l-752 -193q-10 -3 -17 -12.5t-7 -19.5v-689q-64 17 -138 -7 q-79 -25 -122.5 -82t-25.5 -112z" />
<glyph unicode="&#xe003;" d="M23 693q0 200 142 342t342 142t342 -142t142 -342q0 -142 -78 -261l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 693q0 -136 97 -233t234 -97t233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5 t-234 -97t-97 -233z" />
<glyph unicode="&#xe005;" d="M100 784q0 64 28 123t73 100.5t104.5 64t119 20.5t120 -38.5t104.5 -104.5q48 69 109.5 105t121.5 38t118.5 -20.5t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-149.5 152.5t-126.5 127.5 t-94 124.5t-33.5 117.5z" />
<glyph unicode="&#xe006;" d="M-72 800h479l146 400h2l146 -400h472l-382 -278l145 -449l-384 275l-382 -275l146 447zM168 71l2 1z" />
<glyph unicode="&#xe007;" d="M-72 800h479l146 400h2l146 -400h472l-382 -278l145 -449l-384 275l-382 -275l146 447zM168 71l2 1zM237 700l196 -142l-73 -226l192 140l195 -141l-74 229l193 140h-235l-77 211l-78 -211h-239z" />
<glyph unicode="&#xe008;" d="M0 0v143l400 257v100q-37 0 -68.5 74.5t-31.5 125.5v200q0 124 88 212t212 88t212 -88t88 -212v-200q0 -51 -31.5 -125.5t-68.5 -74.5v-100l400 -257v-143h-1200z" />
<glyph unicode="&#xe009;" d="M0 0v1100h1200v-1100h-1200zM100 100h100v100h-100v-100zM100 300h100v100h-100v-100zM100 500h100v100h-100v-100zM100 700h100v100h-100v-100zM100 900h100v100h-100v-100zM300 100h600v400h-600v-400zM300 600h600v400h-600v-400zM1000 100h100v100h-100v-100z M1000 300h100v100h-100v-100zM1000 500h100v100h-100v-100zM1000 700h100v100h-100v-100zM1000 900h100v100h-100v-100z" />
<glyph unicode="&#xe010;" d="M0 50v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5zM0 650v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5zM600 50v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5zM600 650v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5z" />
<glyph unicode="&#xe011;" d="M0 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM0 450v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200 q-21 0 -35.5 14.5t-14.5 35.5zM0 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5 t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 450v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5 v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 450v200q0 21 14.5 35.5t35.5 14.5h200 q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5z" />
<glyph unicode="&#xe012;" d="M0 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM0 450q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v200q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5 t-14.5 -35.5v-200zM0 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 50v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5 t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5zM400 450v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5zM400 850v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5 v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5z" />
<glyph unicode="&#xe013;" d="M29 454l419 -420l818 820l-212 212l-607 -607l-206 207z" />
<glyph unicode="&#xe014;" d="M106 318l282 282l-282 282l212 212l282 -282l282 282l212 -212l-282 -282l282 -282l-212 -212l-282 282l-282 -282z" />
<glyph unicode="&#xe015;" d="M23 693q0 200 142 342t342 142t342 -142t142 -342q0 -142 -78 -261l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 693q0 -136 97 -233t234 -97t233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5 t-234 -97t-97 -233zM300 600v200h100v100h200v-100h100v-200h-100v-100h-200v100h-100z" />
<glyph unicode="&#xe016;" d="M23 694q0 200 142 342t342 142t342 -142t142 -342q0 -141 -78 -262l300 -299q7 -7 7 -18t-7 -18l-109 -109q-8 -8 -18 -8t-18 8l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 694q0 -136 97 -233t234 -97t233.5 97t96.5 233t-96.5 233t-233.5 97t-234 -97 t-97 -233zM300 601h400v200h-400v-200z" />
<glyph unicode="&#xe017;" d="M23 600q0 183 105 331t272 210v-166q-103 -55 -165 -155t-62 -220q0 -177 125 -302t302 -125t302 125t125 302q0 120 -62 220t-165 155v166q167 -62 272 -210t105 -331q0 -118 -45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123t-123 184t-45.5 224.5 zM500 750q0 -21 14.5 -35.5t35.5 -14.5h100q21 0 35.5 14.5t14.5 35.5v400q0 21 -14.5 35.5t-35.5 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-400z" />
<glyph unicode="&#xe018;" d="M100 1h200v300h-200v-300zM400 1v500h200v-500h-200zM700 1v800h200v-800h-200zM1000 1v1200h200v-1200h-200z" />
<glyph unicode="&#xe019;" d="M26 601q0 -33 6 -74l151 -38l2 -6q14 -49 38 -93l3 -5l-80 -134q45 -59 105 -105l133 81l5 -3q45 -26 94 -39l5 -2l38 -151q40 -5 74 -5q27 0 74 5l38 151l6 2q46 13 93 39l5 3l134 -81q56 44 104 105l-80 134l3 5q24 44 39 93l1 6l152 38q5 40 5 74q0 28 -5 73l-152 38 l-1 6q-16 51 -39 93l-3 5l80 134q-44 58 -104 105l-134 -81l-5 3q-45 25 -93 39l-6 1l-38 152q-40 5 -74 5q-27 0 -74 -5l-38 -152l-5 -1q-50 -14 -94 -39l-5 -3l-133 81q-59 -47 -105 -105l80 -134l-3 -5q-25 -47 -38 -93l-2 -6l-151 -38q-6 -48 -6 -73zM385 601 q0 88 63 151t152 63t152 -63t63 -151q0 -89 -63 -152t-152 -63t-152 63t-63 152z" />
<glyph unicode="&#xe020;" d="M100 1025v50q0 10 7.5 17.5t17.5 7.5h275v100q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5v-100h275q10 0 17.5 -7.5t7.5 -17.5v-50q0 -11 -7 -18t-18 -7h-1050q-11 0 -18 7t-7 18zM200 100v800h900v-800q0 -41 -29.5 -71t-70.5 -30h-700q-41 0 -70.5 30 t-29.5 71zM300 100h100v700h-100v-700zM500 100h100v700h-100v-700zM500 1100h300v100h-300v-100zM700 100h100v700h-100v-700zM900 100h100v700h-100v-700z" />
<glyph unicode="&#xe021;" d="M1 601l656 644l644 -644h-200v-600h-300v400h-300v-400h-300v600h-200z" />
<glyph unicode="&#xe022;" d="M100 25v1150q0 11 7 18t18 7h475v-500h400v-675q0 -11 -7 -18t-18 -7h-850q-11 0 -18 7t-7 18zM700 800v300l300 -300h-300z" />
<glyph unicode="&#xe023;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM500 500v400h100 v-300h200v-100h-300z" />
<glyph unicode="&#xe024;" d="M-100 0l431 1200h209l-21 -300h162l-20 300h208l431 -1200h-538l-41 400h-242l-40 -400h-539zM488 500h224l-27 300h-170z" />
<glyph unicode="&#xe025;" d="M0 0v400h490l-290 300h200v500h300v-500h200l-290 -300h490v-400h-1100zM813 200h175v100h-175v-100z" />
<glyph unicode="&#xe026;" d="M1 600q0 122 47.5 233t127.5 191t191 127.5t233 47.5t233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233zM188 600q0 -170 121 -291t291 -121t291 121t121 291t-121 291t-291 121 t-291 -121t-121 -291zM350 600h150v300h200v-300h150l-250 -300z" />
<glyph unicode="&#xe027;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM350 600l250 300 l250 -300h-150v-300h-200v300h-150z" />
<glyph unicode="&#xe028;" d="M0 25v475l200 700h800l199 -700l1 -475q0 -11 -7 -18t-18 -7h-1150q-11 0 -18 7t-7 18zM200 500h200l50 -200h300l50 200h200l-97 500h-606z" />
<glyph unicode="&#xe029;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM500 397v401 l297 -200z" />
<glyph unicode="&#xe030;" d="M23 600q0 -118 45.5 -224.5t123 -184t184 -123t224.5 -45.5t224.5 45.5t184 123t123 184t45.5 224.5h-150q0 -177 -125 -302t-302 -125t-302 125t-125 302t125 302t302 125q136 0 246 -81l-146 -146h400v400l-145 -145q-157 122 -355 122q-118 0 -224.5 -45.5t-184 -123 t-123 -184t-45.5 -224.5z" />
<glyph unicode="&#xe031;" d="M23 600q0 118 45.5 224.5t123 184t184 123t224.5 45.5q198 0 355 -122l145 145v-400h-400l147 147q-112 80 -247 80q-177 0 -302 -125t-125 -302h-150zM100 0v400h400l-147 -147q112 -80 247 -80q177 0 302 125t125 302h150q0 -118 -45.5 -224.5t-123 -184t-184 -123 t-224.5 -45.5q-198 0 -355 122z" />
<glyph unicode="&#xe032;" d="M100 0h1100v1200h-1100v-1200zM200 100v900h900v-900h-900zM300 200v100h100v-100h-100zM300 400v100h100v-100h-100zM300 600v100h100v-100h-100zM300 800v100h100v-100h-100zM500 200h500v100h-500v-100zM500 400v100h500v-100h-500zM500 600v100h500v-100h-500z M500 800v100h500v-100h-500z" />
<glyph unicode="&#xe033;" d="M0 100v600q0 41 29.5 70.5t70.5 29.5h100v200q0 82 59 141t141 59h300q82 0 141 -59t59 -141v-200h100q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-900q-41 0 -70.5 29.5t-29.5 70.5zM400 800h300v150q0 21 -14.5 35.5t-35.5 14.5h-200 q-21 0 -35.5 -14.5t-14.5 -35.5v-150z" />
<glyph unicode="&#xe034;" d="M100 0v1100h100v-1100h-100zM300 400q60 60 127.5 84t127.5 17.5t122 -23t119 -30t110 -11t103 42t91 120.5v500q-40 -81 -101.5 -115.5t-127.5 -29.5t-138 25t-139.5 40t-125.5 25t-103 -29.5t-65 -115.5v-500z" />
<glyph unicode="&#xe035;" d="M0 275q0 -11 7 -18t18 -7h50q11 0 18 7t7 18v300q0 127 70.5 231.5t184.5 161.5t245 57t245 -57t184.5 -161.5t70.5 -231.5v-300q0 -11 7 -18t18 -7h50q11 0 18 7t7 18v300q0 116 -49.5 227t-131 192.5t-192.5 131t-227 49.5t-227 -49.5t-192.5 -131t-131 -192.5 t-49.5 -227v-300zM200 20v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14zM800 20v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14z" />
<glyph unicode="&#xe036;" d="M0 400h300l300 -200v800l-300 -200h-300v-400zM688 459l141 141l-141 141l71 71l141 -141l141 141l71 -71l-141 -141l141 -141l-71 -71l-141 141l-141 -141z" />
<glyph unicode="&#xe037;" d="M0 400h300l300 -200v800l-300 -200h-300v-400zM700 857l69 53q111 -135 111 -310q0 -169 -106 -302l-67 54q86 110 86 248q0 146 -93 257z" />
<glyph unicode="&#xe038;" d="M0 401v400h300l300 200v-800l-300 200h-300zM702 858l69 53q111 -135 111 -310q0 -170 -106 -303l-67 55q86 110 86 248q0 145 -93 257zM889 951l7 -8q123 -151 123 -344q0 -189 -119 -339l-7 -8l81 -66l6 8q142 178 142 405q0 230 -144 408l-6 8z" />
<glyph unicode="&#xe039;" d="M0 0h500v500h-200v100h-100v-100h-200v-500zM0 600h100v100h400v100h100v100h-100v300h-500v-600zM100 100v300h300v-300h-300zM100 800v300h300v-300h-300zM200 200v100h100v-100h-100zM200 900h100v100h-100v-100zM500 500v100h300v-300h200v-100h-100v-100h-200v100 h-100v100h100v200h-200zM600 0v100h100v-100h-100zM600 1000h100v-300h200v-300h300v200h-200v100h200v500h-600v-200zM800 800v300h300v-300h-300zM900 0v100h300v-100h-300zM900 900v100h100v-100h-100zM1100 200v100h100v-100h-100z" />
<glyph unicode="&#xe040;" d="M0 200h100v1000h-100v-1000zM100 0v100h300v-100h-300zM200 200v1000h100v-1000h-100zM500 0v91h100v-91h-100zM500 200v1000h200v-1000h-200zM700 0v91h100v-91h-100zM800 200v1000h100v-1000h-100zM900 0v91h200v-91h-200zM1000 200v1000h200v-1000h-200z" />
<glyph unicode="&#xe041;" d="M0 700l1 475q0 10 7.5 17.5t17.5 7.5h474l700 -700l-500 -500zM148 953q0 -42 29 -71q30 -30 71.5 -30t71.5 30q29 29 29 71t-29 71q-30 30 -71.5 30t-71.5 -30q-29 -29 -29 -71z" />
<glyph unicode="&#xe042;" d="M1 700l1 475q0 11 7 18t18 7h474l700 -700l-500 -500zM148 953q0 -42 30 -71q29 -30 71 -30t71 30q30 29 30 71t-30 71q-29 30 -71 30t-71 -30q-30 -29 -30 -71zM701 1200h100l700 -700l-500 -500l-50 50l450 450z" />
<glyph unicode="&#xe043;" d="M100 0v1025l175 175h925v-1000l-100 -100v1000h-750l-100 -100h750v-1000h-900z" />
<glyph unicode="&#xe044;" d="M200 0l450 444l450 -443v1150q0 20 -14.5 35t-35.5 15h-800q-21 0 -35.5 -15t-14.5 -35v-1151z" />
<glyph unicode="&#xe045;" d="M0 100v700h200l100 -200h600l100 200h200v-700h-200v200h-800v-200h-200zM253 829l40 -124h592l62 124l-94 346q-2 11 -10 18t-18 7h-450q-10 0 -18 -7t-10 -18zM281 24l38 152q2 10 11.5 17t19.5 7h500q10 0 19.5 -7t11.5 -17l38 -152q2 -10 -3.5 -17t-15.5 -7h-600 q-10 0 -15.5 7t-3.5 17z" />
<glyph unicode="&#xe046;" d="M0 200q0 -41 29.5 -70.5t70.5 -29.5h1000q41 0 70.5 29.5t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5h-150q-4 8 -11.5 21.5t-33 48t-53 61t-69 48t-83.5 21.5h-200q-41 0 -82 -20.5t-70 -50t-52 -59t-34 -50.5l-12 -20h-150q-41 0 -70.5 -29.5t-29.5 -70.5v-600z M356 500q0 100 72 172t172 72t172 -72t72 -172t-72 -172t-172 -72t-172 72t-72 172zM494 500q0 -44 31 -75t75 -31t75 31t31 75t-31 75t-75 31t-75 -31t-31 -75zM900 700v100h100v-100h-100z" />
<glyph unicode="&#xe047;" d="M53 0h365v66q-41 0 -72 11t-49 38t1 71l92 234h391l82 -222q16 -45 -5.5 -88.5t-74.5 -43.5v-66h417v66q-34 1 -74 43q-18 19 -33 42t-21 37l-6 13l-385 998h-93l-399 -1006q-24 -48 -52 -75q-12 -12 -33 -25t-36 -20l-15 -7v-66zM416 521l178 457l46 -140l116 -317h-340 z" />
<glyph unicode="&#xe048;" d="M100 0v89q41 7 70.5 32.5t29.5 65.5v827q0 28 -1 39.5t-5.5 26t-15.5 21t-29 14t-49 14.5v71l471 -1q120 0 213 -88t93 -228q0 -55 -11.5 -101.5t-28 -74t-33.5 -47.5t-28 -28l-12 -7q8 -3 21.5 -9t48 -31.5t60.5 -58t47.5 -91.5t21.5 -129q0 -84 -59 -156.5t-142 -111 t-162 -38.5h-500zM400 200h161q89 0 153 48.5t64 132.5q0 90 -62.5 154.5t-156.5 64.5h-159v-400zM400 700h139q76 0 130 61.5t54 138.5q0 82 -84 130.5t-239 48.5v-379z" />
<glyph unicode="&#xe049;" d="M200 0v57q77 7 134.5 40.5t65.5 80.5l173 849q10 56 -10 74t-91 37q-6 1 -10.5 2.5t-9.5 2.5v57h425l2 -57q-33 -8 -62 -25.5t-46 -37t-29.5 -38t-17.5 -30.5l-5 -12l-128 -825q-10 -52 14 -82t95 -36v-57h-500z" />
<glyph unicode="&#xe050;" d="M-75 200h75v800h-75l125 167l125 -167h-75v-800h75l-125 -167zM300 900v300h150h700h150v-300h-50q0 29 -8 48.5t-18.5 30t-33.5 15t-39.5 5.5t-50.5 1h-200v-850l100 -50v-100h-400v100l100 50v850h-200q-34 0 -50.5 -1t-40 -5.5t-33.5 -15t-18.5 -30t-8.5 -48.5h-49z " />
<glyph unicode="&#xe051;" d="M33 51l167 125v-75h800v75l167 -125l-167 -125v75h-800v-75zM100 901v300h150h700h150v-300h-50q0 29 -8 48.5t-18 30t-33.5 15t-40 5.5t-50.5 1h-200v-650l100 -50v-100h-400v100l100 50v650h-200q-34 0 -50.5 -1t-39.5 -5.5t-33.5 -15t-18.5 -30t-8 -48.5h-50z" />
<glyph unicode="&#xe052;" d="M0 50q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 350q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5 v-100zM0 650q0 -20 14.5 -35t35.5 -15h1000q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1000q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 950q0 -20 14.5 -35t35.5 -15h600q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-600q-21 0 -35.5 -14.5 t-14.5 -35.5v-100z" />
<glyph unicode="&#xe053;" d="M0 50q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 650q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5 v-100zM200 350q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM200 950q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5 t-14.5 -35.5v-100z" />
<glyph unicode="&#xe054;" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM100 650v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1000q-21 0 -35.5 15 t-14.5 35zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM500 950v100q0 21 14.5 35.5t35.5 14.5h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-600 q-21 0 -35.5 15t-14.5 35z" />
<glyph unicode="&#xe055;" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 350v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15 t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 950v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100 q-21 0 -35.5 15t-14.5 35z" />
<glyph unicode="&#xe056;" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM0 350v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15 t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM0 950v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15 t-14.5 35zM300 50v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800 q-21 0 -35.5 15t-14.5 35zM300 650v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM300 950v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15 h-800q-21 0 -35.5 15t-14.5 35z" />
<glyph unicode="&#xe057;" d="M-101 500v100h201v75l166 -125l-166 -125v75h-201zM300 0h100v1100h-100v-1100zM500 50q0 -20 14.5 -35t35.5 -15h600q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-600q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 350q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35 v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 650q0 -20 14.5 -35t35.5 -15h500q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 950q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35v100 q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100z" />
<glyph unicode="&#xe058;" d="M1 50q0 -20 14.5 -35t35.5 -15h600q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-600q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 350q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 650 q0 -20 14.5 -35t35.5 -15h500q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 950q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM801 0v1100h100v-1100 h-100zM934 550l167 -125v75h200v100h-200v75z" />
<glyph unicode="&#xe059;" d="M0 275v650q0 31 22 53t53 22h750q31 0 53 -22t22 -53v-650q0 -31 -22 -53t-53 -22h-750q-31 0 -53 22t-22 53zM900 600l300 300v-600z" />
<glyph unicode="&#xe060;" d="M0 44v1012q0 18 13 31t31 13h1112q19 0 31.5 -13t12.5 -31v-1012q0 -18 -12.5 -31t-31.5 -13h-1112q-18 0 -31 13t-13 31zM100 263l247 182l298 -131l-74 156l293 318l236 -288v500h-1000v-737zM208 750q0 56 39 95t95 39t95 -39t39 -95t-39 -95t-95 -39t-95 39t-39 95z " />
<glyph unicode="&#xe062;" d="M148 745q0 124 60.5 231.5t165 172t226.5 64.5q123 0 227 -63t164.5 -169.5t60.5 -229.5t-73 -272q-73 -114 -166.5 -237t-150.5 -189l-57 -66q-10 9 -27 26t-66.5 70.5t-96 109t-104 135.5t-100.5 155q-63 139 -63 262zM342 772q0 -107 75.5 -182.5t181.5 -75.5 q107 0 182.5 75.5t75.5 182.5t-75.5 182t-182.5 75t-182 -75.5t-75 -181.5z" />
<glyph unicode="&#xe063;" d="M1 600q0 122 47.5 233t127.5 191t191 127.5t233 47.5t233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233zM173 600q0 -177 125.5 -302t301.5 -125v854q-176 0 -301.5 -125 t-125.5 -302z" />
<glyph unicode="&#xe064;" d="M117 406q0 94 34 186t88.5 172.5t112 159t115 177t87.5 194.5q21 -71 57.5 -142.5t76 -130.5t83 -118.5t82 -117t70 -116t50 -125.5t18.5 -136q0 -89 -39 -165.5t-102 -126.5t-140 -79.5t-156 -33.5q-114 6 -211.5 53t-161.5 139t-64 210zM243 414q14 -82 59.5 -136 t136.5 -80l16 98q-7 6 -18 17t-34 48t-33 77q-15 73 -14 143.5t10 122.5l9 51q-92 -110 -119.5 -185t-12.5 -156z" />
<glyph unicode="&#xe065;" d="M0 400v300q0 165 117.5 282.5t282.5 117.5q366 -6 397 -14l-186 -186h-311q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v125l200 200v-225q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5 t-117.5 282.5zM436 341l161 50l412 412l-114 113l-405 -405zM995 1015l113 -113l113 113l-21 85l-92 28z" />
<glyph unicode="&#xe066;" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h261l2 -80q-133 -32 -218 -120h-145q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5l200 153v-53q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5 zM423 524q30 38 81.5 64t103 35.5t99 14t77.5 3.5l29 -1v-209l360 324l-359 318v-216q-7 0 -19 -1t-48 -8t-69.5 -18.5t-76.5 -37t-76.5 -59t-62 -88t-39.5 -121.5z" />
<glyph unicode="&#xe067;" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q61 0 127 -23l-178 -177h-349q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v69l200 200v-169q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5 t-117.5 282.5zM342 632l283 -284l567 567l-137 137l-430 -431l-146 147z" />
<glyph unicode="&#xe068;" d="M0 603l300 296v-198h200v200h-200l300 300l295 -300h-195v-200h200v198l300 -296l-300 -300v198h-200v-200h195l-295 -300l-300 300h200v200h-200v-198z" />
<glyph unicode="&#xe069;" d="M200 50v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-1100l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5z" />
<glyph unicode="&#xe070;" d="M0 50v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-487l500 487v-1100l-500 488v-488l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5z" />
<glyph unicode="&#xe071;" d="M136 550l564 550v-487l500 487v-1100l-500 488v-488z" />
<glyph unicode="&#xe072;" d="M200 0l900 550l-900 550v-1100z" />
<glyph unicode="&#xe073;" d="M200 150q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v800q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5t-14.5 -35.5v-800zM600 150q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v800q0 21 -14.5 35.5t-35.5 14.5h-200 q-21 0 -35.5 -14.5t-14.5 -35.5v-800z" />
<glyph unicode="&#xe074;" d="M200 150q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35v800q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5v-800z" />
<glyph unicode="&#xe075;" d="M0 0v1100l500 -487v487l564 -550l-564 -550v488z" />
<glyph unicode="&#xe076;" d="M0 0v1100l500 -487v487l500 -487v437q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-500 -488v488z" />
<glyph unicode="&#xe077;" d="M300 0v1100l500 -487v437q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438z" />
<glyph unicode="&#xe078;" d="M100 250v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5zM100 500h1100l-550 564z" />
<glyph unicode="&#xe079;" d="M185 599l592 -592l240 240l-353 353l353 353l-240 240z" />
<glyph unicode="&#xe080;" d="M272 194l353 353l-353 353l241 240l572 -571l21 -22l-1 -1v-1l-592 -591z" />
<glyph unicode="&#xe081;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM300 500h200v-200h200v200h200v200h-200v200h-200v-200h-200v-200z" />
<glyph unicode="&#xe082;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM300 500h600v200h-600v-200z" />
<glyph unicode="&#xe083;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM246 459l213 -213l141 142l141 -142l213 213l-142 141l142 141l-213 212l-141 -141l-141 142l-212 -213l141 -141 z" />
<glyph unicode="&#xe084;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM270 551l276 -277l411 411l-175 174l-236 -236l-102 102z" />
<glyph unicode="&#xe085;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM364 700h143q4 0 11.5 -1t11 -1t6.5 3t3 9t1 11t3.5 8.5t3.5 6t5.5 4t6.5 2.5t9 1.5t9 0.5h11.5h12.5 q19 0 30 -10t11 -26q0 -22 -4 -28t-27 -22q-5 -1 -12.5 -3t-27 -13.5t-34 -27t-26.5 -46t-11 -68.5h200q5 3 14 8t31.5 25.5t39.5 45.5t31 69t14 94q0 51 -17.5 89t-42 58t-58.5 32t-58.5 15t-51.5 3q-50 0 -90.5 -12t-75 -38.5t-53.5 -74.5t-19 -114zM500 300h200v100h-200 v-100z" />
<glyph unicode="&#xe086;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM400 300h400v100h-100v300h-300v-100h100v-200h-100v-100zM500 800h200v100h-200v-100z" />
<glyph unicode="&#xe087;" d="M0 500v200h195q31 125 98.5 199.5t206.5 100.5v200h200v-200q54 -20 113 -60t112.5 -105.5t71.5 -134.5h203v-200h-203q-25 -102 -116.5 -186t-180.5 -117v-197h-200v197q-140 27 -208 102.5t-98 200.5h-194zM290 500q24 -73 79.5 -127.5t130.5 -78.5v206h200v-206 q149 48 201 206h-201v200h200q-25 74 -75.5 127t-124.5 77v-204h-200v203q-75 -23 -130 -77t-79 -126h209v-200h-210z" />
<glyph unicode="&#xe088;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM356 465l135 135 l-135 135l109 109l135 -135l135 135l109 -109l-135 -135l135 -135l-109 -109l-135 135l-135 -135z" />
<glyph unicode="&#xe089;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM322 537l141 141 l87 -87l204 205l142 -142l-346 -345z" />
<glyph unicode="&#xe090;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -115 62 -215l568 567q-100 62 -216 62q-171 0 -292.5 -121.5t-121.5 -292.5zM391 245q97 -59 209 -59q171 0 292.5 121.5t121.5 292.5 q0 112 -59 209z" />
<glyph unicode="&#xe091;" d="M0 547l600 453v-300h600v-300h-600v-301z" />
<glyph unicode="&#xe092;" d="M0 400v300h600v300l600 -453l-600 -448v301h-600z" />
<glyph unicode="&#xe093;" d="M204 600l450 600l444 -600h-298v-600h-300v600h-296z" />
<glyph unicode="&#xe094;" d="M104 600h296v600h300v-600h298l-449 -600z" />
<glyph unicode="&#xe095;" d="M0 200q6 132 41 238.5t103.5 193t184 138t271.5 59.5v271l600 -453l-600 -448v301q-95 -2 -183 -20t-170 -52t-147 -92.5t-100 -135.5z" />
<glyph unicode="&#xe096;" d="M0 0v400l129 -129l294 294l142 -142l-294 -294l129 -129h-400zM635 777l142 -142l294 294l129 -129v400h-400l129 -129z" />
<glyph unicode="&#xe097;" d="M34 176l295 295l-129 129h400v-400l-129 130l-295 -295zM600 600v400l129 -129l295 295l142 -141l-295 -295l129 -130h-400z" />
<glyph unicode="&#xe101;" d="M23 600q0 118 45.5 224.5t123 184t184 123t224.5 45.5t224.5 -45.5t184 -123t123 -184t45.5 -224.5t-45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123t-123 184t-45.5 224.5zM456 851l58 -302q4 -20 21.5 -34.5t37.5 -14.5h54q20 0 37.5 14.5 t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5h-207q-21 0 -33 -14.5t-8 -34.5zM500 300h200v100h-200v-100z" />
<glyph unicode="&#xe102;" d="M0 800h100v-200h400v300h200v-300h400v200h100v100h-111q1 1 1 6.5t-1.5 15t-3.5 17.5l-34 172q-11 39 -41.5 63t-69.5 24q-32 0 -61 -17l-239 -144q-22 -13 -40 -35q-19 24 -40 36l-238 144q-33 18 -62 18q-39 0 -69.5 -23t-40.5 -61l-35 -177q-2 -8 -3 -18t-1 -15v-6 h-111v-100zM100 0h400v400h-400v-400zM200 900q-3 0 14 48t36 96l18 47l213 -191h-281zM700 0v400h400v-400h-400zM731 900l202 197q5 -12 12 -32.5t23 -64t25 -72t7 -28.5h-269z" />
<glyph unicode="&#xe103;" d="M0 -22v143l216 193q-9 53 -13 83t-5.5 94t9 113t38.5 114t74 124q47 60 99.5 102.5t103 68t127.5 48t145.5 37.5t184.5 43.5t220 58.5q0 -189 -22 -343t-59 -258t-89 -181.5t-108.5 -120t-122 -68t-125.5 -30t-121.5 -1.5t-107.5 12.5t-87.5 17t-56.5 7.5l-99 -55z M238.5 300.5q19.5 -6.5 86.5 76.5q55 66 367 234q70 38 118.5 69.5t102 79t99 111.5t86.5 148q22 50 24 60t-6 19q-7 5 -17 5t-26.5 -14.5t-33.5 -39.5q-35 -51 -113.5 -108.5t-139.5 -89.5l-61 -32q-369 -197 -458 -401q-48 -111 -28.5 -117.5z" />
<glyph unicode="&#xe104;" d="M111 408q0 -33 5 -63q9 -56 44 -119.5t105 -108.5q31 -21 64 -16t62 23.5t57 49.5t48 61.5t35 60.5q32 66 39 184.5t-13 157.5q79 -80 122 -164t26 -184q-5 -33 -20.5 -69.5t-37.5 -80.5q-10 -19 -14.5 -29t-12 -26t-9 -23.5t-3 -19t2.5 -15.5t11 -9.5t19.5 -5t30.5 2.5 t42 8q57 20 91 34t87.5 44.5t87 64t65.5 88.5t47 122q38 172 -44.5 341.5t-246.5 278.5q22 -44 43 -129q39 -159 -32 -154q-15 2 -33 9q-79 33 -120.5 100t-44 175.5t48.5 257.5q-13 -8 -34 -23.5t-72.5 -66.5t-88.5 -105.5t-60 -138t-8 -166.5q2 -12 8 -41.5t8 -43t6 -39.5 t3.5 -39.5t-1 -33.5t-6 -31.5t-13.5 -24t-21 -20.5t-31 -12q-38 -10 -67 13t-40.5 61.5t-15 81.5t10.5 75q-52 -46 -83.5 -101t-39 -107t-7.5 -85z" />
<glyph unicode="&#xe105;" d="M-61 600l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5t145.5 -23.5t132.5 -59t116.5 -83.5t97 -90t74.5 -85.5t49 -63.5t20 -30l26 -40l-26 -40q-6 -10 -20 -30t-49 -63.5t-74.5 -85.5t-97 -90t-116.5 -83.5t-132.5 -59t-145.5 -23.5 t-145.5 23.5t-132.5 59t-116.5 83.5t-97 90t-74.5 85.5t-49 63.5t-20 30zM120 600q7 -10 40.5 -58t56 -78.5t68 -77.5t87.5 -75t103 -49.5t125 -21.5t123.5 20t100.5 45.5t85.5 71.5t66.5 75.5t58 81.5t47 66q-1 1 -28.5 37.5t-42 55t-43.5 53t-57.5 63.5t-58.5 54 q49 -74 49 -163q0 -124 -88 -212t-212 -88t-212 88t-88 212q0 85 46 158q-102 -87 -226 -258zM377 656q49 -124 154 -191l105 105q-37 24 -75 72t-57 84l-20 36z" />
<glyph unicode="&#xe106;" d="M-61 600l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5q61 0 121 -17l37 142h148l-314 -1200h-148l37 143q-82 21 -165 71.5t-140 102t-109.5 112t-72 88.5t-29.5 43zM120 600q210 -282 393 -336l37 141q-107 18 -178.5 101.5t-71.5 193.5 q0 85 46 158q-102 -87 -226 -258zM377 656q49 -124 154 -191l47 47l23 87q-30 28 -59 69t-44 68l-14 26zM780 161l38 145q22 15 44.5 34t46 44t40.5 44t41 50.5t33.5 43.5t33 44t24.5 34q-97 127 -140 175l39 146q67 -54 131.5 -125.5t87.5 -103.5t36 -52l26 -40l-26 -40 q-7 -12 -25.5 -38t-63.5 -79.5t-95.5 -102.5t-124 -100t-146.5 -79z" />
<glyph unicode="&#xe107;" d="M-97.5 34q13.5 -34 50.5 -34h1294q37 0 50.5 35.5t-7.5 67.5l-642 1056q-20 34 -48 36.5t-48 -29.5l-642 -1066q-21 -32 -7.5 -66zM155 200l445 723l445 -723h-345v100h-200v-100h-345zM500 600l100 -300l100 300v100h-200v-100z" />
<glyph unicode="&#xe108;" d="M100 262v41q0 20 11 44.5t26 38.5l363 325v339q0 62 44 106t106 44t106 -44t44 -106v-339l363 -325q15 -14 26 -38.5t11 -44.5v-41q0 -20 -12 -26.5t-29 5.5l-359 249v-263q100 -91 100 -113v-64q0 -20 -13 -28.5t-32 0.5l-94 78h-222l-94 -78q-19 -9 -32 -0.5t-13 28.5 v64q0 22 100 113v263l-359 -249q-17 -12 -29 -5.5t-12 26.5z" />
<glyph unicode="&#xe109;" d="M0 50q0 -20 14.5 -35t35.5 -15h1000q21 0 35.5 15t14.5 35v750h-1100v-750zM0 900h1100v150q0 21 -14.5 35.5t-35.5 14.5h-150v100h-100v-100h-500v100h-100v-100h-150q-21 0 -35.5 -14.5t-14.5 -35.5v-150zM100 100v100h100v-100h-100zM100 300v100h100v-100h-100z M100 500v100h100v-100h-100zM300 100v100h100v-100h-100zM300 300v100h100v-100h-100zM300 500v100h100v-100h-100zM500 100v100h100v-100h-100zM500 300v100h100v-100h-100zM500 500v100h100v-100h-100zM700 100v100h100v-100h-100zM700 300v100h100v-100h-100zM700 500 v100h100v-100h-100zM900 100v100h100v-100h-100zM900 300v100h100v-100h-100zM900 500v100h100v-100h-100z" />
<glyph unicode="&#xe110;" d="M0 200v200h259l600 600h241v198l300 -295l-300 -300v197h-159l-600 -600h-341zM0 800h259l122 -122l141 142l-181 180h-341v-200zM678 381l141 142l122 -123h159v198l300 -295l-300 -300v197h-241z" />
<glyph unicode="&#xe111;" d="M0 400v600q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-596l-304 -300v300h-100q-41 0 -70.5 29.5t-29.5 70.5z" />
<glyph unicode="&#xe112;" d="M100 600v200h300v-250q0 -113 6 -145q17 -92 102 -117q39 -11 92 -11q37 0 66.5 5.5t50 15.5t36 24t24 31.5t14 37.5t7 42t2.5 45t0 47v25v250h300v-200q0 -42 -3 -83t-15 -104t-31.5 -116t-58 -109.5t-89 -96.5t-129 -65.5t-174.5 -25.5t-174.5 25.5t-129 65.5t-89 96.5 t-58 109.5t-31.5 116t-15 104t-3 83zM100 900v300h300v-300h-300zM800 900v300h300v-300h-300z" />
<glyph unicode="&#xe113;" d="M-30 411l227 -227l352 353l353 -353l226 227l-578 579z" />
<glyph unicode="&#xe114;" d="M70 797l580 -579l578 579l-226 227l-353 -353l-352 353z" />
<glyph unicode="&#xe115;" d="M-198 700l299 283l300 -283h-203v-400h385l215 -200h-800v600h-196zM402 1000l215 -200h381v-400h-198l299 -283l299 283h-200v600h-796z" />
<glyph unicode="&#xe116;" d="M18 939q-5 24 10 42q14 19 39 19h896l38 162q5 17 18.5 27.5t30.5 10.5h94q20 0 35 -14.5t15 -35.5t-15 -35.5t-35 -14.5h-54l-201 -961q-2 -4 -6 -10.5t-19 -17.5t-33 -11h-31v-50q0 -20 -14.5 -35t-35.5 -15t-35.5 15t-14.5 35v50h-300v-50q0 -20 -14.5 -35t-35.5 -15 t-35.5 15t-14.5 35v50h-50q-21 0 -35.5 15t-14.5 35q0 21 14.5 35.5t35.5 14.5h535l48 200h-633q-32 0 -54.5 21t-27.5 43z" />
<glyph unicode="&#xe117;" d="M0 0v800h1200v-800h-1200zM0 900v100h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500v-100h-1200z" />
<glyph unicode="&#xe118;" d="M1 0l300 700h1200l-300 -700h-1200zM1 400v600h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500v-200h-1000z" />
<glyph unicode="&#xe119;" d="M302 300h198v600h-198l298 300l298 -300h-198v-600h198l-298 -300z" />
<glyph unicode="&#xe120;" d="M0 600l300 298v-198h600v198l300 -298l-300 -297v197h-600v-197z" />
<glyph unicode="&#xe121;" d="M0 100v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM31 400l172 739q5 22 23 41.5t38 19.5h672q19 0 37.5 -22.5t23.5 -45.5l172 -732h-1138zM800 100h100v100h-100v-100z M1000 100h100v100h-100v-100z" />
<glyph unicode="&#xe122;" d="M-101 600v50q0 24 25 49t50 38l25 13v-250l-11 5.5t-24 14t-30 21.5t-24 27.5t-11 31.5zM100 500v250v8v8v7t0.5 7t1.5 5.5t2 5t3 4t4.5 3.5t6 1.5t7.5 0.5h200l675 250v-850l-675 200h-38l47 -276q2 -12 -3 -17.5t-11 -6t-21 -0.5h-8h-83q-20 0 -34.5 14t-18.5 35 q-55 337 -55 351zM1100 200v850q0 21 14.5 35.5t35.5 14.5q20 0 35 -14.5t15 -35.5v-850q0 -20 -15 -35t-35 -15q-21 0 -35.5 15t-14.5 35z" />
<glyph unicode="&#xe123;" d="M74 350q0 21 13.5 35.5t33.5 14.5h18l117 173l63 327q15 77 76 140t144 83l-18 32q-6 19 3 32t29 13h94q20 0 29 -10.5t3 -29.5q-18 -36 -18 -37q83 -19 144 -82.5t76 -140.5l63 -327l118 -173h17q20 0 33.5 -14.5t13.5 -35.5q0 -20 -13 -40t-31 -27q-8 -3 -23 -8.5 t-65 -20t-103 -25t-132.5 -19.5t-158.5 -9q-125 0 -245.5 20.5t-178.5 40.5l-58 20q-18 7 -31 27.5t-13 40.5zM497 110q12 -49 40 -79.5t63 -30.5t63 30.5t39 79.5q-48 -6 -102 -6t-103 6z" />
<glyph unicode="&#xe124;" d="M21 445l233 -45l-78 -224l224 78l45 -233l155 179l155 -179l45 233l224 -78l-78 224l234 45l-180 155l180 156l-234 44l78 225l-224 -78l-45 233l-155 -180l-155 180l-45 -233l-224 78l78 -225l-233 -44l179 -156z" />
<glyph unicode="&#xe125;" d="M0 200h200v600h-200v-600zM300 275q0 -75 100 -75h61q124 -100 139 -100h250q46 0 83 57l238 344q29 31 29 74v100q0 44 -30.5 84.5t-69.5 40.5h-328q28 118 28 125v150q0 44 -30.5 84.5t-69.5 40.5h-50q-27 0 -51 -20t-38 -48l-96 -198l-145 -196q-20 -26 -20 -63v-400z M400 300v375l150 213l100 212h50v-175l-50 -225h450v-125l-250 -375h-214l-136 100h-100z" />
<glyph unicode="&#xe126;" d="M0 400v600h200v-600h-200zM300 525v400q0 75 100 75h61q124 100 139 100h250q46 0 83 -57l238 -344q29 -31 29 -74v-100q0 -44 -30.5 -84.5t-69.5 -40.5h-328q28 -118 28 -125v-150q0 -44 -30.5 -84.5t-69.5 -40.5h-50q-27 0 -51 20t-38 48l-96 198l-145 196 q-20 26 -20 63zM400 525l150 -212l100 -213h50v175l-50 225h450v125l-250 375h-214l-136 -100h-100v-375z" />
<glyph unicode="&#xe127;" d="M8 200v600h200v-600h-200zM308 275v525q0 17 14 35.5t28 28.5l14 9l362 230q14 6 25 6q17 0 29 -12l109 -112q14 -14 14 -34q0 -18 -11 -32l-85 -121h302q85 0 138.5 -38t53.5 -110t-54.5 -111t-138.5 -39h-107l-130 -339q-7 -22 -20.5 -41.5t-28.5 -19.5h-341 q-7 0 -90 81t-83 94zM408 289l100 -89h293l131 339q6 21 19.5 41t28.5 20h203q16 0 25 15t9 36q0 20 -9 34.5t-25 14.5h-457h-6.5h-7.5t-6.5 0.5t-6 1t-5 1.5t-5.5 2.5t-4 4t-4 5.5q-5 12 -5 20q0 14 10 27l147 183l-86 83l-339 -236v-503z" />
<glyph unicode="&#xe128;" d="M-101 651q0 72 54 110t139 38l302 -1l-85 121q-11 16 -11 32q0 21 14 34l109 113q13 12 29 12q11 0 25 -6l365 -230q7 -4 17 -10.5t26.5 -26t16.5 -36.5v-526q0 -13 -86 -93.5t-94 -80.5h-341q-16 0 -29.5 20t-19.5 41l-130 339h-107q-84 0 -139 39t-55 111zM-1 601h222 q15 0 28.5 -20.5t19.5 -40.5l131 -339h293l107 89v502l-343 237l-87 -83l145 -184q10 -11 10 -26q0 -11 -5 -20q-1 -3 -3.5 -5.5l-4 -4t-5 -2.5t-5.5 -1.5t-6.5 -1t-6.5 -0.5h-7.5h-6.5h-476v-100zM1000 201v600h200v-600h-200z" />
<glyph unicode="&#xe129;" d="M97 719l230 -363q4 -6 10.5 -15.5t26 -25t36.5 -15.5h525q13 0 94 83t81 90v342q0 15 -20 28.5t-41 19.5l-339 131v106q0 84 -39 139t-111 55t-110 -53.5t-38 -138.5v-302l-121 84q-15 12 -33.5 11.5t-32.5 -13.5l-112 -110q-22 -22 -6 -53zM172 739l83 86l183 -146 q22 -18 47 -5q3 1 5.5 3.5l4 4t2.5 5t1.5 5.5t1 6.5t0.5 6.5v7.5v6.5v456q0 22 25 31t50 -0.5t25 -30.5v-202q0 -16 20 -29.5t41 -19.5l339 -130v-294l-89 -100h-503zM400 0v200h600v-200h-600z" />
<glyph unicode="&#xe130;" d="M2 585q-16 -31 6 -53l112 -110q13 -13 32 -13.5t34 10.5l121 85q0 -51 -0.5 -153.5t-0.5 -148.5q0 -84 38.5 -138t110.5 -54t111 55t39 139v106l339 131q20 6 40.5 19.5t20.5 28.5v342q0 7 -81 90t-94 83h-525q-17 0 -35.5 -14t-28.5 -28l-10 -15zM77 565l236 339h503 l89 -100v-294l-340 -130q-20 -6 -40 -20t-20 -29v-202q0 -22 -25 -31t-50 0t-25 31v456v14.5t-1.5 11.5t-5 12t-9.5 7q-24 13 -46 -5l-184 -146zM305 1104v200h600v-200h-600z" />
<glyph unicode="&#xe131;" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM298 701l2 -201h300l-2 -194l402 294l-402 298v-197h-300z" />
<glyph unicode="&#xe132;" d="M0 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t231.5 47.5q122 0 232.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-218 -217.5t-300 -80t-299.5 80t-217.5 217.5t-80 299.5zM200 600l402 -294l-2 194h300l2 201h-300v197z" />
<glyph unicode="&#xe133;" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM300 600h200v-300h200v300h200l-300 400z" />
<glyph unicode="&#xe134;" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM300 600l300 -400l300 400h-200v300h-200v-300h-200z" />
<glyph unicode="&#xe135;" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q121 0 231.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM254 780q-8 -33 5.5 -92.5t7.5 -87.5q0 -9 17 -44t16 -60 q12 0 23 -5.5t23 -15t20 -13.5q24 -12 108 -42q22 -8 53 -31.5t59.5 -38.5t57.5 -11q8 -18 -15 -55t-20 -57q42 -71 87 -80q0 -6 -3 -15.5t-3.5 -14.5t4.5 -17q104 -3 221 112q30 29 47 47t34.5 49t20.5 62q-14 9 -37 9.5t-36 7.5q-14 7 -49 15t-52 19q-9 0 -39.5 -0.5 t-46.5 -1.5t-39 -6.5t-39 -16.5q-50 -35 -66 -12q-4 2 -3.5 25.5t0.5 25.5q-6 13 -26.5 17t-24.5 7q2 22 -2 41t-16.5 28t-38.5 -20q-23 -25 -42 4q-19 28 -8 58q6 16 22 22q6 -1 26 -1.5t33.5 -4t19.5 -13.5q12 -19 32 -37.5t34 -27.5l14 -8q0 3 9.5 39.5t5.5 57.5 q-4 23 14.5 44.5t22.5 31.5q5 14 10 35t8.5 31t15.5 22.5t34 21.5q-6 18 10 37q8 0 23.5 -1.5t24.5 -1.5t20.5 4.5t20.5 15.5q-10 23 -30.5 42.5t-38 30t-49 26.5t-43.5 23q11 39 2 44q31 -13 58 -14.5t39 3.5l11 4q7 36 -16.5 53.5t-64.5 28.5t-56 23q-19 -3 -37 0 q-15 -12 -36.5 -21t-34.5 -12t-44 -8t-39 -6q-15 -3 -45.5 0.5t-45.5 -2.5q-21 -7 -52 -26.5t-34 -34.5q-3 -11 6.5 -22.5t8.5 -18.5q-3 -34 -27.5 -90.5t-29.5 -79.5zM518 916q3 12 16 30t16 25q10 -10 18.5 -10t14 6t14.5 14.5t16 12.5q0 -24 17 -66.5t17 -43.5 q-9 2 -31 5t-36 5t-32 8t-30 14zM692 1003h1h-1z" />
<glyph unicode="&#xe136;" d="M0 164.5q0 21.5 15 37.5l600 599q-33 101 6 201.5t135 154.5q164 92 306 -9l-259 -138l145 -232l251 126q13 -175 -151 -267q-123 -70 -253 -23l-596 -596q-15 -16 -36.5 -16t-36.5 16l-111 110q-15 15 -15 36.5z" />
<glyph unicode="&#xe137;" horiz-adv-x="1220" d="M0 196v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM0 596v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5zM0 996v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM600 596h500v100h-500v-100zM800 196h300v100h-300v-100zM900 996h200v100h-200v-100z" />
<glyph unicode="&#xe138;" d="M100 1100v100h1000v-100h-1000zM150 1000h900l-350 -500v-300l-200 -200v500z" />
<glyph unicode="&#xe139;" d="M0 200v200h1200v-200q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM0 500v400q0 41 29.5 70.5t70.5 29.5h300v100q0 41 29.5 70.5t70.5 29.5h200q41 0 70.5 -29.5t29.5 -70.5v-100h300q41 0 70.5 -29.5t29.5 -70.5v-400h-500v100h-200v-100h-500z M500 1000h200v100h-200v-100z" />
<glyph unicode="&#xe140;" d="M0 0v400l129 -129l200 200l142 -142l-200 -200l129 -129h-400zM0 800l129 129l200 -200l142 142l-200 200l129 129h-400v-400zM729 329l142 142l200 -200l129 129v-400h-400l129 129zM729 871l200 200l-129 129h400v-400l-129 129l-200 -200z" />
<glyph unicode="&#xe141;" d="M0 596q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM182 596q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM291 655 q0 23 15.5 38.5t38.5 15.5t39 -16t16 -38q0 -23 -16 -39t-39 -16q-22 0 -38 16t-16 39zM400 850q0 22 16 38.5t39 16.5q22 0 38 -16t16 -39t-16 -39t-38 -16q-23 0 -39 16.5t-16 38.5zM514 609q0 32 20.5 56.5t51.5 29.5l122 126l1 1q-9 14 -9 28q0 22 16 38.5t39 16.5 q22 0 38 -16t16 -39t-16 -39t-38 -16q-14 0 -29 10l-55 -145q17 -22 17 -51q0 -36 -25.5 -61.5t-61.5 -25.5t-61.5 25.5t-25.5 61.5zM800 655q0 22 16 38t39 16t38.5 -15.5t15.5 -38.5t-16 -39t-38 -16q-23 0 -39 16t-16 39z" />
<glyph unicode="&#xe142;" d="M-40 375q-13 -95 35 -173q35 -57 94 -89t129 -32q63 0 119 28q33 16 65 40.5t52.5 45.5t59.5 64q40 44 57 61l394 394q35 35 47 84t-3 96q-27 87 -117 104q-20 2 -29 2q-46 0 -78.5 -16.5t-67.5 -51.5l-389 -396l-7 -7l69 -67l377 373q20 22 39 38q23 23 50 23 q38 0 53 -36q16 -39 -20 -75l-547 -547q-52 -52 -125 -52q-55 0 -100 33t-54 96q-5 35 2.5 66t31.5 63t42 50t56 54q24 21 44 41l348 348q52 52 82.5 79.5t84 54t107.5 26.5q25 0 48 -4q95 -17 154 -94.5t51 -175.5q-7 -101 -98 -192l-252 -249l-253 -256l7 -7l69 -60 l517 511q67 67 95 157t11 183q-16 87 -67 154t-130 103q-69 33 -152 33q-107 0 -197 -55q-40 -24 -111 -95l-512 -512q-68 -68 -81 -163z" />
<glyph unicode="&#xe143;" d="M80 784q0 131 98.5 229.5t230.5 98.5q143 0 241 -129q103 129 246 129q129 0 226 -98.5t97 -229.5q0 -46 -17.5 -91t-61 -99t-77 -89.5t-104.5 -105.5q-197 -191 -293 -322l-17 -23l-16 23q-43 58 -100 122.5t-92 99.5t-101 100q-71 70 -104.5 105.5t-77 89.5t-61 99 t-17.5 91zM250 784q0 -27 30.5 -70t61.5 -75.5t95 -94.5l22 -22q93 -90 190 -201q82 92 195 203l12 12q64 62 97.5 97t64.5 79t31 72q0 71 -48 119.5t-105 48.5q-74 0 -132 -83l-118 -171l-114 174q-51 80 -123 80q-60 0 -109.5 -49.5t-49.5 -118.5z" />
<glyph unicode="&#xe144;" d="M57 353q0 -95 66 -159l141 -142q68 -66 159 -66q93 0 159 66l283 283q66 66 66 159t-66 159l-141 141q-8 9 -19 17l-105 -105l212 -212l-389 -389l-247 248l95 95l-18 18q-46 45 -75 101l-55 -55q-66 -66 -66 -159zM269 706q0 -93 66 -159l141 -141q7 -7 19 -17l105 105 l-212 212l389 389l247 -247l-95 -96l18 -17q47 -49 77 -100l29 29q35 35 62.5 88t27.5 96q0 93 -66 159l-141 141q-66 66 -159 66q-95 0 -159 -66l-283 -283q-66 -64 -66 -159z" />
<glyph unicode="&#xe145;" d="M200 100v953q0 21 30 46t81 48t129 38t163 15t162 -15t127 -38t79 -48t29 -46v-953q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-41 0 -70.5 29.5t-29.5 70.5zM300 300h600v700h-600v-700zM496 150q0 -43 30.5 -73.5t73.5 -30.5t73.5 30.5t30.5 73.5t-30.5 73.5t-73.5 30.5 t-73.5 -30.5t-30.5 -73.5z" />
<glyph unicode="&#xe146;" d="M0 0l303 380l207 208l-210 212h300l267 279l-35 36q-15 14 -15 35t15 35q14 15 35 15t35 -15l283 -282q15 -15 15 -36t-15 -35q-14 -15 -35 -15t-35 15l-36 35l-279 -267v-300l-212 210l-208 -207z" />
<glyph unicode="&#xe148;" d="M295 433h139q5 -77 48.5 -126.5t117.5 -64.5v335q-6 1 -15.5 4t-11.5 3q-46 14 -79 26.5t-72 36t-62.5 52t-40 72.5t-16.5 99q0 92 44 159.5t109 101t144 40.5v78h100v-79q38 -4 72.5 -13.5t75.5 -31.5t71 -53.5t51.5 -84t24.5 -118.5h-159q-8 72 -35 109.5t-101 50.5 v-307l64 -14q34 -7 64 -16.5t70 -31.5t67.5 -52t47.5 -80.5t20 -112.5q0 -139 -89 -224t-244 -96v-77h-100v78q-152 17 -237 104q-40 40 -52.5 93.5t-15.5 139.5zM466 889q0 -29 8 -51t16.5 -34t29.5 -22.5t31 -13.5t38 -10q7 -2 11 -3v274q-61 -8 -97.5 -37.5t-36.5 -102.5 zM700 237q170 18 170 151q0 64 -44 99.5t-126 60.5v-311z" />
<glyph unicode="&#xe149;" d="M100 600v100h166q-24 49 -44 104q-10 26 -14.5 55.5t-3 72.5t25 90t68.5 87q97 88 263 88q129 0 230 -89t101 -208h-153q0 52 -34 89.5t-74 51.5t-76 14q-37 0 -79 -14.5t-62 -35.5q-41 -44 -41 -101q0 -28 16.5 -69.5t28 -62.5t41.5 -72h241v-100h-197q8 -50 -2.5 -115 t-31.5 -94q-41 -59 -99 -113q35 11 84 18t70 7q33 1 103 -16t103 -17q76 0 136 30l50 -147q-41 -25 -80.5 -36.5t-59 -13t-61.5 -1.5q-23 0 -128 33t-155 29q-39 -4 -82 -17t-66 -25l-24 -11l-55 145l16.5 11t15.5 10t13.5 9.5t14.5 12t14.5 14t17.5 18.5q48 55 54 126.5 t-30 142.5h-221z" />
<glyph unicode="&#xe150;" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM602 900l298 300l298 -300h-198v-900h-200v900h-198z" />
<glyph unicode="&#xe151;" d="M2 300h198v900h200v-900h198l-298 -300zM700 0v200h100v-100h200v-100h-300zM700 400v100h300v-200h-99v-100h-100v100h99v100h-200zM700 700v500h300v-500h-100v100h-100v-100h-100zM801 900h100v200h-100v-200z" />
<glyph unicode="&#xe152;" d="M2 300h198v900h200v-900h198l-298 -300zM700 0v500h300v-500h-100v100h-100v-100h-100zM700 700v200h100v-100h200v-100h-300zM700 1100v100h300v-200h-99v-100h-100v100h99v100h-200zM801 200h100v200h-100v-200z" />
<glyph unicode="&#xe153;" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM800 100v400h300v-500h-100v100h-200zM800 1100v100h200v-500h-100v400h-100zM901 200h100v200h-100v-200z" />
<glyph unicode="&#xe154;" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM800 400v100h200v-500h-100v400h-100zM800 800v400h300v-500h-100v100h-200zM901 900h100v200h-100v-200z" />
<glyph unicode="&#xe155;" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM700 100v200h500v-200h-500zM700 400v200h400v-200h-400zM700 700v200h300v-200h-300zM700 1000v200h200v-200h-200z" />
<glyph unicode="&#xe156;" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM700 100v200h200v-200h-200zM700 400v200h300v-200h-300zM700 700v200h400v-200h-400zM700 1000v200h500v-200h-500z" />
<glyph unicode="&#xe157;" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q162 0 281 -118.5t119 -281.5v-300q0 -165 -118.5 -282.5t-281.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500z" />
<glyph unicode="&#xe158;" d="M0 400v300q0 163 119 281.5t281 118.5h300q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-163 0 -281.5 117.5t-118.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM400 300l333 250l-333 250v-500z" />
<glyph unicode="&#xe159;" d="M0 400v300q0 163 117.5 281.5t282.5 118.5h300q163 0 281.5 -119t118.5 -281v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM300 700l250 -333l250 333h-500z" />
<glyph unicode="&#xe160;" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q165 0 282.5 -117.5t117.5 -282.5v-300q0 -162 -118.5 -281t-281.5 -119h-300q-165 0 -282.5 118.5t-117.5 281.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM300 400h500l-250 333z" />
<glyph unicode="&#xe161;" d="M0 400v300h300v200l400 -350l-400 -350v200h-300zM500 0v200h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-500v200h400q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-400z" />
<glyph unicode="&#xe162;" d="M217 519q8 -19 31 -19h302q-155 -438 -160 -458q-5 -21 4 -32l9 -8h9q14 0 26 15q11 13 274.5 321.5t264.5 308.5q14 19 5 36q-8 17 -31 17l-301 -1q1 4 78 219.5t79 227.5q2 15 -5 27l-9 9h-9q-15 0 -25 -16q-4 -6 -98 -111.5t-228.5 -257t-209.5 -237.5q-16 -19 -6 -41 z" />
<glyph unicode="&#xe163;" d="M0 400q0 -165 117.5 -282.5t282.5 -117.5h300q47 0 100 15v185h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h500v185q-14 4 -114 7.5t-193 5.5l-93 2q-165 0 -282.5 -117.5t-117.5 -282.5v-300zM600 400v300h300v200l400 -350l-400 -350v200h-300z " />
<glyph unicode="&#xe164;" d="M0 400q0 -165 117.5 -282.5t282.5 -117.5h300q163 0 281.5 117.5t118.5 282.5v98l-78 73l-122 -123v-148q0 -41 -29.5 -70.5t-70.5 -29.5h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h156l118 122l-74 78h-100q-165 0 -282.5 -117.5t-117.5 -282.5 v-300zM496 709l353 342l-149 149h500v-500l-149 149l-342 -353z" />
<glyph unicode="&#xe165;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM406 600 q0 80 57 137t137 57t137 -57t57 -137t-57 -137t-137 -57t-137 57t-57 137z" />
<glyph unicode="&#xe166;" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 800l445 -500l450 500h-295v400h-300v-400h-300zM900 150h100v50h-100v-50z" />
<glyph unicode="&#xe167;" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 700h300v-300h300v300h295l-445 500zM900 150h100v50h-100v-50z" />
<glyph unicode="&#xe168;" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 705l305 -305l596 596l-154 155l-442 -442l-150 151zM900 150h100v50h-100v-50z" />
<glyph unicode="&#xe169;" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 988l97 -98l212 213l-97 97zM200 400l697 1l3 699l-250 -239l-149 149l-212 -212l149 -149zM900 150h100v50h-100v-50z" />
<glyph unicode="&#xe170;" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM200 612l212 -212l98 97l-213 212zM300 1200l239 -250l-149 -149l212 -212l149 148l249 -237l-1 697zM900 150h100v50h-100v-50z" />
<glyph unicode="&#xe171;" d="M23 415l1177 784v-1079l-475 272l-310 -393v416h-392zM494 210l672 938l-672 -712v-226z" />
<glyph unicode="&#xe172;" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-850q0 -21 -15 -35.5t-35 -14.5h-150v400h-700v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 1000h100v200h-100v-200z" />
<glyph unicode="&#xe173;" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-218l-276 -275l-120 120l-126 -127h-378v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM581 306l123 123l120 -120l353 352l123 -123l-475 -476zM600 1000h100v200h-100v-200z" />
<glyph unicode="&#xe174;" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-269l-103 -103l-170 170l-298 -298h-329v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 1000h100v200h-100v-200zM700 133l170 170l-170 170l127 127l170 -170l170 170l127 -128l-170 -169l170 -170 l-127 -127l-170 170l-170 -170z" />
<glyph unicode="&#xe175;" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-300h-400v-200h-500v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 300l300 -300l300 300h-200v300h-200v-300h-200zM600 1000v200h100v-200h-100z" />
<glyph unicode="&#xe176;" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-402l-200 200l-298 -298h-402v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 300h200v-300h200v300h200l-300 300zM600 1000v200h100v-200h-100z" />
<glyph unicode="&#xe177;" d="M0 250q0 -21 14.5 -35.5t35.5 -14.5h1100q21 0 35.5 14.5t14.5 35.5v550h-1200v-550zM0 900h1200v150q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-150zM100 300v200h400v-200h-400z" />
<glyph unicode="&#xe178;" d="M0 400l300 298v-198h400v-200h-400v-198zM100 800v200h100v-200h-100zM300 800v200h100v-200h-100zM500 800v200h400v198l300 -298l-300 -298v198h-400zM800 300v200h100v-200h-100zM1000 300h100v200h-100v-200z" />
<glyph unicode="&#xe179;" d="M100 700v400l50 100l50 -100v-300h100v300l50 100l50 -100v-300h100v300l50 100l50 -100v-400l-100 -203v-447q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v447zM800 597q0 -29 10.5 -55.5t25 -43t29 -28.5t25.5 -18l10 -5v-397q0 -21 14.5 -35.5 t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v1106q0 31 -18 40.5t-44 -7.5l-276 -116q-25 -17 -43.5 -51.5t-18.5 -65.5v-359z" />
<glyph unicode="&#xe180;" d="M100 0h400v56q-75 0 -87.5 6t-12.5 44v394h500v-394q0 -38 -12.5 -44t-87.5 -6v-56h400v56q-4 0 -11 0.5t-24 3t-30 7t-24 15t-11 24.5v888q0 22 25 34.5t50 13.5l25 2v56h-400v-56q75 0 87.5 -6t12.5 -44v-394h-500v394q0 38 12.5 44t87.5 6v56h-400v-56q4 0 11 -0.5 t24 -3t30 -7t24 -15t11 -24.5v-888q0 -22 -25 -34.5t-50 -13.5l-25 -2v-56z" />
<glyph unicode="&#xe181;" d="M0 300q0 -41 29.5 -70.5t70.5 -29.5h300q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-300q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM100 100h400l200 200h105l295 98v-298h-425l-100 -100h-375zM100 300v200h300v-200h-300zM100 600v200h300v-200h-300z M100 1000h400l200 -200v-98l295 98h105v200h-425l-100 100h-375zM700 402v163l400 133v-163z" />
<glyph unicode="&#xe182;" d="M16.5 974.5q0.5 -21.5 16 -90t46.5 -140t104 -177.5t175 -208q103 -103 207.5 -176t180 -103.5t137 -47t92.5 -16.5l31 1l163 162q17 18 13.5 41t-22.5 37l-192 136q-19 14 -45 12t-42 -19l-118 -118q-142 101 -268 227t-227 268l118 118q17 17 20 41.5t-11 44.5 l-139 194q-14 19 -36.5 22t-40.5 -14l-162 -162q-1 -11 -0.5 -32.5z" />
<glyph unicode="&#xe183;" d="M0 50v212q0 20 10.5 45.5t24.5 39.5l365 303v50q0 4 1 10.5t12 22.5t30 28.5t60 23t97 10.5t97 -10t60 -23.5t30 -27.5t12 -24l1 -10v-50l365 -303q14 -14 24.5 -39.5t10.5 -45.5v-212q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-20 0 -35 14.5t-15 35.5zM0 712 q0 -21 14.5 -33.5t34.5 -8.5l202 33q20 4 34.5 21t14.5 38v146q141 24 300 24t300 -24v-146q0 -21 14.5 -38t34.5 -21l202 -33q20 -4 34.5 8.5t14.5 33.5v200q-6 8 -19 20.5t-63 45t-112 57t-171 45t-235 20.5q-92 0 -175 -10.5t-141.5 -27t-108.5 -36.5t-81.5 -40 t-53.5 -36.5t-31 -27.5l-9 -10v-200z" />
<glyph unicode="&#xe184;" d="M100 0v100h1100v-100h-1100zM175 200h950l-125 150v250l100 100v400h-100v-200h-100v200h-200v-200h-100v200h-200v-200h-100v200h-100v-400l100 -100v-250z" />
<glyph unicode="&#xe185;" d="M100 0h300v400q0 41 -29.5 70.5t-70.5 29.5h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-400zM500 0v1000q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-1000h-300zM900 0v700q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-700h-300z" />
<glyph unicode="&#xe186;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v300h-200v100h200v100h-300v-300h200v-100h-200v-100zM600 300h200v100h100v300h-100v100h-200v-500 zM700 400v300h100v-300h-100z" />
<glyph unicode="&#xe187;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h100v200h100v-200h100v500h-100v-200h-100v200h-100v-500zM600 300h200v100h100v300h-100v100h-200v-500 zM700 400v300h100v-300h-100z" />
<glyph unicode="&#xe188;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v100h-200v300h200v100h-300v-500zM600 300h300v100h-200v300h200v100h-300v-500z" />
<glyph unicode="&#xe189;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 550l300 -150v300zM600 400l300 150l-300 150v-300z" />
<glyph unicode="&#xe190;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300v500h700v-500h-700zM300 400h130q41 0 68 42t27 107t-28.5 108t-66.5 43h-130v-300zM575 549 q0 -65 27 -107t68 -42h130v300h-130q-38 0 -66.5 -43t-28.5 -108z" />
<glyph unicode="&#xe191;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v300h-200v100h200v100h-300v-300h200v-100h-200v-100zM601 300h100v100h-100v-100zM700 700h100 v-400h100v500h-200v-100z" />
<glyph unicode="&#xe192;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v400h-200v100h-100v-500zM301 400v200h100v-200h-100zM601 300h100v100h-100v-100zM700 700h100 v-400h100v500h-200v-100z" />
<glyph unicode="&#xe193;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 700v100h300v-300h-99v-100h-100v100h99v200h-200zM201 300v100h100v-100h-100zM601 300v100h100v-100h-100z M700 700v100h200v-500h-100v400h-100z" />
<glyph unicode="&#xe194;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM400 500v200 l100 100h300v-100h-300v-200h300v-100h-300z" />
<glyph unicode="&#xe195;" d="M0 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM182 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM400 400v400h300 l100 -100v-100h-100v100h-200v-100h200v-100h-200v-100h-100zM700 400v100h100v-100h-100z" />
<glyph unicode="&#xe197;" d="M-14 494q0 -80 56.5 -137t135.5 -57h222v300h400v-300h128q120 0 205 86.5t85 207.5t-85 207t-205 86q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5zM300 200h200v300h200v-300h200 l-300 -300z" />
<glyph unicode="&#xe198;" d="M-14 494q0 -80 56.5 -137t135.5 -57h8l414 414l403 -403q94 26 154.5 104.5t60.5 178.5q0 120 -85 206.5t-205 86.5q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5zM300 200l300 300 l300 -300h-200v-300h-200v300h-200z" />
<glyph unicode="&#xe199;" d="M100 200h400v-155l-75 -45h350l-75 45v155h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170z" />
<glyph unicode="&#xe200;" d="M121 700q0 -53 28.5 -97t75.5 -65q-4 -16 -4 -38q0 -74 52.5 -126.5t126.5 -52.5q56 0 100 30v-306l-75 -45h350l-75 45v306q46 -30 100 -30q74 0 126.5 52.5t52.5 126.5q0 24 -9 55q50 32 79.5 83t29.5 112q0 90 -61.5 155.5t-150.5 71.5q-26 89 -99.5 145.5 t-167.5 56.5q-116 0 -197.5 -81.5t-81.5 -197.5q0 -4 1 -11.5t1 -11.5q-14 2 -23 2q-74 0 -126.5 -52.5t-52.5 -126.5z" />
</font>
</defs></svg>

До

Ширина:  |  Высота:  |  Размер: 62 KiB

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -1,793 +0,0 @@
'use strict';
/*
HTTP Hawk Authentication Scheme
Copyright (c) 2012-2016, Eran Hammer <eran@hammer.io>
BSD Licensed
*/
// Declare namespace
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var hawk = {
internals: {}
};
hawk.client = {
// Generate an Authorization header for a given request
/*
uri: 'http://example.com/resource?a=b' or object generated by hawk.utils.parseUri()
method: HTTP verb (e.g. 'GET', 'POST')
options: {
// Required
credentials: {
id: 'dh37fgj492je',
key: 'aoijedoaijsdlaksjdl',
algorithm: 'sha256' // 'sha1', 'sha256'
},
// Optional
ext: 'application-specific', // Application specific data sent via the ext attribute
timestamp: Date.now() / 1000, // A pre-calculated timestamp in seconds
nonce: '2334f34f', // A pre-generated nonce
localtimeOffsetMsec: 400, // Time offset to sync with server time (ignored if timestamp provided)
payload: '{"some":"payload"}', // UTF-8 encoded string for body hash generation (ignored if hash provided)
contentType: 'application/json', // Payload content-type (ignored if hash provided)
hash: 'U4MKKSmiVxk37JCCrAVIjV=', // Pre-calculated payload hash
app: '24s23423f34dx', // Oz application id
dlg: '234sz34tww3sd' // Oz delegated-by application id
}
*/
header: function header(uri, method, options) {
var result = {
field: '',
artifacts: {}
};
// Validate inputs
if (!uri || typeof uri !== 'string' && (typeof uri === 'undefined' ? 'undefined' : _typeof(uri)) !== 'object' || !method || typeof method !== 'string' || !options || (typeof options === 'undefined' ? 'undefined' : _typeof(options)) !== 'object') {
result.err = 'Invalid argument type';
return result;
}
// Application time
var timestamp = options.timestamp || hawk.utils.nowSec(options.localtimeOffsetMsec);
// Validate credentials
var credentials = options.credentials;
if (!credentials || !credentials.id || !credentials.key || !credentials.algorithm) {
result.err = 'Invalid credentials object';
return result;
}
if (hawk.crypto.algorithms.indexOf(credentials.algorithm) === -1) {
result.err = 'Unknown algorithm';
return result;
}
// Parse URI
if (typeof uri === 'string') {
uri = hawk.utils.parseUri(uri);
}
// Calculate signature
var artifacts = {
ts: timestamp,
nonce: options.nonce || hawk.utils.randomString(6),
method: method,
resource: uri.resource,
host: uri.host,
port: uri.port,
hash: options.hash,
ext: options.ext,
app: options.app,
dlg: options.dlg
};
result.artifacts = artifacts;
// Calculate payload hash
if (!artifacts.hash && (options.payload || options.payload === '')) {
artifacts.hash = hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, options.contentType);
}
var mac = hawk.crypto.calculateMac('header', credentials, artifacts);
// Construct header
var hasExt = artifacts.ext !== null && artifacts.ext !== undefined && artifacts.ext !== ''; // Other falsey values allowed
var header = 'Hawk id="' + credentials.id + '", ts="' + artifacts.ts + '", nonce="' + artifacts.nonce + (artifacts.hash ? '", hash="' + artifacts.hash : '') + (hasExt ? '", ext="' + hawk.utils.escapeHeaderAttribute(artifacts.ext) : '') + '", mac="' + mac + '"';
if (artifacts.app) {
header += ', app="' + artifacts.app + (artifacts.dlg ? '", dlg="' + artifacts.dlg : '') + '"';
}
result.field = header;
return result;
},
// Generate a bewit value for a given URI
/*
uri: 'http://example.com/resource?a=b'
options: {
// Required
credentials: {
id: 'dh37fgj492je',
key: 'aoijedoaijsdlaksjdl',
algorithm: 'sha256' // 'sha1', 'sha256'
},
ttlSec: 60 * 60, // TTL in seconds
// Optional
ext: 'application-specific', // Application specific data sent via the ext attribute
localtimeOffsetMsec: 400 // Time offset to sync with server time
};
*/
bewit: function bewit(uri, options) {
// Validate inputs
if (!uri || typeof uri !== 'string' || !options || (typeof options === 'undefined' ? 'undefined' : _typeof(options)) !== 'object' || !options.ttlSec) {
return '';
}
options.ext = options.ext === null || options.ext === undefined ? '' : options.ext; // Zero is valid value
// Application time
var now = hawk.utils.nowSec(options.localtimeOffsetMsec);
// Validate credentials
var credentials = options.credentials;
if (!credentials || !credentials.id || !credentials.key || !credentials.algorithm) {
return '';
}
if (hawk.crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return '';
}
// Parse URI
uri = hawk.utils.parseUri(uri);
// Calculate signature
var exp = now + options.ttlSec;
var mac = hawk.crypto.calculateMac('bewit', credentials, {
ts: exp,
nonce: '',
method: 'GET',
resource: uri.resource, // Maintain trailing '?' and query params
host: uri.host,
port: uri.port,
ext: options.ext
});
// Construct bewit: id\exp\mac\ext
var bewit = credentials.id + '\\' + exp + '\\' + mac + '\\' + options.ext;
return hawk.utils.base64urlEncode(bewit);
},
// Validate server response
/*
request: object created via 'new XMLHttpRequest()' after response received or fetch API 'Response'
artifacts: object received from header().artifacts
options: {
payload: optional payload received
required: specifies if a Server-Authorization header is required. Defaults to 'false'
}
*/
authenticate: function authenticate(request, credentials, artifacts, options) {
options = options || {};
var getHeader = function getHeader(name) {
// Fetch API or plain headers
if (request.headers) {
return typeof request.headers.get === 'function' ? request.headers.get(name) : request.headers[name];
}
// XMLHttpRequest
return request.getResponseHeader ? request.getResponseHeader(name) : request.getHeader(name);
};
var wwwAuthenticate = getHeader('www-authenticate');
if (wwwAuthenticate) {
// Parse HTTP WWW-Authenticate header
var wwwAttributes = hawk.utils.parseAuthorizationHeader(wwwAuthenticate, ['ts', 'tsm', 'error']);
if (!wwwAttributes) {
return false;
}
if (wwwAttributes.ts) {
var tsm = hawk.crypto.calculateTsMac(wwwAttributes.ts, credentials);
if (tsm !== wwwAttributes.tsm) {
return false;
}
hawk.utils.setNtpSecOffset(wwwAttributes.ts - Math.floor(Date.now() / 1000)); // Keep offset at 1 second precision
}
}
// Parse HTTP Server-Authorization header
var serverAuthorization = getHeader('server-authorization');
if (!serverAuthorization && !options.required) {
return true;
}
var attributes = hawk.utils.parseAuthorizationHeader(serverAuthorization, ['mac', 'ext', 'hash']);
if (!attributes) {
return false;
}
var modArtifacts = {
ts: artifacts.ts,
nonce: artifacts.nonce,
method: artifacts.method,
resource: artifacts.resource,
host: artifacts.host,
port: artifacts.port,
hash: attributes.hash,
ext: attributes.ext,
app: artifacts.app,
dlg: artifacts.dlg
};
var mac = hawk.crypto.calculateMac('response', credentials, modArtifacts);
if (mac !== attributes.mac) {
return false;
}
if (!options.payload && options.payload !== '') {
return true;
}
if (!attributes.hash) {
return false;
}
var calculatedHash = hawk.crypto.calculatePayloadHash(options.payload, credentials.algorithm, getHeader('content-type'));
return calculatedHash === attributes.hash;
},
message: function message(host, port, _message, options) {
// Validate inputs
if (!host || typeof host !== 'string' || !port || typeof port !== 'number' || _message === null || _message === undefined || typeof _message !== 'string' || !options || (typeof options === 'undefined' ? 'undefined' : _typeof(options)) !== 'object') {
return null;
}
// Application time
var timestamp = options.timestamp || hawk.utils.nowSec(options.localtimeOffsetMsec);
// Validate credentials
var credentials = options.credentials;
if (!credentials || !credentials.id || !credentials.key || !credentials.algorithm) {
// Invalid credential object
return null;
}
if (hawk.crypto.algorithms.indexOf(credentials.algorithm) === -1) {
return null;
}
// Calculate signature
var artifacts = {
ts: timestamp,
nonce: options.nonce || hawk.utils.randomString(6),
host: host,
port: port,
hash: hawk.crypto.calculatePayloadHash(_message, credentials.algorithm)
};
// Construct authorization
var result = {
id: credentials.id,
ts: artifacts.ts,
nonce: artifacts.nonce,
hash: artifacts.hash,
mac: hawk.crypto.calculateMac('message', credentials, artifacts)
};
return result;
},
authenticateTimestamp: function authenticateTimestamp(message, credentials, updateClock) {
// updateClock defaults to true
var tsm = hawk.crypto.calculateTsMac(message.ts, credentials);
if (tsm !== message.tsm) {
return false;
}
if (updateClock !== false) {
hawk.utils.setNtpSecOffset(message.ts - Math.floor(Date.now() / 1000)); // Keep offset at 1 second precision
}
return true;
}
};
hawk.crypto = {
headerVersion: '1',
algorithms: ['sha1', 'sha256'],
calculateMac: function calculateMac(type, credentials, options) {
var normalized = hawk.crypto.generateNormalizedString(type, options);
var hmac = CryptoJS['Hmac' + credentials.algorithm.toUpperCase()](normalized, credentials.key);
return hmac.toString(CryptoJS.enc.Base64);
},
generateNormalizedString: function generateNormalizedString(type, options) {
var normalized = 'hawk.' + hawk.crypto.headerVersion + '.' + type + '\n' + options.ts + '\n' + options.nonce + '\n' + (options.method || '').toUpperCase() + '\n' + (options.resource || '') + '\n' + options.host.toLowerCase() + '\n' + options.port + '\n' + (options.hash || '') + '\n';
if (options.ext) {
normalized += options.ext.replace('\\', '\\\\').replace('\n', '\\n');
}
normalized += '\n';
if (options.app) {
normalized += options.app + '\n' + (options.dlg || '') + '\n';
}
return normalized;
},
calculatePayloadHash: function calculatePayloadHash(payload, algorithm, contentType) {
var hash = CryptoJS.algo[algorithm.toUpperCase()].create();
hash.update('hawk.' + hawk.crypto.headerVersion + '.payload\n');
hash.update(hawk.utils.parseContentType(contentType) + '\n');
hash.update(payload);
hash.update('\n');
return hash.finalize().toString(CryptoJS.enc.Base64);
},
calculateTsMac: function calculateTsMac(ts, credentials) {
var hash = CryptoJS['Hmac' + credentials.algorithm.toUpperCase()]('hawk.' + hawk.crypto.headerVersion + '.ts\n' + ts + '\n', credentials.key);
return hash.toString(CryptoJS.enc.Base64);
}
};
// localStorage compatible interface
hawk.internals.LocalStorage = function () {
this._cache = {};
this.length = 0;
this.getItem = function (key) {
return this._cache.hasOwnProperty(key) ? String(this._cache[key]) : null;
};
this.setItem = function (key, value) {
this._cache[key] = String(value);
this.length = Object.keys(this._cache).length;
};
this.removeItem = function (key) {
delete this._cache[key];
this.length = Object.keys(this._cache).length;
};
this.clear = function () {
this._cache = {};
this.length = 0;
};
this.key = function (i) {
return Object.keys(this._cache)[i || 0];
};
};
hawk.utils = {
storage: new hawk.internals.LocalStorage(),
setStorage: function setStorage(storage) {
var ntpOffset = hawk.utils.storage.getItem('hawk_ntp_offset');
hawk.utils.storage = storage;
if (ntpOffset) {
hawk.utils.setNtpSecOffset(ntpOffset);
}
},
setNtpSecOffset: function setNtpSecOffset(offset) {
try {
hawk.utils.storage.setItem('hawk_ntp_offset', offset);
} catch (err) {
console.error('[hawk] could not write to storage.');
console.error(err);
}
},
getNtpSecOffset: function getNtpSecOffset() {
var offset = hawk.utils.storage.getItem('hawk_ntp_offset');
if (!offset) {
return 0;
}
return parseInt(offset, 10);
},
now: function now(localtimeOffsetMsec) {
return Date.now() + (localtimeOffsetMsec || 0) + hawk.utils.getNtpSecOffset() * 1000;
},
nowSec: function nowSec(localtimeOffsetMsec) {
return Math.floor(hawk.utils.now(localtimeOffsetMsec) / 1000);
},
escapeHeaderAttribute: function escapeHeaderAttribute(attribute) {
return attribute.replace(/\\/g, '\\\\').replace(/\"/g, '\\"');
},
parseContentType: function parseContentType(header) {
if (!header) {
return '';
}
return header.split(';')[0].replace(/^\s+|\s+$/g, '').toLowerCase();
},
parseAuthorizationHeader: function parseAuthorizationHeader(header, keys) {
if (!header) {
return null;
}
var headerParts = header.match(/^(\w+)(?:\s+(.*))?$/); // Header: scheme[ something]
if (!headerParts) {
return null;
}
var scheme = headerParts[1];
if (scheme.toLowerCase() !== 'hawk') {
return null;
}
var attributesString = headerParts[2];
if (!attributesString) {
return null;
}
var attributes = {};
var verify = attributesString.replace(/(\w+)="([^"\\]*)"\s*(?:,\s*|$)/g, function ($0, $1, $2) {
// Check valid attribute names
if (keys.indexOf($1) === -1) {
return;
}
// Allowed attribute value characters: !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9
if ($2.match(/^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~]+$/) === null) {
return;
}
// Check for duplicates
if (attributes.hasOwnProperty($1)) {
return;
}
attributes[$1] = $2;
return '';
});
if (verify !== '') {
return null;
}
return attributes;
},
randomString: function randomString(size) {
var randomSource = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
var len = randomSource.length;
var result = [];
for (var i = 0; i < size; ++i) {
result[i] = randomSource[Math.floor(Math.random() * len)];
}
return result.join('');
},
// 1 2 3 4
uriRegex: /^([^:]+)\:\/\/(?:[^@/]*@)?([^\/:]+)(?:\:(\d+))?([^#]*)(?:#.*)?$/, // scheme://credentials@host:port/resource#fragment
parseUri: function parseUri(input) {
var parts = input.match(hawk.utils.uriRegex);
if (!parts) {
return { host: '', port: '', resource: '' };
}
var scheme = parts[1].toLowerCase();
var uri = {
host: parts[2],
port: parts[3] || (scheme === 'http' ? '80' : scheme === 'https' ? '443' : ''),
resource: parts[4]
};
return uri;
},
base64urlEncode: function base64urlEncode(value) {
var wordArray = CryptoJS.enc.Utf8.parse(value);
var encoded = CryptoJS.enc.Base64.stringify(wordArray);
return encoded.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');
}
};
// $lab:coverage:off$
/* eslint-disable */
// Based on: Crypto-JS v3.1.2
// Copyright (c) 2009-2013, Jeff Mott. All rights reserved.
// http://code.google.com/p/crypto-js/
// http://code.google.com/p/crypto-js/wiki/License
var CryptoJS = CryptoJS || function (h, r) {
var k = {},
l = k.lib = {},
n = function n() {},
f = l.Base = { extend: function extend(a) {
n.prototype = this;var b = new n();a && b.mixIn(a);b.hasOwnProperty("init") || (b.init = function () {
b.$super.init.apply(this, arguments);
});b.init.prototype = b;b.$super = this;return b;
}, create: function create() {
var a = this.extend();a.init.apply(a, arguments);return a;
}, init: function init() {}, mixIn: function mixIn(a) {
for (var _b in a) {
a.hasOwnProperty(_b) && (this[_b] = a[_b]);
}a.hasOwnProperty("toString") && (this.toString = a.toString);
}, clone: function clone() {
return this.init.prototype.extend(this);
} },
j = l.WordArray = f.extend({ init: function init(a, b) {
a = this.words = a || [];this.sigBytes = b != r ? b : 4 * a.length;
}, toString: function toString(a) {
return (a || s).stringify(this);
}, concat: function concat(a) {
var b = this.words,
d = a.words,
c = this.sigBytes;a = a.sigBytes;this.clamp();if (c % 4) for (var e = 0; e < a; e++) {
b[c + e >>> 2] |= (d[e >>> 2] >>> 24 - 8 * (e % 4) & 255) << 24 - 8 * ((c + e) % 4);
} else if (65535 < d.length) for (var _e = 0; _e < a; _e += 4) {
b[c + _e >>> 2] = d[_e >>> 2];
} else b.push.apply(b, d);this.sigBytes += a;return this;
}, clamp: function clamp() {
var a = this.words,
b = this.sigBytes;a[b >>> 2] &= 4294967295 << 32 - 8 * (b % 4);a.length = h.ceil(b / 4);
}, clone: function clone() {
var a = f.clone.call(this);a.words = this.words.slice(0);return a;
}, random: function random(a) {
for (var _b2 = [], d = 0; d < a; d += 4) {
_b2.push(4294967296 * h.random() | 0);
}return new j.init(b, a);
} }),
m = k.enc = {},
s = m.Hex = { stringify: function stringify(a) {
var b = a.words;a = a.sigBytes;for (var d = [], c = 0; c < a; c++) {
var e = b[c >>> 2] >>> 24 - 8 * (c % 4) & 255;d.push((e >>> 4).toString(16));d.push((e & 15).toString(16));
}return d.join("");
}, parse: function parse(a) {
for (var b = a.length, d = [], c = 0; c < b; c += 2) {
d[c >>> 3] |= parseInt(a.substr(c, 2), 16) << 24 - 4 * (c % 8);
}return new j.init(d, b / 2);
} },
p = m.Latin1 = { stringify: function stringify(a) {
var b = a.words;a = a.sigBytes;for (var d = [], c = 0; c < a; c++) {
d.push(String.fromCharCode(b[c >>> 2] >>> 24 - 8 * (c % 4) & 255));
}return d.join("");
}, parse: function parse(a) {
for (var b = a.length, d = [], c = 0; c < b; c++) {
d[c >>> 2] |= (a.charCodeAt(c) & 255) << 24 - 8 * (c % 4);
}return new j.init(d, b);
} },
t = m.Utf8 = { stringify: function stringify(a) {
try {
return decodeURIComponent(escape(p.stringify(a)));
} catch (b) {
throw Error("Malformed UTF-8 data");
}
}, parse: function parse(a) {
return p.parse(unescape(encodeURIComponent(a)));
} },
q = l.BufferedBlockAlgorithm = f.extend({ reset: function reset() {
this._data = new j.init();this._nDataBytes = 0;
}, _append: function _append(a) {
"string" == typeof a && (a = t.parse(a));this._data.concat(a);this._nDataBytes += a.sigBytes;
}, _process: function _process(a) {
var b = this._data,
d = b.words,
c = b.sigBytes,
e = this.blockSize,
f = c / (4 * e),
f = a ? h.ceil(f) : h.max((f | 0) - this._minBufferSize, 0);a = f * e;c = h.min(4 * a, c);if (a) {
for (var g = 0; g < a; g += e) {
this._doProcessBlock(d, g);
}g = d.splice(0, a);b.sigBytes -= c;
}return new j.init(g, c);
}, clone: function clone() {
var a = f.clone.call(this);a._data = this._data.clone();return a;
}, _minBufferSize: 0 });l.Hasher = q.extend({ cfg: f.extend(), init: function init(a) {
this.cfg = this.cfg.extend(a);this.reset();
}, reset: function reset() {
q.reset.call(this);this._doReset();
}, update: function update(a) {
this._append(a);this._process();return this;
}, finalize: function finalize(a) {
a && this._append(a);return this._doFinalize();
}, blockSize: 16, _createHelper: function _createHelper(a) {
return function (b, d) {
return new a.init(d).finalize(b);
};
}, _createHmacHelper: function _createHmacHelper(a) {
return function (b, d) {
return new u.HMAC.init(a, d).finalize(b);
};
} });var u = k.algo = {};return k;
}(Math);
(function () {
var k = CryptoJS,
b = k.lib,
m = b.WordArray,
l = b.Hasher,
d = [],
b = k.algo.SHA1 = l.extend({ _doReset: function _doReset() {
this._hash = new m.init([1732584193, 4023233417, 2562383102, 271733878, 3285377520]);
}, _doProcessBlock: function _doProcessBlock(n, p) {
for (var a = this._hash.words, e = a[0], f = a[1], h = a[2], j = a[3], b = a[4], c = 0; 80 > c; c++) {
if (16 > c) d[c] = n[p + c] | 0;else {
var g = d[c - 3] ^ d[c - 8] ^ d[c - 14] ^ d[c - 16];d[c] = g << 1 | g >>> 31;
}g = (e << 5 | e >>> 27) + b + d[c];g = 20 > c ? g + ((f & h | ~f & j) + 1518500249) : 40 > c ? g + ((f ^ h ^ j) + 1859775393) : 60 > c ? g + ((f & h | f & j | h & j) - 1894007588) : g + ((f ^ h ^ j) - 899497514);b = j;j = h;h = f << 30 | f >>> 2;f = e;e = g;
}a[0] = a[0] + e | 0;a[1] = a[1] + f | 0;a[2] = a[2] + h | 0;a[3] = a[3] + j | 0;a[4] = a[4] + b | 0;
}, _doFinalize: function _doFinalize() {
var b = this._data,
d = b.words,
a = 8 * this._nDataBytes,
e = 8 * b.sigBytes;d[e >>> 5] |= 128 << 24 - e % 32;d[(e + 64 >>> 9 << 4) + 14] = Math.floor(a / 4294967296);d[(e + 64 >>> 9 << 4) + 15] = a;b.sigBytes = 4 * d.length;this._process();return this._hash;
}, clone: function clone() {
var b = l.clone.call(this);b._hash = this._hash.clone();return b;
} });k.SHA1 = l._createHelper(b);k.HmacSHA1 = l._createHmacHelper(b);
})();
(function (k) {
for (var g = CryptoJS, h = g.lib, v = h.WordArray, j = h.Hasher, h = g.algo, s = [], t = [], u = function u(q) {
return 4294967296 * (q - (q | 0)) | 0;
}, l = 2, b = 0; 64 > b;) {
var d;a: {
d = l;for (var w = k.sqrt(d), r = 2; r <= w; r++) {
if (!(d % r)) {
d = !1;break a;
}
}d = !0;
}d && (8 > b && (s[b] = u(k.pow(l, 0.5))), t[b] = u(k.pow(l, 1 / 3)), b++);l++;
}var n = [],
h = h.SHA256 = j.extend({ _doReset: function _doReset() {
this._hash = new v.init(s.slice(0));
}, _doProcessBlock: function _doProcessBlock(q, h) {
for (var a = this._hash.words, c = a[0], d = a[1], b = a[2], k = a[3], f = a[4], g = a[5], j = a[6], l = a[7], e = 0; 64 > e; e++) {
if (16 > e) n[e] = q[h + e] | 0;else {
var m = n[e - 15],
p = n[e - 2];n[e] = ((m << 25 | m >>> 7) ^ (m << 14 | m >>> 18) ^ m >>> 3) + n[e - 7] + ((p << 15 | p >>> 17) ^ (p << 13 | p >>> 19) ^ p >>> 10) + n[e - 16];
}m = l + ((f << 26 | f >>> 6) ^ (f << 21 | f >>> 11) ^ (f << 7 | f >>> 25)) + (f & g ^ ~f & j) + t[e] + n[e];p = ((c << 30 | c >>> 2) ^ (c << 19 | c >>> 13) ^ (c << 10 | c >>> 22)) + (c & d ^ c & b ^ d & b);l = j;j = g;g = f;f = k + m | 0;k = b;b = d;d = c;c = m + p | 0;
}a[0] = a[0] + c | 0;a[1] = a[1] + d | 0;a[2] = a[2] + b | 0;a[3] = a[3] + k | 0;a[4] = a[4] + f | 0;a[5] = a[5] + g | 0;a[6] = a[6] + j | 0;a[7] = a[7] + l | 0;
}, _doFinalize: function _doFinalize() {
var d = this._data,
b = d.words,
a = 8 * this._nDataBytes,
c = 8 * d.sigBytes;b[c >>> 5] |= 128 << 24 - c % 32;b[(c + 64 >>> 9 << 4) + 14] = k.floor(a / 4294967296);b[(c + 64 >>> 9 << 4) + 15] = a;d.sigBytes = 4 * b.length;this._process();return this._hash;
}, clone: function clone() {
var b = j.clone.call(this);b._hash = this._hash.clone();return b;
} });g.SHA256 = j._createHelper(h);g.HmacSHA256 = j._createHmacHelper(h);
})(Math);
(function () {
var c = CryptoJS,
k = c.enc.Utf8;c.algo.HMAC = c.lib.Base.extend({ init: function init(a, b) {
a = this._hasher = new a.init();"string" == typeof b && (b = k.parse(b));var c = a.blockSize,
e = 4 * c;b.sigBytes > e && (b = a.finalize(b));b.clamp();for (var f = this._oKey = b.clone(), g = this._iKey = b.clone(), h = f.words, j = g.words, d = 0; d < c; d++) {
h[d] ^= 1549556828, j[d] ^= 909522486;
}f.sigBytes = g.sigBytes = e;this.reset();
}, reset: function reset() {
var a = this._hasher;a.reset();a.update(this._iKey);
}, update: function update(a) {
this._hasher.update(a);return this;
}, finalize: function finalize(a) {
var b = this._hasher;a = b.finalize(a);b.reset();return b.finalize(this._oKey.clone().concat(a));
} });
})();
(function () {
var h = CryptoJS,
j = h.lib.WordArray;h.enc.Base64 = { stringify: function stringify(b) {
var e = b.words,
f = b.sigBytes,
c = this._map;b.clamp();b = [];for (var a = 0; a < f; a += 3) {
for (var d = (e[a >>> 2] >>> 24 - 8 * (a % 4) & 255) << 16 | (e[a + 1 >>> 2] >>> 24 - 8 * ((a + 1) % 4) & 255) << 8 | e[a + 2 >>> 2] >>> 24 - 8 * ((a + 2) % 4) & 255, g = 0; 4 > g && a + 0.75 * g < f; g++) {
b.push(c.charAt(d >>> 6 * (3 - g) & 63));
}
}if (e = c.charAt(64)) for (; b.length % 4;) {
b.push(e);
}return b.join("");
}, parse: function parse(b) {
var e = b.length,
f = this._map,
c = f.charAt(64);c && (c = b.indexOf(c), -1 != c && (e = c));for (var c = [], a = 0, d = 0; d < e; d++) {
if (d % 4) {
var g = f.indexOf(b.charAt(d - 1)) << 2 * (d % 4),
h = f.indexOf(b.charAt(d)) >>> 6 - 2 * (d % 4);c[a >>> 2] |= (g | h) << 24 - 8 * (a % 4);a++;
}
}return j.create(c, a);
}, _map: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" };
})();
hawk.crypto.utils = CryptoJS;
// Export if used as a module
if (typeof module !== 'undefined' && module.exports) {
module.exports = hawk;
}
/* eslint-enable */
// $lab:coverage:on$

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -1,83 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
.navbar {
background-color: #4d4f53;
border-radius: 0;
}
.navbar .navbar-brand {
color: #fff;
font-size: 14px;
font-weight: 600;
letter-spacing: 1px;
text-transform: uppercase;
}
.navbar .nav a {
color: #ccc;
}
.navbar .nav a:hover,
.navbar .nav a:focus{
color: #ccc;
background: transparent;
border: 0;
outline: none;
}
.navbar .nav .dropdown-menu a {
color: #292b2c
}
footer {
color: #4d4f53;
font-size: 12px;
line-height: 1.6em;
margin: 2em 0;
padding: 0;
text-align: center;
}
footer ul li {
display: inline-block;
margin: 0 1em;
}
footer ul {
list-style: none;
margin: 0;
padding: 0;
}
/* Phones (<768px) */
@media (max-width: 768px) {
}
/* Tablets (≥768px) */
@media (max-width: 992px) and (min-width: 768px) {
#content .container-fluid,
.navbar .container-fluid {
max-width: 733px;
}
}
/* Desktops (≥992px) */
@media (max-width: 1200px) and (min-width: 992px) {
#content .container-fluid,
.navbar .container-fluid {
max-width: 957px;
}
}
/* Desktops (≥1200px) */
@media (min-width: 1200px) {
#content .container-fluid,
.navbar .container-fluid {
max-width: 1165px;
}
}

Просмотреть файл

@ -1,126 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
angular.module('relengapi', []);
angular.module('relengapi').config(function($httpProvider) {
var summarize_config = function(config) {
var meth = config.method;
var url = config.url;
return meth + " " + url;
};
// Unlike POST and PUT, Angular doesn't set the content-type for 'patch' by
// default, but we'd like it to do so
$httpProvider.defaults.headers.patch['Content-Type'] = $httpProvider.defaults.headers.post['Content-Type'];
$httpProvider.interceptors.push(function($q) {
return {
'request': function(config) {
if (config.is_restapi_request) {
config.headers["Authorization"] = tcauth.get_header(config.url, config.method);
if (config.data) {
// Firefox will helpfully produce a clickable rendition of the data
console.log("RelengAPI request:", summarize_config(config),
'body', config.data);
} else {
console.log("RelengAPI request:", summarize_config(config));
}
}
return config;
},
'response': function(response) {
if (response.config.is_restapi_request) {
if (response.data) {
console.log("RelengAPI response:", summarize_config(response.config),
'HTTP', response.status, 'body', response.data);
} else {
console.log("RelengAPI response:", summarize_config(response.config),
'HTTP', response.status);
}
}
return response;
},
'responseError': function(response) {
if (response.config.is_restapi_request) {
var message;
if (response.data.error && response.data.error.description) {
message = response.data.error.description;
} else {
message = response.statusText || ("HTTP Status " + response.status);
}
var action = response.config.while
|| (response.config.method + " " + response.config.url);
var expectedStatuses = response.config.expectedStatuses
|| [response.config.expectedStatus];
if (expectedStatuses.indexOf(response.status) == -1) {
console.log("RelengAPI call error response:", response.data);
alertify.error("Failure while " + action + ": " + message);
} else {
console.log("RelengAPI response:", summarize_config(response.config),
'HTTP', response.status);
}
}
return $q.reject(response);
},
};
});
});
angular.module('relengapi').provider('restapi', function() {
var wrap = function(wrapped, config_pos) {
return function() {
var config;
/* find the (possibly omitted) config argument at config_pos */
var args = [].slice.apply(arguments);
if (args.length == config_pos) {
args.push({});
}
config = args[config_pos];
/* add the flag that our interceptor uses to identify RelengAPI requests */
config.is_restapi_request = true;
return wrapped.apply(this, args);
};
};
this.$get = function($http) {
// wrap the $http provider specifically for access to the backend API
var relengapi = wrap($http, 0);
relengapi.get = wrap($http.get, 1);
relengapi.head = wrap($http.head, 1);
relengapi.post = wrap($http.post, 2);
relengapi.put = wrap($http.put, 2);
relengapi.delete = wrap($http.delete, 1);
relengapi.jsonp = wrap($http.jsonp, 1);
relengapi.patch = wrap($http.patch, 2);
return relengapi;
};
});
angular.module('relengapi').directive('perm', function(initial_data) {
return {
restrict: 'E',
replace: true,
scope: {
'name': '@'
},
template:
// note the trailing space!
'<span class="label label-info" ' +
'data-toggle="tooltip" data-placement="top" >{{name}}</span> ',
link: function(scope, elt) {
elt.tooltip({
delay: 250,
title: function() {
return initial_data.perms[scope.name];
},
});
}
};
});

Просмотреть файл

@ -1,3 +0,0 @@
# Stop all search engines from archiving this site
User-agent: *
Disallow: /

Просмотреть файл

@ -1,114 +0,0 @@
(function() {
window.tcauth = {};
window.tcauth.get_header = function(url, method) {
var tc_auth = window.localStorage.getItem('tc_auth');
try {
tc_auth = JSON.parse(tc_auth);
} catch(err) {
tc_auth = null;
}
if (tc_auth == null) {
return '';
}
if (tc_auth.credentials &&
tc_auth.credentials.clientId &&
tc_auth.credentials.accessToken) {
var extData = null;
if (tc_auth.credentials.certificate) {
extData = new buffer.Buffer(JSON.stringify({
certificate: JSON.parse(tc_auth.credentials.certificate)
})).toString('base64');
}
var header = hawk.client.header(
url,
method || 'GET',
{
credentials: {
id: tc_auth.credentials.clientId,
key: tc_auth.credentials.accessToken,
algorithm: 'sha256'
},
ext: extData,
}
);
};
return header.field;
};
window.tcauth.setup = function(service, default_service_url) {
var $login = $('#login');
var $loggedin = $('#loggedin');
var $logout= $('#logout');
var $email = $('#email');
var service_url = $('body').attr('data-' + service + '-url') || default_service_url;
var auth = window.localStorage.getItem('auth');
try {
auth = JSON.parse(auth);
} catch(err) {
auth = null;
}
if (auth != null && auth.access_token) {
$.ajax({
url: 'https://login.taskcluster.net/v1/oidc-credentials/mozilla-auth0',
async: false,
beforeSend: function (xhr) {
xhr.setRequestHeader("Authorization", "Bearer " + auth.access_token);
},
error: function(xhr, status, error) {
},
success: function(data, status, xhr) {
$email.html('<span>' + data.credentials.clientId + '</span>' + '<span class="caret"></span/>');
$login.toggleClass('hidden');
$loggedin.toggleClass('hidden');
window.localStorage.setItem('tc_auth', JSON.stringify(data));
$.ajax({
url: service_url + '/init',
async: false,
beforeSend: function (xhr, config) {
xhr.setRequestHeader("Authorization", tcauth.get_header(config.url, config.method));
},
success: function(data, status, xhr) {
angular.module('initial_data', []).constant('initial_data', data || {});
}
});
}
});
} else {
angular.module('initial_data', []).constant('initial_data', {});
}
$login.on('click', function(e) {
e.preventDefault();
e.stopPropagation();
var url = $('body').attr('data-treestatus-api-url') || 'https://localhost:8000';
$.ajax({
url: url + '/auth0/login',
error: function(xhr, status, error) {
},
success: function(redirect_url, status, xhr) {
window.location = redirect_url;
}
});
});
$logout.on('click', function(e) {
e.preventDefault();
e.stopPropagation();
window.localStorage.removeItem('auth');
window.localStorage.removeItem('tc_auth');
window.location.reload();
});
};
})();

Просмотреть файл

@ -1,130 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>ToolTool - Mozilla RelEng Services</title>
<link href="../alertify.core.css" media="screen" rel="stylesheet" type="text/css" />
<link href="../alertify.default.css" media="screen" rel="stylesheet" type="text/css" />
<link href="../bootstrap.min.css" media="screen" rel="stylesheet" type="text/css" />
<link href="../bootstrap-theme.min.css" media="screen" rel="stylesheet" type="text/css" />
<link href="../relengapi.css" media="screen" rel="stylesheet" type="text/css" />
<link href="tokens.css" media="screen" rel="stylesheet" type="text/css" />
</head>
<body>
<nav class="navbar">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="../">Release Engineering</a>
</div>
<ul class="nav navbar-nav navbar-right">
<li><a id="login" href="#">Login</a></li>
<li id="loggedin" class="dropdown hidden">
<a id="email" href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"></a>
<ul class="dropdown-menu">
<li><a href="https://tools.taskcluster.net/credentials">Manage credentials</a></li>
<li><a id="logout" href="#">Logout</a></li>
</ul>
</li>
</ul>
</div>
</nav>
<div id="content">
<div class="container-fluid">
<!-- This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<div ng-app="tooltool" ng-controller="TTSearchController">
<div class="row">
<div class="col-xs-4">
<span class="h2">Tooltool</span>
</div>
<div class="col-xs-8 tt-searchbar">
<form ng-submit="startSearch()">
<div class="input-group">
<input type="text" class="form-control"
placeholder="Search for..." autofocus
ng-model="search_query">
<span class="input-group-btn">
<button class="btn btn-default" type="submit">
<span class="glyphicon glyphicon-search" aria-hidden="true"></span>
<span class="sr-only">Search</span>
</button>
</span>
</form>
</div>
</div>
</div>
<div class="row" ng-show="show_help">
<div class="col-xs-12">
<div class="alert alert-info" role="alert">
To begin, enter search terms above. You can search by
<ul>
<li>Filename</li>
<li>SHA512 hash</li>
<li>Upload author</li>
<li>Upload message (including bug number)</li>
</ul>
</div>
</div>
</div>
<div class="row" ng-show="file_results">
<div class="col-xs-12">
<span class="h3">Files</span>
<div class="list-group">
<div ng-repeat="res in file_results" class="list-group-item">
<tt-result-file res="res"></tt-result-file>
</div>
</div>
</div>
</div>
<div class="row" ng-show="batch_results">
<div class="col-xs-12">
<span class="h3">Upload Batches</span>
<div class="list-group">
<div ng-repeat="res in batch_results" class="list-group-item">
<tt-result-batch res="res"></tt-result-batch>
</div>
</div>
</div>
</div>
</div>
<footer>
<hr>
<ul>
<li>
<a href="https://docs.mozilla-releng.net">Documentation</a>
</li>
<li>
<a href="https://github.com/mozilla/release-services/blob/master/CONTRIBUTING.rst">Contribute</a>
</li>
<li>
<a href="https://github.com/mozilla/release-services/issues/new">Contact</a>
</li>
</ul>
</footer>
</div>
</div>
<script src="../jquery-1.11.1.min.js" type="text/javascript"></script>
<script src="../bootstrap.min.js" type="text/javascript"></script>
<script src="../alertify.min.js" type="text/javascript"></script>
<script src="../auth_external.js" type="text/javascript"></script>
<script src="../angular.min.js" type="text/javascript"></script>
<script src="../moment.min.js" type="text/javascript"></script>
<script src="../angular-moment.min.js" type="text/javascript"></script>
<script src="../buffer.js" type="text/javascript"></script>
<script src="../hawk.js" type="text/javascript"></script>
<script src="../relengapi.js" type="text/javascript"></script>
<script src="../tcauth.js" type="text/javascript"></script>
<script src="tooltool.js" type="text/javascript"></script>
<script type="text/javascript">
window.tcauth.setup('tooltool-api', 'https://localhost:8002');
</script>
</body>
</html>

Просмотреть файл

@ -1,40 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
.list-group {
/* list-group's bottom margin is huge and looks awful when
* list-groups are nested */
margin-bottom:5px
}
.tt-searchbar {
/* a little breathing room beneath the searchbar */
margin-bottom: 1em;
}
.tt-result-controls {
text-align: right;
position: absolute;
right: 8px;
width: 3em;
}
.tt-filesize {
color: #888;
}
.tt-sha512 {
text-size: 85%;
}
.tt-upload-message {
font-style: italic;
}
.download-link {
cursor: pointer;
}
.details-link {
cursor: pointer;
}

Просмотреть файл

@ -1,91 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
angular.module('tooltool', ['relengapi', 'angularMoment']);
angular.module('tooltool').controller('TTSearchController',
function($scope, restapi) {
$scope.search_query = ''
$scope.show_help = true;
$scope.backend_url = $('body').attr('data-tooltool-api-url') || 'https://localhost:8002';
$scope.file_results = []
$scope.batch_results = []
// temp
$scope.startSearch = function() {
$scope.show_help = false;
var q = $scope.search_query;
// search for files and batches at the same time
restapi({
url: $scope.backend_url + '/file?q=' + q,
method: 'GET',
while: 'searching files',
}).then(function(response) {
$scope.file_results = response.data.result;
});
restapi({
url: $scope.backend_url + '/upload?q=' + q,
method: 'GET',
while: 'searching upload batches',
}).then(function(response) {
var batches = response.data.result;
// put the filename into each file record
angular.forEach(batches, function (batch) {
angular.forEach(batch.files, function(file, filename) {
file.filenames = [filename];
});
});
$scope.batch_results = batches;
});
}
});
angular.module('tooltool').directive('ttResultFile', function() {
return {
restrict: 'E',
replace: true,
priority: 1001, // run after ng-repeat
templateUrl: 'tt-result-file.html',
scope: {
res: '=',
},
link: function(scope, element, attrs) {
scope.backend_url = $('body').attr('data-tooltool-api-url') || 'https://localhost:8002';
},
};
});
angular.module('tooltool').directive('ttResultBatch', function() {
return {
restrict: 'E',
replace: true,
priority: 1001, // run after ng-repeat
templateUrl: 'tt-result-batch.html',
scope: {
res: '=',
},
link: function(scope, element, attrs) {
scope.details = false;
scope.backend_url = $('body').attr('data-tooltool-api-url') || 'https://localhost:8002';
},
};
});
angular.module('tooltool').directive('ttDigest', function() {
return {
restrict: 'E',
replace: true,
template: '<span ng-click="limit = 128" class="tt-sha512">{{digest|limitTo:limit}}</span>',
scope: {
algorithm: '@',
digest: '@',
},
link: function(scope, element, attrs) {
scope.limit = 8;
},
};
});

Просмотреть файл

@ -1,25 +0,0 @@
<div>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<div class="tt-result-controls">
<span ng-click="details = !details"
class="glyphicon details-link"
ng-class="{'glyphicon-chevron-up': details,
'glyphicon-chevron-down': !details}"
aria-hidden="true"></span>
</div>
<div class="tt-result-info">
<span class="tt-upload-date">{{res.uploaded | amDateFormat:'l LTS'}}</span> -
<span class="tt-upload-author">{{res.author}}</span> -
<!-- TODO: filter to replace Bug XXXXXX by a link -->
<span class="tt-upload-message">{{res.message}}</span>
</div>
<div ng-if="details">
<div class="list-group">
<div ng-repeat="file in res.files" class="list-group-item">
<tt-result-file res="file"></tt-result-file>
</div>
</div>
</div>
</div>

Просмотреть файл

@ -1,19 +0,0 @@
<div>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<div ng-if="res.has_instances" class="tt-result-controls">
<a href="{{backend_url}}/{{res.algorithm}}/{{res.digest}}"><span class="glyphicon glyphicon-download download-link" aria-hidden="true"></span>
<span class="sr-only">Download</span></a>
</div>
<div class="tt-result-info">
<span ng-if="res.filenames">
<span class="tt-filename">{{res.filenames.join(', ')}}</span> -
</span>
<tt-digest algorithm="{{res.algorithm}}" digest="{{res.digest}}"></tt-digest> -
<!-- TODO: get all filenames used for this file in any batch -->
<span class="tt-filesize">{{res.size}} bytes</span> -
<span ng-if="res.visibility == 'public'" class="text-muted">PUBLIC</span>
<span ng-if="res.visibility == 'internal'" class="text-danger">INTERNAL</span>
</div>
</div>

Просмотреть файл

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<meta name="description" content="<%= htmlWebpackPlugin.options.description %>" />
<meta name="author" content="<%= htmlWebpackPlugin.options.author %>" />
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
</body>
</html>

Просмотреть файл

@ -1 +0,0 @@
../../../lib/frontend_common/title.js

Просмотреть файл

@ -1,34 +0,0 @@
import React from 'react'
import chai from 'chai'
import chaiEnzyme from 'chai-enzyme'
import chaiImmutable from 'chai-immutable';
import { Link } from 'react-router'
import { fromJS } from 'immutable';
import { shallow } from 'enzyme'
import { Layout, routes } from './../src/layout';
import app, { initialState, reducers } from './../src/index';
const expect = chai.expect;
chai.use(chaiImmutable);
chai.use(chaiEnzyme())
describe('<Layout />', () => {
it('renders nav and main element', () => {
const wrapper = shallow(<Layout/>);
expect(wrapper.find('nav')).to.have.length(1);
expect(wrapper.find('#content')).to.have.length(1);
});
it('renders <Link/> elements for all routes', () => {
const wrapper = shallow(<Layout/>);
expect(wrapper.find(Link)).to.have.length(routes.count());
});
it('test that initialState is our current state (without routing)', () => {
expect(app.store.getState().delete('routing')).to.equal(fromJS(initialState));
});
});

Просмотреть файл

@ -1 +0,0 @@
../../lib/frontend_common/webpack.config.js

Просмотреть файл

@ -1 +0,0 @@
1.0.0

Просмотреть файл

@ -1,91 +0,0 @@
{ releng_pkgs
}:
let
inherit (releng_pkgs.lib) mkBackend mkTaskclusterHook fromRequirementsFile filterSource;
inherit (releng_pkgs.pkgs) writeScript writeText dockerTools;
inherit (releng_pkgs.pkgs.lib) fileContents;
inherit (releng_pkgs.tools) pypi2nix;
python = import ./requirements.nix { inherit (releng_pkgs) pkgs; };
project_name = "tooltool/api";
version = fileContents ./VERSION;
mkCronJob = { schedule, command }:
builtins.listToAttrs (
map (channel:
{ name = channel;
value =
let
hook_name = "${self.name}_${command}_${channel}";
hook = mkTaskclusterHook {
name = hook_name;
owner = "rgarbas@mozilla.com";
inherit schedule;
scopes =
[ "secrets:get:repo:github.com/mozilla-releng/services:branch:${channel}"
"queue:create-task:aws-provisioner-v1/releng-svc"
];
taskImage = self.docker;
taskEnv = {
TASKCLUSTER_SECRET = "repo:github.com/mozilla-releng/services:branch:${channel}";
};
taskCapabilities = {};
taskCommand = [
"flask"
command
];
deadline = "4 hours";
maxRunTime = 4 * 60 * 60;
};
in
writeText "taskcluster-hook-${hook_name}.json" (builtins.toJSON hook);
}) ["testing" "staging" "production"]);
self = mkBackend {
inherit python version project_name;
inStaging = true;
inProduction = true;
src = filterSource ./. { inherit(self) name; };
buildInputs =
(fromRequirementsFile ./../../../lib/cli_common/requirements-dev.txt python.packages) ++
(fromRequirementsFile ./../../../lib/backend_common/requirements-dev.txt python.packages) ++
(fromRequirementsFile ./requirements-dev.txt python.packages);
propagatedBuildInputs =
(fromRequirementsFile ./requirements.txt python.packages);
prePatch = ''
rm -f tooltool_api/tooltool.py.download
ln -s ${../client/tooltool.py} tooltool_api/tooltool.py.download
'';
passthru = {
cron = {
check_pending_uploads = mkCronJob { schedule = [ "*/10 * * * *" ]; # every 10 min;
command = "check-pending-uploads";
};
replicate = mkCronJob { schedule = [ "0 * * * *" ]; # every 1 hour;
command = "replicate";
};
};
update = writeScript "update-${self.name}" ''
pushd ${self.src_path}
cache_dir=$PWD/../../../tmp/pypi2nix
mkdir -p $cache_dir
eval ${pypi2nix}/bin/pypi2nix -v \
-C $cache_dir \
-V 3.7 \
-O ../../../nix/requirements_override.nix \
-E postgresql \
-s intreehooks \
-s flit \
-s vcversioner \
-s pytest-runner \
-s setuptools-scm \
-r requirements.txt \
-r requirements-dev.txt
popd
'';
};
};
in self

Просмотреть файл

@ -1 +0,0 @@
-r ./../../../lib/backend_common/requirements-dev.txt

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,7 +0,0 @@
-e ./../../../lib/cli_common[log,pulse] #egg=mozilla-cli-common
-e ./../../../lib/backend_common[log,security,cors,api,auth,db,pulse] #egg=mozilla-backend-common
boto
gunicorn
psycopg2

Просмотреть файл

@ -1,109 +0,0 @@
aioamqp==0.12.0
aiohttp==3.5.4
alembic==1.0.8
amqp==2.4.2
async-timeout==3.0.1
atomicwrites==1.3.0
attrs==19.1.0
blinker==1.4
boto==2.49.0
boto3==1.9.123
botocore==1.12.123
certifi==2019.3.9
chardet==3.0.4
Click==7.0
clickclick==1.2.2
codecov==2.0.15
connexion==2.2.0
coverage==4.5.3
coveralls==1.7.0
docopt==0.6.2
docutils==0.14
ecdsa==0.13
entrypoints==0.3
fancycompleter==0.8
flake8==3.7.7
flake8-coding==1.3.1
flake8-copyright==0.2.2
flake8-debugger==3.1.0
flake8-isort==2.7.0
flake8-mypy==17.8.0
flake8-quotes==1.0.0
Flask==1.0.2
Flask-Caching==1.6.0
Flask-Cors==3.0.7
Flask-Login==0.4.1
Flask-Migrate==2.4.0
flask-oidc==1.4.0
Flask-SQLAlchemy==2.3.2
flask-talisman==0.6.0
flit==1.3
future==0.17.1
gunicorn==19.9.0
httplib2==0.12.1
idna==2.8
inflection==0.3.1
inotify==0.2.10
intreehooks==1.0
isort==4.3.16
itsdangerous==0.24
Jinja2==2.10
jmespath==0.9.4
jsonschema==2.6.0
kombu==4.4.0
Logbook==1.4.3
Mako==1.0.8
MarkupSafe==1.1.1
mccabe==0.6.1
mohawk==0.3.4
more-itertools==6.0.0
mozdef-client==1.0.11
mozilla-backend-common==1.0.0
mozilla-cli-common==1.0.0
multidict==4.5.2
mypy==0.670
mypy-extensions==0.4.1
nose==1.3.7
oauth2client==4.1.3
openapi-spec-validator==0.2.6
pdbpp==0.9.14
pluggy==0.9.0
psycopg2==2.7.7
py==1.8.0
pyasn1==0.4.5
pyasn1-modules==0.2.4
pycodestyle==2.5.0
pyflakes==2.1.1
Pygments==2.3.1
pytest==4.3.1
pytest-cov==2.6.1
pytest-runner==4.4
python-dateutil==2.6.1
python-editor==1.0.4
python-hglib==2.6.1
python-jose==3.0.1
pytoml==0.1.20
pytz==2018.9
PyYAML==5.1
raven==6.10.0
requests==2.21.0
requests-futures==0.9.9
responses==0.10.6
rsa==4.0
s3transfer==0.2.0
setuptools-scm==3.2.0
six==1.12.0
slugid==2.0.0
SQLAlchemy==1.3.1
structlog==19.1.0
swagger-ui-bundle==0.0.3
taskcluster==7.0.1
taskcluster-urls==11.0.0
testfixtures==6.6.2
typed-ast==1.3.1
urllib3==1.24.1
vcversioner==2.16.0.0
vine==1.3.0
Werkzeug==0.15.1
wmctrl==0.3
yarl==1.3.0

Просмотреть файл

@ -1,5 +0,0 @@
{ pkgs, python }:
self: super: {
}

Просмотреть файл

@ -1,71 +0,0 @@
# -*- coding: utf-8 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import base64
import os
import cli_common.taskcluster
import tooltool_api.config
DEBUG = bool(os.environ.get('DEBUG', False))
# -- LOAD SECRETS -------------------------------------------------------------
required = [
'APP_CHANNEL',
'SECRET_KEY_BASE64',
'DATABASE_URL',
# https://github.com/mozilla/build-cloud-tools/blob/master/configs/cloudformation/tooltool.py
'S3_REGIONS',
# https://github.com/mozilla/build-cloud-tools/blob/master/configs/cloudformation/iam_relengapi.py
'S3_REGIONS_ACCESS_KEY_ID',
'S3_REGIONS_SECRET_ACCESS_KEY',
'PULSE_USER',
'PULSE_PASSWORD',
]
existing = {x: os.environ.get(x) for x in required if x in os.environ}
existing['ALLOW_ANONYMOUS_PUBLIC_DOWNLOAD'] = False
# This value should be fairly short (and its value is included in the
# `upload_batch` docstring). Uploads cannot be validated until this
# time has elapsed, otherwise a malicious uploader could alter a file
# after it had been verified.
existing['UPLOAD_EXPIRES_IN'] = 60
existing['DOWLOAD_EXPIRES_IN'] = 60
secrets = cli_common.taskcluster.get_secrets(
os.environ.get('TASKCLUSTER_SECRET'),
tooltool_api.config.PROJECT_NAME,
required=required,
existing=existing,
taskcluster_client_id=os.environ.get('TASKCLUSTER_CLIENT_ID'),
taskcluster_access_token=os.environ.get('TASKCLUSTER_ACCESS_TOKEN'),
)
locals().update(secrets)
RELENGAPI_AUTH = True
SECRET_KEY = base64.b64decode(secrets['SECRET_KEY_BASE64'])
# -- DATABASE -----------------------------------------------------------------
SQLALCHEMY_TRACK_MODIFICATIONS = False
if DEBUG:
SQLALCHEMY_ECHO = True
# We require DATABASE_URL set by environment variables for branches deployed to Dockerflow.
if secrets['APP_CHANNEL'] in ('testing', 'staging', 'production'):
if 'DATABASE_URL' not in os.environ:
SQLALCHEMY_DATABASE_URI = secrets['DATABASE_URL']
# XXX: until we only deploy to GCP
# raise RuntimeError(f'DATABASE_URL has to be set as an environment variable, when '
# f'APP_CHANNEL is set to {secrets["APP_CHANNEL"]}')
else:
SQLALCHEMY_DATABASE_URI = os.environ['DATABASE_URL']
else:
SQLALCHEMY_DATABASE_URI = secrets['DATABASE_URL']

Просмотреть файл

@ -1 +0,0 @@
../../../nix/setup.cfg

Просмотреть файл

@ -1,43 +0,0 @@
# -*- coding: utf-8 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import setuptools
def read_requirements(file_):
lines = []
with open(file_) as f:
for line in f.readlines():
line = line.strip()
if line.startswith('-e ') or line.startswith('http://') or line.startswith('https://'):
extras = ''
if '[' in line:
extras = '[' + line.split('[')[1].split(']')[0] + ']'
line = line.split('#')[1].split('egg=')[1] + extras
elif line == '' or line.startswith('#') or line.startswith('-'):
continue
line = line.split('#')[0].strip()
lines.append(line)
return sorted(list(set(lines)))
with open('VERSION') as f:
VERSION = f.read().strip()
setuptools.setup(
name='mozilla-tooltool-api',
version=VERSION,
description='The code behind https://tooltool.mozilla-releng.net/',
author='Mozilla Release Services Team',
author_email='release-services@mozilla.com',
url='https://tooltool.mozilla-releng.net',
tests_require=read_requirements('requirements-dev.txt'),
install_requires=read_requirements('requirements.txt'),
packages=setuptools.find_packages(),
include_package_data=True,
zip_safe=False,
license='MPL2',
)

Просмотреть файл

@ -1,28 +0,0 @@
# -*- coding: utf-8 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import pytest
import backend_common
@pytest.fixture(scope='session')
def app():
'''Load tooltool_api in test mode
'''
import tooltool_api
config = backend_common.testing.get_app_config({
'SQLALCHEMY_DATABASE_URI': 'sqlite://',
'SQLALCHEMY_TRACK_MODIFICATIONS': False,
'S3_REGIONS': dict(),
'S3_REGIONS_ACCESS_KEY_ID': '123',
'S3_REGIONS_SECRET_ACCESS_KEY': '123',
})
app = tooltool_api.create_app(config)
with app.app_context():
backend_common.testing.configure_app(app)
yield app

Просмотреть файл

@ -1,30 +0,0 @@
# -*- coding: utf-8 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import hashlib
def test_now():
import tooltool_api.utils
assert tooltool_api.utils.now().tzname() == 'UTC'
def test_keyname():
import tooltool_api.utils
ONE = '1\n'
ONE_DIGEST = hashlib.sha512(ONE.encode('utf-8')).hexdigest()
assert tooltool_api.utils.keyname(ONE_DIGEST) == 'sha512/' + ONE_DIGEST
def test_is_valid_sha512():
import tooltool_api.utils
VALID_SHA512 = '1' * 128
assert tooltool_api.utils.is_valid_sha512('123') is None
assert tooltool_api.utils.is_valid_sha512(VALID_SHA512).string == VALID_SHA512

Просмотреть файл

@ -1,58 +0,0 @@
# -*- coding: utf-8 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import typing
import flask
import werkzeug.exceptions
import backend_common
import backend_common.api
import tooltool_api.aws
import tooltool_api.cli
import tooltool_api.config
import tooltool_api.models # noqa
def custom_handle_default_exceptions(e: Exception) -> typing.Tuple[int, str]:
'''Conform structure of errors as before, to make it work with client (tooltool.py).
'''
error = backend_common.api.handle_default_exceptions_raw(e)
error['name'] = error['title']
error['description'] = error['detail']
import flask # for some reason flask needs to be imported here
return flask.jsonify(dict(error=error)), error['status']
def create_app(config: dict = None) -> flask.Flask:
app = backend_common.create_app(
project_name=tooltool_api.config.PROJECT_NAME,
app_name=tooltool_api.config.APP_NAME,
config=config,
extensions=[
'log',
'security',
'cors',
'api',
'auth',
'db',
'pulse',
],
)
app.api.register(os.path.join(os.path.dirname(__file__), 'api.yml'))
app.aws = tooltool_api.aws.AWS(app.config['S3_REGIONS_ACCESS_KEY_ID'],
app.config['S3_REGIONS_SECRET_ACCESS_KEY'])
for code, exception in werkzeug.exceptions.default_exceptions.items():
app.register_error_handler(exception, custom_handle_default_exceptions)
app.cli.add_command(tooltool_api.cli.cmd_worker, 'worker')
app.cli.add_command(tooltool_api.cli.cmd_replicate, 'replicate')
app.cli.add_command(tooltool_api.cli.cmd_check_pending_uploads, 'check-pending-uploads')
app.add_url_rule('/tooltool.py', view_func=tooltool_api.api.download_client)
return app

Просмотреть файл

@ -1,314 +0,0 @@
# -*- coding: utf-8 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import datetime
import pathlib
import random
import typing
import flask
import flask_login
import pytz
import sqlalchemy as sa
import werkzeug
import werkzeug.exceptions
import backend_common.auth
import cli_common.log
import tooltool_api.aws
import tooltool_api.config
import tooltool_api.models
import tooltool_api.utils
logger = cli_common.log.get_logger(__name__)
def _get_region_and_bucket(region: typing.Optional[str],
regions: typing.Dict[str, str],
) -> typing.Tuple[str, str]:
if region and region in regions:
return region, regions[region]
# no region specified, so return one at random
return random.choice(list(regions.items()))
def search_batches(q: str) -> dict:
return dict(
result=[
row.to_dict()
for row in tooltool_api.models.Batch.query.filter(
sa.or_(
tooltool_api.models.Batch.author.contains(q),
tooltool_api.models.Batch.message.contains(q)
)
).all()
]
)
def get_batch(id: int) -> dict:
row = tooltool_api.models.Batch.query.filter(tooltool_api.models.Batch.id == id).first()
if not row:
raise werkzeug.exceptions.NotFound
return row.to_dict()
def upload_batch(body: dict, region: typing.Optional[str] = None) -> dict:
if not body['message']:
raise werkzeug.exceptions.BadRequest('message must be non-empty')
if not body['files']:
raise werkzeug.exceptions.BadRequest('a batch must include at least one file')
if 'author' in body:
raise werkzeug.exceptions.BadRequest('Author must NOT be specified for upload.')
UPLOAD_EXPIRES_IN = flask.current_app.config['UPLOAD_EXPIRES_IN']
if type(UPLOAD_EXPIRES_IN) is not int:
raise werkzeug.exceptions.InternalServerError('UPLOAD_EXPIRES_IN should be of type int.')
S3_REGIONS = flask.current_app.config['S3_REGIONS'] # type: typing.Dict[str, str]
if type(S3_REGIONS) is not dict:
raise werkzeug.exceptions.InternalServerError('S3_REGIONS should be of type dict.')
region, bucket = _get_region_and_bucket(region, S3_REGIONS)
body['author'] = flask_login.current_user.get_id()
# verify permissions based on visibilities
visibilities = set(f['visibility'] for f in body['files'].values())
for visibility in visibilities:
permission = f'{tooltool_api.config.SCOPE_PREFIX}/upload/{visibility}'
if not flask_login.current_user.has_permissions(permission):
raise werkzeug.exceptions.Forbidden(f'no permission to upload {visibility} files')
session = flask.g.db.session
batch = tooltool_api.models.Batch(
uploaded=tooltool_api.utils.now(),
author=body['author'],
message=body['message'],
)
s3 = flask.current_app.aws.connect_to('s3', region)
for filename, info in body['files'].items():
logger2 = logger.bind(tooltool_sha512=info['digest'],
tooltool_operation='upload',
tooltool_batch_id=batch.id,
mozdef=True)
if info['algorithm'] != 'sha512':
raise werkzeug.exceptions.BadRequest('`sha512` is the only allowed digest algorithm')
if not tooltool_api.utils.is_valid_sha512(info['digest']):
raise werkzeug.exceptions.BadRequest('Invalid sha512 digest'
)
digest = info['digest']
file = tooltool_api.models.File.query.filter(tooltool_api.models.File.sha512 == digest).first()
if file and file.visibility != info['visibility']:
raise werkzeug.exceptions.BadRequest('Cannot change already existing file\'s visibility level.')
if file and file.instances != []:
if file.size != info['size']:
raise werkzeug.exceptions.BadRequest(f'Size mismatch for {filename}')
else:
if not file:
file = tooltool_api.models.File(sha512=digest,
visibility=info['visibility'],
size=info['size'])
session.add(file)
logger2.info(f'Generating signed S3 PUT URL to {info["digest"][:10]} for {flask_login.current_user}; expiring in {UPLOAD_EXPIRES_IN}s')
info['put_url'] = s3.generate_url(
method='PUT',
expires_in=UPLOAD_EXPIRES_IN,
bucket=bucket,
key=tooltool_api.utils.keyname(info['digest']),
headers={'Content-Type': 'application/octet-stream'},
)
# The PendingUpload row needs to reflect the updated expiration
# time, even if there's an existing pending upload that expires
# earlier. The `merge` method does a SELECT and then either
# UPDATEs or INSERTs the row. However, merge needs the file_id,
# rather than just a reference to the file object; and for that, we
# need to flush the inserted file.
session.flush()
expires = tooltool_api.utils.now() + datetime.timedelta(seconds=UPLOAD_EXPIRES_IN)
pu = tooltool_api.models.PendingUpload(file_id=file.id,
region=region,
expires=expires)
session.merge(pu)
session.add(tooltool_api.models.BatchFile(filename=filename, file=file, batch=batch))
session.add(batch)
session.commit()
body['id'] = batch.id
return dict(result=body)
def upload_complete(digest: str) -> typing.Union[werkzeug.Response,
typing.Tuple[str, int]]:
if not tooltool_api.utils.is_valid_sha512(digest):
raise werkzeug.exceptions.BadRequest('Invalid sha512 digest')
# if the pending upload is still valid, then we can't check this file
# yet, so return 409 Conflict. If there is no PU, or it's expired,
# then we can proceed.
file = tooltool_api.models.File.query.filter(tooltool_api.models.File.sha512 == digest).first()
if file:
for pending_upload in file.pending_uploads:
until = pending_upload.expires.replace(tzinfo=pytz.UTC) - tooltool_api.utils.now()
if until > datetime.timedelta(0):
# add 1 second to avoid rounding / skew errors
headers = {'X-Retry-After': str(1 + int(until.total_seconds()))}
return werkzeug.Response(status=409, headers=headers)
exchange = f'exchange/{flask.current_app.config["PULSE_USER"]}/{tooltool_api.config.PROJECT_NAME}'
logger.info(f'Sending digest `{digest}` to queue `{exchange}` for route `{tooltool_api.config.PULSE_ROUTE_CHECK_FILE_PENDING_UPLOADS}`.')
try:
flask.current_app.pulse.publish(
exchange,
tooltool_api.config.PULSE_ROUTE_CHECK_FILE_PENDING_UPLOADS,
dict(digest=digest),
)
except Exception as e:
import traceback
msg = 'Can\'t send notification to pulse.'
trace = traceback.format_exc()
logger.error(f'{msg}\nException:{e}\nTraceback: {trace}')
return '{}', 202
def search_files(q: str) -> dict:
session = flask.g.db.session
query = session.query(tooltool_api.models.File).join(tooltool_api.models.BatchFile)
query = query.filter(sa.or_(tooltool_api.models.BatchFile.filename.contains(q),
tooltool_api.models.File.sha512.startswith(q)))
return dict(result=[row.to_dict() for row in query.all()])
def get_file(digest: str) -> dict:
if not tooltool_api.utils.is_valid_sha512(digest):
raise werkzeug.exceptions.BadRequest('Invalid sha512 digest')
row = tooltool_api.models.File.query.filter(tooltool_api.models.File.sha512 == digest).first()
if not row:
raise werkzeug.exceptions.NotFound
return row.to_dict(include_instances=True)
@backend_common.auth.auth.require_permissions([tooltool_api.config.SCOPE_MANAGE])
def patch_file(digest: str, body: dict) -> dict:
S3_REGIONS = flask.current_app.config['S3_REGIONS'] # type: typing.Dict[str, str]
if type(S3_REGIONS) is not dict:
raise werkzeug.exceptions.InternalServerError('S3_REGIONS should be of type dict.')
session = flask.current_app.db.session
file = session.query(tooltool_api.models.File).filter(tooltool_api.models.File.sha512 == digest).first()
if not file:
raise werkzeug.exceptions.NotFound
for change in body:
if 'op' not in change:
raise werkzeug.exceptions.BadRequest('No op.')
if change['op'] == 'delete_instances':
key_name = tooltool_api.utils.keyname(digest)
for instance in file.instances:
conn = flask.current_app.aws.connect_to('s3', instance.region)
region_bucket = S3_REGIONS.get(instance.region)
if region_bucket is None:
raise werkzeug.exceptions.InternalServerError(f'No bucket for region `{instance.region}` defined.')
bucket = conn.get_bucket(region_bucket)
bucket.delete_key(key_name)
session.delete(instance)
elif change['op'] == 'set_visibility':
if change['visibility'] not in ('internal', 'public'):
raise werkzeug.exceptions.BadRequest('bad visibility level')
file.visibility = change['visibility']
else:
raise werkzeug.exceptions.BadRequest('Unknown op')
session.commit()
return file.to_dict(include_instances=True)
def download_file(digest: str, region: typing.Optional[str] = None) -> werkzeug.Response:
logger2 = logger.bind(tooltool_sha512=digest, tooltool_operation='download_file')
S3_REGIONS = flask.current_app.config['S3_REGIONS'] # type: typing.Dict[str, str]
if type(S3_REGIONS) is not dict:
raise werkzeug.exceptions.InternalServerError('S3_REGIONS should be of type dict.')
DOWLOAD_EXPIRES_IN = flask.current_app.config['DOWLOAD_EXPIRES_IN']
if type(DOWLOAD_EXPIRES_IN) is not int:
raise werkzeug.exceptions.InternalServerError('DOWLOAD_EXPIRES_IN should be of type int.')
ALLOW_ANONYMOUS_PUBLIC_DOWNLOAD = flask.current_app.config['ALLOW_ANONYMOUS_PUBLIC_DOWNLOAD']
if type(ALLOW_ANONYMOUS_PUBLIC_DOWNLOAD) is not bool:
raise werkzeug.exceptions.InternalServerError('ALLOW_ANONYMOUS_PUBLIC_DOWNLOAD should be of type bool.')
regions = ', '.join(S3_REGIONS.keys())
logger2.debug(f'Looking for file in following regions: {regions}')
if not tooltool_api.utils.is_valid_sha512(digest):
raise werkzeug.exceptions.BadRequest('Invalid sha512 digest')
# see where the file is.
file_row = tooltool_api.models.File.query.filter(
tooltool_api.models.File.sha512 == digest).first()
if not file_row or not file_row.instances:
raise werkzeug.exceptions.NotFound
# check visibility
if file_row.visibility != 'public' or not ALLOW_ANONYMOUS_PUBLIC_DOWNLOAD:
permission = f'{tooltool_api.config.SCOPE_PREFIX}/download/{file_row.visibility}'
if not flask_login.current_user.has_permissions(permission):
raise werkzeug.exceptions.Forbidden
# figure out which region to use, and from there which bucket
selected_region = None
for file_instance in file_row.instances:
if file_instance.region == region:
selected_region = file_instance.region
break
else:
# preferred region not found, so pick one from the available set
selected_region = random.choice([inst.region for inst in file_row.instances])
bucket = S3_REGIONS.get(selected_region)
if bucket is None:
raise werkzeug.exceptions.InternalServerError(f'Region `{selected_region}` can not be found in S3_REGIONS.')
key = tooltool_api.utils.keyname(digest)
s3 = flask.current_app.aws.connect_to('s3', selected_region)
logger2.info(f'Generating signed S3 GET URL for {digest[:10]}, expiring in {DOWLOAD_EXPIRES_IN}s')
signed_url = s3.generate_url(method='GET', expires_in=DOWLOAD_EXPIRES_IN, bucket=bucket, key=key)
return flask.redirect(signed_url)
def download_client():
client = pathlib.Path(__file__).parent / 'tooltool.py.download'
return flask.send_file(str(client.absolute()), attachment_filename='tooltool.py')

Просмотреть файл

@ -1,487 +0,0 @@
---
swagger: "2.0"
info:
version: "1.0.0"
title: "ToolTool"
consumes:
- application/json
produces:
- application/json
paths:
/init:
get:
operationId: "backend_common.auth.initial_data"
description: Initial data for frontend
responses:
200:
description: Initial data
schema:
type: object
required:
- user
- perms
properties:
user:
type: object
required:
- type
- permissions
properties:
type:
type: string
permissions:
type: array
items:
type: object
required:
- doc
- name
properties:
doc:
type: string
name:
type: string
authenticated_email:
type: string
perms:
type: object
additionalProperties:
type: string
/upload:
get:
operationId: "tooltool_api.api.search_batches"
description: Search upload batches.
parameters:
- name: q
in: query
description: |
Query parameter can match a substring of an authors email or a
batch message.
required: true
type: string
responses:
200:
description: An array of upload batches.
schema:
type: object
required:
- result
properties:
result:
type: array
items:
$ref: '#/definitions/UploadBatch'
post:
operationId: "tooltool_api.api.upload_batch"
description: |
Create a new upload batch. The response object will contain
a ``put_url`` for each file which needs to be uploaded -- which may not
be all! The caller is then responsible for uploading to those URLs.
The resulting signed URLs are valid for one hour, so uploads should
begin within that timeframe. Consider using Amazon MD5-verification
capabilities to ensure that the uploaded files are transferred
correctly, although the tooltool server will verify the integrity
anyway. The upload must have the header ``Content-Type:
application/octet-stream``.
The returned URLs are only valid for 60 seconds, so all upload requests
must begin within that timeframe. Clients should therefore perform all
uploads in parallel, rather than sequentially. This limitation is in
place to prevent malicious modification of files after they have been
verified.
parameters:
- name: body
in: body
description: Upload batch.
required: true
schema:
$ref: '#/definitions/UploadBatch'
- name: region
in: query
description: |
The region query argument ``region=us-west-1`` indicates a
preference for URLs in that region, although if the region is not
available then URLs in other regions may be returned.
required: false
type: string
responses:
200:
description: Upload batch.
schema:
type: object
required:
- result
properties:
result:
$ref: '#/definitions/UploadBatch'
/upload/{id}:
get:
operationId: "tooltool_api.api.get_batch"
description: Get a specific upload batch by id.
parameters:
- name: id
in: path
description: Upload batch id.
required: true
type: integer
responses:
200:
description: Upload batch.
schema:
$ref: '#/definitions/UploadBatch'
404:
description: Batch can not be found.
schema:
$ref: '#/definitions/Problem'
/upload/complete/sha512/{digest}:
get:
operationId: "tooltool_api.api.upload_complete"
description: |
Signal that a file has been uploaded and the server should begin
validating it. This is merely an optimization: the server also polls
occasionally for uploads and validates them when they appear.
Uploads cannot be safely validated until the upload URL has expired,
which occurs a short time after the URL is generated (currently 60
seconds but subject to change).
parameters:
- name: digest
in: path
required: true
type: string
responses:
202:
description: |
If the upload URL has expired, then the response is an HTTP 202
indicating that the signal has been accepted. If the URL has not
expired, then the response is an HTTP 409, and the
``X-Retry-After`` header gives a time, in seconds, that the client
should wait before trying again.
schema:
type: string
400:
description: Wrong digest.
schema:
$ref: '#/definitions/Problem'
409:
description: Upload expired, send retry eader
headers:
X-Retry-After:
description: Retry after timestamp in epoc.
type: string
/file:
get:
operationId: "tooltool_api.api.search_files"
description: Search for files matching the query.
parameters:
- name: q
in: query
description: |
The query matches against prefixes of hashes (at least 8
characters) or against filenames.
required: true
type: string
responses:
200:
description: File
schema:
type: object
required:
- result
properties:
result:
type: array
items:
$ref: '#/definitions/File'
/file/sha512/{digest}:
get:
operationId: "tooltool_api.api.get_file"
description: |
Get a single file, by its digest. Filenames are associated with upload
batches, not directly with files, so use ``GET /uploads`` to find files
by filename.
The returned File instance contains an ``instances`` attribute showing
the regions in which the file exists.
parameters:
- name: digest
in: path
required: true
type: string
responses:
200:
description: File
schema:
$ref: '#/definitions/File'
400:
description: Wrong digest.
schema:
$ref: '#/definitions/Problem'
404:
description: File can not be found.
schema:
$ref: '#/definitions/Problem'
500:
description: Internal server error.
schema:
$ref: '#/definitions/Problem'
patch:
operationId: "tooltool_api.api.patch_file"
description: |
Make administrative changes to an existing file. The body is a list of
changes to apply, each represented by a JSON object.
The object ``{"op": "delete_instances"}`` will cause all instances of
the file to be deleted. The file record itself will not be deleted,
as it is still a part of one or more upload batches, but until and
unless someone uploads a new copy, the content will not be available
for download.
If the change has op ``"set_visibility"``, then the files visibility
will be set to the value given by the changes ``visibility`` attribute.
For example, ``{"op": "set_visibility", "visibility": "internal"}``
will mark a file as "internal" after someone has accidentally uploaded
it with public visibility.
The returned File instance contains an ``instances`` attribute showing
any changes.
parameters:
- name: digest
in: path
required: true
type: string
- name: body
in: body
description: Upload batch.
required: true
schema:
type: array
items:
type: object
required:
- op
properties:
op:
type: string
enum:
- set_visibility
- delete_instances
visibility:
type: string
enum:
- public
- internal
responses:
200:
description: File
schema:
$ref: '#/definitions/File'
400:
description: Wrong op.
schema:
$ref: '#/definitions/Problem'
401:
description: No permission.
schema:
$ref: '#/definitions/Problem'
404:
description: File can not be found.
schema:
$ref: '#/definitions/Problem'
500:
description: Internal server error.
schema:
$ref: '#/definitions/Problem'
/sha512/{digest}:
get:
operationId: "tooltool_api.api.download_file"
description: Fetch a link to the file with the given sha512 digest.
parameters:
- name: digest
in: path
required: true
type: string
- name: region
in: query
required: false
description: |
The query argument ``region=us-west-1`` indicates a preference for
a URL in that region, although if the file is not available in tht
region then a URL from another region may be returned.
type: string
responses:
302:
description: Redirect to a signed download URL.
headers:
Location:
description: Download URL.
type: string
400:
description: sha512 digest is not valid.
schema:
$ref: '#/definitions/Problem'
401:
description: No permission to download file.
schema:
$ref: '#/definitions/Problem'
404:
description: File can not be found.
schema:
$ref: '#/definitions/Problem'
500:
description: Internal server error.
schema:
$ref: '#/definitions/Problem'
definitions:
UploadBatch:
type: object
description: |
An upload batch describes a collection of related files that are uploaded
together -- similar to a version-control commit. The message and files
list must be non-empty.
required:
- message
- files
properties:
id:
type: integer
description: Identifier for this batch
uploaded:
type: string # TODO: dateTime
description: |
The date and time when this upload occurred. This will be added by
the server and need not be specified when making a new upload.
author:
type: string
description: |
The author (uploader) of the batch. Do not include this when
submitting a batch for upload; it will be filled in based on the
request authentication.
message:
type: string
description: |
The message for the batch. Format this like a version-control
message.
files:
type: object
description: |
The collection of files in this batch, keyed by filename. Note that
filenames containing path separators (``\`` and ``/``) will be
rejected the tooltool client.
additionalProperties:
$ref: '#/definitions/File'
File:
type: object
description: |
A representation of a single file, identified by its contents rather than
its filename. Depending on context, this may contain URLs to download or
upload the file.
required:
- visibility
properties:
size:
type: integer
description: The size of the file, in bytes
digest:
type: string
description: The sha512 digest of the file contents.
algorithm:
type: string
description: |
The digest algorithm (reserved for future expansion; must always be
sha512).
visibility:
type: string
description: |
The visibility level of this file. When making an upload, the uploader
is (legally) responsible for selecting the correct visibility level.
default: public
enum:
- public
- internal
has_instances:
type: boolean
description: |
Boolean to determine whether the file is available to download.
instances:
type: array
description: |
The regions containing an instance of this file. This field is
generally omitted except where specified.
items:
type: string
get_url:
type: string
description: The URL from which this file can be downlaoded via HTTP GET.
put_url:
type: string
description: |
The URL to which this file can be uploaded via HTTP PUT. The URL
requires the request content-type to be ``application/octet-stream``.
Problem:
type: object
properties:
type:
type: string
format: uri
description: |
An absolute URI that identifies the problem type. When dereferenced,
it SHOULD provide human-readable documentation for the problem type
(e.g., using HTML).
default: 'about:blank'
example: 'https://zalando.github.io/problem/constraint-violation'
title:
type: string
description: |
A short, summary of the problem type. Written in english and readable
for engineers (usually not suited for non technical stakeholders and
not localized)
example: Service Unavailable
status:
type: integer
format: int32
description: |
The HTTP status code generated by the origin server for this occurrence
of the problem.
minimum: 100
maximum: 600
exclusiveMaximum: true
example: 503
detail:
type: string
description: |
A human readable explanation specific to this occurrence of the
problem.
example: Connection to database timed out
instance:
type: string
format: uri
description: |
An absolute URI that identifies the specific occurrence of the problem.
It may or may not yield further information if dereferenced.

Просмотреть файл

@ -1,144 +0,0 @@
# -*- coding: utf-8 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import importlib
import json
import threading
import time
import boto
import boto.s3
import boto.sqs
from cli_common import log
logger = log.get_logger(__name__)
class StopListening(Exception):
pass
class AWS(object):
def __init__(self, access_key_id, secret_access_key):
self.access_key_id = access_key_id
self.secret_access_key = secret_access_key
self._connections = {}
self._queues = {}
self._listeners = []
def connect_to(self, service_name, region_name):
key = service_name, region_name
if key in self._connections:
return self._connections[key]
# handle special cases
try:
fn = getattr(self, 'connect_to_' + service_name)
except AttributeError:
fn = self.connect_to_default
conn = fn(service_name, region_name)
self._connections[key] = conn
return conn
def connect_to_default(self, service_name, region_name):
# for the service, import 'boto.$service'
service = importlib.import_module('boto.' + service_name)
for region in service.regions():
if region.name == region_name:
break
else:
raise RuntimeError('invalid region %r' % (region_name,))
connect_fn = getattr(boto, 'connect_' + service_name)
return connect_fn(
region=region,
aws_access_key_id=self.access_key_id,
aws_secret_access_key=self.secret_access_key,
)
def connect_to_s3(self, service_name, region_name):
# special case for S3, which boto does differently than
# the other services
return boto.s3.connect_to_region(
region_name=region_name,
aws_access_key_id=self.access_key_id,
aws_secret_access_key=self.secret_access_key,
)
def get_sqs_queue(self, region_name, queue_name):
key = (region_name, queue_name)
if key in self._queues:
return self._queues[key]
sqs = self.connect_to('sqs', region_name)
queue = sqs.get_queue(queue_name)
if not queue:
raise RuntimeError('no such queue %r in %s' %
(queue_name, region_name))
self._queues[key] = queue
return queue
def sqs_write(self, region_name, queue_name, body):
queue = self.get_sqs_queue(region_name, queue_name)
m = boto.sqs.message.Message(body=json.dumps(body))
queue.write(m)
def sqs_listen(self, region_name, queue_name, read_args=None):
def decorate(func):
self._listeners.append(
(region_name, queue_name, read_args or {}, func))
return func
return decorate
def _listen_thd(self, region_name, queue_name, read_args, listener):
logger.info(
'Listening to SQS queue %r in region %s', queue_name, region_name)
try:
queue = self.get_sqs_queue(region_name, queue_name)
except Exception:
logger.exception(
'While getting queue %r in region %s; listening cancelled',
queue_name, region_name,
)
return
while True:
msg = queue.read(wait_time_seconds=20, **read_args)
if msg:
try:
listener(msg)
except StopListening: # for tests
break
except Exception:
logger.exception('while invoking %r', listener)
# note that we do nothing with the message; it will
# remain invisible for a while, then reappear and maybe
# cause another exception
continue
msg.delete()
def _spawn_sqs_listeners(self, _testing=False):
# launch a listening thread for each SQS queue
threads = []
for region_name, queue_name, read_args, listener in self._listeners:
thd = threading.Thread(
name='%s/%r -> %r' % (region_name, queue_name, listener),
target=self._listen_thd,
args=(region_name, queue_name, read_args, listener))
# set the thread to daemon so that SIGINT will kill the process
thd.daemon = True
thd.start()
threads.append(thd)
# sleep forever, or until we get a SIGINT, at which point the remaining
# threads will be killed during process shutdown
if not _testing: # pragma: no cover
while True:
time.sleep(2 ** 31)
return threads

Просмотреть файл

@ -1,244 +0,0 @@
# -*- coding: utf-8 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import asyncio
import datetime
import hashlib
import json
import click
import flask
import pytz
import sqlalchemy as sa
import cli_common.log
import cli_common.pulse
import tooltool_api.config
import tooltool_api.models
import tooltool_api.utils
logger = cli_common.log.get_logger(__name__)
def replicate_file(session, file, regions_config, aws):
logger2 = logger.bind(tooltool_sha512=file.sha512)
regions = set(regions_config)
file_regions = set([i.region for i in file.instances])
# only use configured source regions; if a region is removed
# from the configuration, we can't copy from it.
source_regions = file_regions & regions
if not source_regions:
# this should only happen when the only region containing a
# file is removed from the configuration
logger2.warning('no source regions for {}'.format(file.sha512))
return
source_region = source_regions.pop()
source_bucket = regions_config[source_region]
target_regions = regions - file_regions
logger2.info('replicating {} from {} to {}'.format(
file.sha512, source_region, ', '.join(target_regions)))
key_name = tooltool_api.utils.keyname(file.sha512)
for target_region in target_regions:
target_bucket = regions_config[target_region]
conn = aws.connect_to('s3', target_region)
bucket = conn.get_bucket(target_bucket)
# commit the session before replicating, since the DB connection may
# otherwise go away while we're distracted.
session.commit()
bucket.copy_key(
new_key_name=key_name,
src_key_name=key_name,
src_bucket_name=source_bucket,
storage_class='STANDARD',
preserve_acl=False,
)
try:
session.add(tooltool_api.models.FileInstance(file=file, region=target_region))
session.commit()
except sa.exc.IntegrityError:
session.rollback()
def verify_file_instance(sha512, size, key):
'''Verify that the given S3 Key matches the given size and digest.
'''
logger2 = logger.bind(tooltool_sha512=sha512)
if key.size != size:
logger2.warning('Uploaded file {} has unexpected size {}; expected {}'.format(sha512, key.size, size))
return False
m = hashlib.sha512()
for bytes in key:
m.update(bytes)
if m.hexdigest() != sha512:
logger2.warning('Digest of file {} does not match'.format(sha512))
return False
# verify some settings on the key, in case the uploader configured
# it differently
if key.storage_class != 'STANDARD':
logger2.warning('File {} was uploaded with incorrect storage class {}'.format(sha512, key.storage_class))
return False
if key.get_redirect(): # pragma: no cover
# (not covered because moto doesn't support redirects)
logger2.warning('File {} was uploaded with a website redirect set'.format(sha512, key.storage_class))
return False
# verifying the ACL is a bit tricky, so just set it correctly
key.set_acl('private')
return True
def check_pending_upload(session, pending_upload):
# we can check the upload any time between the expiration of the URL
# (after which the user can't make any more changes, but the upload
# may yet be incomplete) and 1 day afterward (ample time for the upload
# to complete)
sha512 = pending_upload.file.sha512
size = pending_upload.file.size
logger2 = logger.bind(tooltool_sha512=sha512)
if tooltool_api.utils.now() < pending_upload.expires.replace(tzinfo=pytz.UTC):
# URL is not expired yet
return
elif tooltool_api.utils.now() > (pending_upload.expires + datetime.timedelta(days=1)).replace(tzinfo=pytz.UTC):
# Upload will probably never complete
logger2.info('Deleting abandoned pending upload for {}'.format(sha512))
session.delete(pending_upload)
return
# connect and see if the file exists..
s3 = flask.current_app.aws.connect_to('s3', pending_upload.region)
s3_regions = flask.current_app.config.get('S3_REGIONS')
if not s3_regions or pending_upload.region not in s3_regions:
logger2.warning('Pending upload for {} was to an un-configured region'.format(sha512))
session.delete(pending_upload)
return
bucket = s3.get_bucket(s3_regions[pending_upload.region], validate=False)
key = bucket.get_key(tooltool_api.utils.keyname(sha512))
if not key:
# not uploaded yet
return
# commit the session before verifying the file instance, since the
# DB connection may otherwise go away while we're distracted.
session.commit()
if not verify_file_instance(sha512, size, key):
logger2.warning('Upload of {} was invalid; deleting key'.format(sha512))
key.delete()
session.delete(pending_upload)
session.commit()
return
logger2.info('Upload of {} considered valid'.format(sha512))
# add a file instance, but it's OK if it already exists
try:
tooltool_api.models.FileInstance(
file=pending_upload.file,
region=pending_upload.region,
)
session.commit()
except sa.exc.IntegrityError:
session.rollback()
# and delete the pending upload
session.delete(pending_upload)
session.commit()
# note that we don't try to copy the file out just yet; that can wait for
# the next scheduled distribution, and in the interim everyone will hit
# this one instance.
async def check_file_pending_uploads(channel, body, envelope, properties):
'''Check for pending uploads for a single file.
'''
body = json.loads(body.decode('utf-8'))
digest = body['payload']['digest']
session = flask.current_app.db.session
file = tooltool_api.models.File.query.filter(
tooltool_api.models.File.sha512 == digest).first()
if file:
for pending_upload in file.pending_uploads:
check_pending_upload(session, pending_upload)
session.commit()
await channel.basic_client_ack(delivery_tag=envelope.delivery_tag)
@click.command()
@flask.cli.with_appcontext
def cmd_check_pending_uploads():
'''Check for any pending uploads and verify them if found.
'''
session = flask.current_app.db.session
pending_uploads = tooltool_api.models.PendingUpload.query.all()
for pending_upload in pending_uploads:
check_pending_upload(session, pending_upload)
session.commit()
@click.command()
@flask.cli.with_appcontext
def cmd_replicate():
'''Replicate objects between regions as necessary.
'''
# fetch all files with at least one instance, but not a full complement
# of instances
regions = flask.current_app.config['S3_REGIONS']
session = flask.current_app.db.session
subq = session.query(
tooltool_api.models.FileInstance.file_id,
sa.func.count('*').label('instance_count'),
)
subq = subq.group_by(tooltool_api.models.FileInstance.file_id)
subq = subq.subquery()
q = session.query(tooltool_api.models.File)
q = q.join(subq, tooltool_api.models.File.id == subq.c.file_id)
q = q.filter(subq.c.instance_count < len(regions))
q = q.all()
for file in q:
replicate_file(session, file, regions, flask.current_app.aws)
session.commit()
@click.command()
@flask.cli.with_appcontext
def cmd_worker():
'''Check for pending uploads for a single file.
'''
pulse_user = flask.current_app.config['PULSE_USER']
pulse_pass = flask.current_app.config['PULSE_PASSWORD']
exchange = f'exchange/{pulse_user}/{tooltool_api.config.PROJECT_NAME}'
check_file_pending_uploads_consumer = cli_common.pulse.create_consumer(
pulse_user,
pulse_pass,
exchange,
tooltool_api.config.PULSE_ROUTE_CHECK_FILE_PENDING_UPLOADS,
check_file_pending_uploads,
)
logger.info(
'Listening for new messages on',
exchange=exchange,
route=tooltool_api.config.PULSE_ROUTE_CHECK_FILE_PENDING_UPLOADS,
)
cli_common.pulse.run_consumer(asyncio.gather(*[
check_file_pending_uploads_consumer,
]))

Просмотреть файл

@ -1,10 +0,0 @@
# -*- coding: utf-8 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
PROJECT_NAME = 'tooltool/api'
APP_NAME = 'tooltool_api'
PULSE_ROUTE_CHECK_FILE_PENDING_UPLOADS = 'check_file_pending_uploads'
SCOPE_PREFIX = f'project:releng:services/{PROJECT_NAME}'
SCOPE_MANAGE = f'{SCOPE_PREFIX}/manage'

Просмотреть файл

@ -1,8 +0,0 @@
# -*- coding: utf-8 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import tooltool_api
app = tooltool_api.create_app()

Просмотреть файл

@ -1,171 +0,0 @@
# -*- coding: utf-8 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import sqlalchemy as sa
from backend_common.db import db
ALLOWED_REGIONS = ('us-east-1', 'us-west-1', 'us-west-2')
class File(db.Model):
'''An file, identified by size and digest. The server may have zero
or many copies of a file.
'''
__tablename__ = 'releng_tooltool_files'
id = sa.Column(
sa.Integer,
primary_key=True,
)
size = sa.Column(
sa.Integer,
nullable=False,
)
sha512 = sa.Column(
sa.String(128),
unique=True,
nullable=False,
)
visibility = sa.Column(
sa.Enum('public', 'internal', name='visibility'),
nullable=False,
)
instances = sa.orm.relationship('FileInstance', backref='file')
# note that changes to this dictionary will not be reflected to the DB;
# add or delete BatchFile instances directly instead.
@property
def batches(self):
return {bf.filename: bf.batch for bf in self._batches}
def to_dict(self, include_instances=False):
file = dict(
size=self.size,
digest=self.sha512,
algorithm='sha512',
visibility=self.visibility,
has_instances=any(self.instances)
)
if include_instances:
file['instances'] = [i.region for i in self.instances]
return file
class Batch(db.Model):
'''Upload batches, with batch metadata, linked to the uploaded files.
'''
__tablename__ = 'releng_tooltool_batches'
id = sa.Column(
sa.Integer,
primary_key=True,
)
uploaded = sa.Column(
sa.DateTime,
index=True,
nullable=False,
)
author = sa.Column(
sa.Text,
nullable=False,
)
message = sa.Column(
sa.Text,
nullable=False,
)
# note that changes to this dictionary will not be reflected to the DB;
# add or delete BatchFile instances directly instead.
@property
def files(self):
return {
batch_file.filename: batch_file.file
for batch_file in self._files
}
def to_dict(self):
return dict(
id=self.id,
uploaded=self.uploaded,
author=self.author,
message=self.message,
files={
filename: file.to_dict()
for filename, file in self.files.items()
}
)
class FileInstance(db.Model):
'''A verified instance of a file in a single region.
'''
__tablename__ = 'releng_tooltool_file_instances'
file_id = sa.Column(
sa.Integer,
sa.ForeignKey('releng_tooltool_files.id'),
primary_key=True,
)
region = sa.Column(
sa.Enum(*ALLOWED_REGIONS, name='region'),
primary_key=True,
)
class BatchFile(db.Model):
'''An association of upload batches to files, with filenames
'''
__tablename__ = 'releng_tooltool_batch_files'
file_id = sa.Column(
sa.Integer,
sa.ForeignKey('releng_tooltool_files.id'),
primary_key=True,
)
file = sa.orm.relationship('File', backref='_batches')
batch_id = sa.Column(
sa.Integer,
sa.ForeignKey('releng_tooltool_batches.id'),
primary_key=True,
)
batch = sa.orm.relationship('Batch', backref='_files')
filename = sa.Column(
sa.Text,
nullable=False,
)
class PendingUpload(db.Model):
'''Files for which upload URLs have been generated, but which haven't yet
been uploaded. This table is used to poll for completed uploads, and to
prevent trusting files for which there is an outstanding signed upload
URL.
'''
__tablename__ = 'releng_tooltool_pending_upload'
file_id = sa.Column(
sa.Integer,
sa.ForeignKey('releng_tooltool_files.id'),
nullable=False,
primary_key=True,
)
expires = sa.Column(
sa.DateTime,
index=True,
nullable=False,
)
region = sa.Column(
sa.Enum(*ALLOWED_REGIONS, name='region'),
nullable=False,
)
file = sa.orm.relationship('File', backref='pending_uploads')

Просмотреть файл

@ -1 +0,0 @@
../../client/tooltool.py

Просмотреть файл

@ -1,22 +0,0 @@
# -*- coding: utf-8 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import datetime
import re
import typing
import pytz
def now() -> datetime.datetime:
return datetime.datetime.utcnow().replace(tzinfo=pytz.UTC)
def keyname(digest: str) -> str:
return 'sha512/{}'.format(digest)
def is_valid_sha512(sha512: str) -> typing.Optional[typing.Match[str]]:
return re.compile(r'^[0-9a-f]{128}$').match(sha512)

Просмотреть файл

@ -1,3 +0,0 @@
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this file,
You can obtain one at http://mozilla.org/MPL/2.0/.

Просмотреть файл

@ -1,11 +0,0 @@
# NOTE: these files must *also* be specified in setup.py
include LICENSE.txt
include Makefile
include README.md
include VERSION
include requirements-dev.txt
include requirements.txt
include test.sh
include test_file.ogg
include test_tooltool.py
include tooltool.py

Просмотреть файл

@ -1,17 +0,0 @@
check: python-tests shell-tests tox
shell-tests:
sh test.sh
python-tests:
clear
python test_tooltool.py
python-test-%:
clear
python test_tooltool.py $*
tox:
tox
.PHONY: check clean shell-tests python-tests python-tests-% tox

Просмотреть файл

@ -1,41 +0,0 @@
# Tooltool
This is tooltool. Tooltool is a program that helps make downloading large
binaries easier in a CI environment. The program creates a json based manifest
that is small compared to the binaries. That manifest is transmitted to the
machine that needs the binary somehow (checked in, included in tarball, etc)
where the machine will run tooltool to download.
When using the fetch mode, the program will check to see if the file exists
locally. If this file does not exist locally the program will try to fetch
from one of the base URLs provided. The API that tooltool uses to fetch files
is exceedingly simple. the API is that each file request will look for an http
resource that is a combination of an arbitrary base url, a directory that is
named as the hashing algorithm used and the hashing results of each file stored.
Example, using base url of "http://localhost:8080/tooltool", algorithm of "sha512"
and a file that hashes to "abcedf0123456789", tooltool would look for the file
at "http://localhost:8080/tooltool/sha512/abcdef0123456789". If there is a local
file that has the filename specified in the manifest already, tooltool will not
overwrite by default. In this case, tooltool will exit with a non-0 exit value.
If overwrite mode is enabled, tooltool will overwrite the local file with the
file specified in the manifest.
## Server
This repository contains only the tooltool client -- `tooltool.py`.
The tooltool server component is a part of [RelengAPI](https://github.com/mozilla/build-relengapi).
If you want to use the client, just copy out `tooltool.py` -- it has no
dependencies.
## Development
To hack on the tooltool client, install into a virtualenv with
pip install -e .[test]
Send pull requests through GitHub.
Both the client and the server components are covered by Travis, via the
`validate.sh` script which you can run yourself.

Просмотреть файл

@ -1 +0,0 @@
1.3.0

Просмотреть файл

@ -1,23 +0,0 @@
[report]
# Regexes for lines to exclude from consideration
exclude_lines =
# Have to re-enable the standard pragma
pragma: no cover
# Don't complain about missing debug-only code:
def __repr__
if self\.debug
# Don't complain if tests don't hit defensive assertion code:
raise AssertionError
raise NotImplementedError
# Don't complain if non-runnable code isn't run:
if 0:
if __name__ == .__main__.:
# 'pass' generally means 'this won't be called'
^ *pass *$
omit =
*/test_*

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше