Merge re-write into demo

This commit is contained in:
Matthew Ross 2020-05-27 14:09:57 -04:00
commit c9444fa5a5
331 changed files with 41870 additions and 38034 deletions

13
.editorconfig Normal file
View File

@ -0,0 +1,13 @@
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false

16
.github/CONTRIBUTING.md vendored Normal file
View File

@ -0,0 +1,16 @@
## Contributing
### Reporting Issues
Fill out the template as completely as possible.
The more information you can provide, the easier it is to research your issue.
### Development
Fork the repository and make your changes on the `dev` branch.
Create a pull request against the `dev` branch to merge your changes with
the main repository.
Make sure to include/update unit tests.

2
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,2 @@
github: kiswa
custom: "https://paypal.me/kiswacom"

31
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,31 @@
---
name: Bug report
about: Found a bug with TaskBoard?
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Please complete the following information:**
- OS: [e.g. Windows 10]
- Browser [e.g. Chrome, Firefox]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Got an idea for TaskBoard?
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

BIN
.github/boards.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 KiB

BIN
.github/settings.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 KiB

BIN
.github/tasks.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 KiB

57
.gitignore vendored
View File

@ -1,5 +1,52 @@
api/taskboard.db
api/uploads/*
tags
.idea/*
vendor/
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/dist-server
/tmp
/out-tsc
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
.phpunit.result.cache
# e2e
/e2e/*.js
/e2e/*.map
# System Files
.DS_Store
Thumbs.db
# Project Specific Files
/src/api/vendor
*.db
*.sqlite
*.zip

View File

@ -1,18 +0,0 @@
# Thanks to GliderPro's comment on Issue 102
SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/text text/html text/plain text/xml text/css application/x-javascript application/javascript text/javascript
</IfModule>
<IfModule mod_expires.c>
ExpiresActive On
#ExpiresByType image/jpg "access 1 year"
#ExpiresByType image/jpeg "access 1 year"
#ExpiresByType image/gif "access 1 year"
#ExpiresByType image/png "access 1 year"
#ExpiresByType text/css "access 1 year"
#ExpiresByType text/x-javascript "access 9 months"
#ExpiresByType image/x-icon "access 1 year"
ExpiresDefault "access 2 seconds"
</IfModule>

24
.travis.yml Normal file
View File

@ -0,0 +1,24 @@
language: php
php:
- '7.2'
- '7.3'
- '7.4'
script:
- npm test
before_script:
- nvm install 13
- nvm use 13
- npm i -g npm@6
- npm i
- touch tests.db
- chmod a+w tests.db
- cd src/api/ && composer update && cd ../../
after_success:
- echo -e "<?php\n print phpversion();" > version.php
- curl "https://raw.githubusercontent.com/andreafabrizi/Dropbox-Uploader/master/dropbox_uploader.sh" -o dropbox_uploader.sh
- chmod +x dropbox_uploader.sh
- touch fakeconfig
- echo "OAUTH_ACCESS_TOKEN=$OAUTH_ACCESS_TOKEN" > fakeconfig
- ./dropbox_uploader.sh -f fakeconfig upload coverage/api coverage-$(php version.php)/
- ./dropbox_uploader.sh -f fakeconfig upload coverage/app/lcov-report coverage-$(php version.php)/

14
CHANGELOG.md Normal file
View File

@ -0,0 +1,14 @@
## v1.0.0
### New Features
* Complete re-write
* Conversion script for migrating to new database format (sqlite only)
### Updates
* N/A
### Bug Fixes
* N/A

View File

@ -1,19 +0,0 @@
# Contributing
* All pull requests must be made against the `dev` branch
* Complete documentation is available at http://taskboard.matthewross.me/docs/
* In fact, more information about TaskBoard in general is available at http://taskboard.matthewross.me/
## Versioning
To clarify for myself and others, here is the versioning scheme used by Taskboard as of version v0.3.0 (for versions prior, it was basically just make it up as I went along):
Versions are defined in three parts; major, minor and revision. For example, v0.3.0 is Major 0, Minor 3, Revision 0.
* Major version numbers change in two cases:'
* v1.0.0 is when I feel TaskBoard is *Production Ready*
* When changes are made that add significant new features or break some form of compatibility with previous versions (e.g. requiring a new database)
Minor version numbers change when a chosen set of changes are ready for use. The changes are issues reported by users or added by myself for tracking planned features.
Revision version numbers change when a bug is reported that needs to be fixed before another minor version is released.

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2014 Matthew Ross
Copyright (c) 2014-2020 Matthew Ross
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

248
README.md
View File

@ -1,132 +1,216 @@
#TaskBoard
# TaskBoard
A [Kanban](http://en.wikipedia.org/wiki/Kanban_board)-inspired app for keeping track of things that need to get done.
**N O T I C E - This branch is in active development! Not all items in this README are final or guaranteed to be accurate until it is merged to `master`!**
The goal of TaskBoard is to provide a simple and clean interface to a functional and minimal application for keeping track of tasks. It's not trying to be the next Trello or LeanKit.
[![Build Status](https://travis-ci.org/kiswa/TaskBoard.svg)](https://travis-ci.org/kiswa/TaskBoard) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/77952f4ac9b44e9fbebe7758442d356d)](https://www.codacy.com/app/kiswa-com/TaskBoard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=kiswa/TaskBoard&amp;utm_campaign=Badge_Grade) [![Join the chat at https://gitter.im/kiswa/TaskBoard](https://badges.gitter.im/kiswa/TaskBoard.svg)](https://gitter.im/kiswa/TaskBoard?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the discussion at https://reddit.com/r/TaskBoard](https://cdn.rawgit.com/kiswa/TaskBoard/09444718053f7405636ab2205ad0f12413df7a20/reddit.svg)](https://reddit.com/r/TaskBoard)
###How It's Made
A [Kanban](http://en.wikipedia.org/wiki/Kanban_board)-inspired app for keeping
track of things that need to get done.
1. Front end
The goal of TaskBoard is to provide a simple and clean interface to a
functional and minimal application for keeping track of tasks.
**It's not trying to be the next Trello or LeanKit.**
* [AngularJS](https://angularjs.org/) single-page app.
## Installation
* [ng-context-menu](https://github.com/ianwalter/ng-context-menu), [jQueryUI Datepicker](http://jqueryui.com/datepicker/), [Spectrum](http://bgrins.github.io/spectrum/) colorpicker, [(noty)](http://ned.im/noty/) notifications, [marked](https://github.com/chjj/marked) Markdown parser, and [-prefix-free](http://leaverou.github.io/prefixfree/) CSS prefix helper.
### Prerequisites
* [Bootstrap](http://getbootstrap.com/) for base look and feel.
A web server running PHP 7.x with sqlite enabled (it may work on PHP 5.6, but
is not supported). See [PHP Supported Versions](https://www.php.net/supported-versions.php).
2. Back end
The server must have `sqlite3` and `php7-sqlite` installed.
**- OR -**
If you're comfortable changing code, you can use any database [supported by RedBeanPHP](https://redbeanphp.com/index.php?p=/connection).
* RESTful API written in PHP, using [Slim Framework](http://www.slimframework.com/) for routing and [RedBeanPHP](http://www.redbeanphp.com/) for database ORM. Also uses [PHPMailer](https://github.com/PHPMailer/PHPMailer) for sending emails.
### Install
* Token-based authentication.
Installing TaskBoard is as easy as 1, 2, 3!
* SQLite database.
1. Download [the latest release](#) since v1.0.0
2. Extract it to your webserver
3. Verify the `api` directory is writable
##Installation
If you intend to use email features, you will need to edit `api/helpers/mailer.php`.
###Requirements
A web server running PHP with sqlite enabled. Developed and tested under Apache2 running PHP 5.5+.
### Server Config
The server must have `sqlite` and `php5-sqlite` installed, as well as the `rewrite` and `expires` Apache modules.
#### Apache
**Note:** For Apache v2.3.9 and later, virtual host for a site should have [`AllowOverride All`](http://httpd.apache.org/docs/2.4/mod/core.html#allowoverride) for TaskBoard root directory. Otherwise, .htaccess files will be completely ignored.
The directory you create for TaskBoard must have `AllowOverride` set so the
`.htaccess` files work.
**Optional:** to build minimized JavaScript and CSS (Install step 3) you must have curl and a jre installed, tested with `openjdk-7-jre` and `openjdk-8-jre`.
You also have to have `mod_rewrite` installed and enabled.
###Install
#### NGINX
Installing TaskBoard is as easy as 1, 2, (3), 4!
TODO
**Note:** You can skip step 3 if you don't care about minification of JavaScript and CSS for a production environment!
#### IIS
1. Clone the repository directly where you want it, or clone and copy to it's final location.
See the [Wiki Page](https://github.com/kiswa/TaskBoard/wiki/TaskBoard-on-IIS)
git clone https://github.com/kiswa/TaskBoard.git
### First Use
2. Install the PHP dependencies via composer. Open `TaskBoard/` in a terminal and run `./build/composer.phar install`
Open a web browser to the location you installed TaskBoard and use `admin` as
the username and password to log into TaskBoard.
3. (Optional) Open `TaskBoard/build/` in a terminal and run `./build-all`.
Go to the `Settings` page to update your user (username, email, password,
*etc.*) and create your first board!
4. Visit the site and log in with the username and password `admin` (and don't forget to change the password once you're in!).
## Features
**Note:** Ensure `TaskBoard/api/` is writable so the back end can do its job!
### Users & Settings
### Development instance
There are three types of users, and the settings page is slightly different
for each.
Now you can start a simple development environment with the php internal webserver.
* User - View boards assigned to them and update their own settings and options.
* Board Admin - Same as above, with the ability to manage boards they are
added to.
* Admin - Same as above, with the ability to add/remove users and boards.
`php -S 127.0.0.1:8080 devrouter.php`
![Settings Page](./.github/settings.png)
After launching the internal webserver go to http://127.0.0.1:8080/
### Boards
##Features
Each board may have as many columns as needed. For example, a software project
might have columns like:
###Email
TaskBoard will email you (if you supply it with an email address) about changes in the following cases: Board Created, Board Updated, Item Created, Item Edited, Item Comment Created, and Item Comment Edited.
For now, it emails all users assigned to the related Board. There will be further work done on this to allow more fine-grained control of emails.
###Settings
The settings page allows normal users to see what boards they have access to and edit their user settings.
Admin users have the same, with the additional abilities of adding/editing users and boards, and viewing a log of all activity in TaskBoard.
![Settings Image](http://taskboard.matthewross.me/docs/images/settings-standard.png)
###Boards
Each board may have as many columns as needed. For example, a software project might have columns like:
* Proposed
* Backlog
* In Work
* Test
* Done
* Archived
* Proposed
* Backlog
* In Work
* Test
* Done
* Archived
Or maybe you want to track a simple todo list with just:
* Todo
* Doing
* Done
* Todo
* Doing
* Done
It's all up to you! However many columns you have, each column may have tasks added to it, and tasks can be dragged to other columns as they progress (or edited and assigned to a new column).
It's all up to you! However many columns you have, each column may have tasks
added to it, and tasks can be dragged to other columns as they progress
(or edited and assigned to a new column).
Boards may also have categories for additional organization, *e.g.* `Bug`, `Enhancement`, `New Feature`.
Boards may also have categories for additional organization, *e.g.* `Bug`,
`Enhancement`, `New Feature`.
![Board Image](http://taskboard.matthewross.me/images/board.png)
![Boards Page](./.github/boards.png)
###Items
An item (task) only has to have a Title to be added to a board, but there is much more than that available. Items may be assigned to any user on the board (or left Unassigned), and include options for Due Date, Color, Points (an optional difficulty rating), and Category.
### Tasks
TaskBoard uses a [Markdown](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#table-of-contents) parser for the Description, allowing for better display of details (like this readme).
A task only has to have a Title to be added to a board, but there is much more
available. Tasks may be assigned to any user on the board (or left Unassigned),
and include options for Due Date, Color, Points (an optional difficulty
rating), and Category.
Once an item has been entered, it may have Comments or Attachments added to it by viewing the task detail. There is a link to edit the item, which takes you to a modal much like the original add item dialog. For admin users, there is also a link to delete the item. This view also shows the task's activity log on the right side of the screen, displaying the complete history of events related to the item.
TaskBoard uses a [Markdown](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#table-of-contents)
parser for the Description, allowing for better display of details
(like this readme).
![Task Image](http://taskboard.matthewross.me/docs/images/view-item2.png)
Once a task has been entered, it may have Comments (also supporting Markdown)
or Attachments added to it by viewing the task detail. There is a link to edit
the task, which takes you to a modal much like the original add task dialog.
##Feedback
For admin users, there is also a link to delete the task. This view also shows
the task's activity log on the side of the screen, displaying the complete
history of events related to the task.
Constructive feedback is appreciated! If you have ideas for improvement, please [add an issue](https://github.com/kiswa/TaskBoard/issues) or submit a pull request.
![Tasks](./.github/tasks.png)
## Development
Developing on TaskBoard is pretty simple too.
1. Clone the repository and navigate to it `git clone https://github.com/kiswa/TaskBoard && cd TaskBoard/`
2. Run `git checkout dev` to work on the `dev` branch
3. If you don't have it already, install the Angular CLI globally with `npm i -g @angular/cli`
4. Run `npm i` to install dependencies (this also installs the API dependencies)
5. Run `npm run watch` for the build to automatically run after any change
### Unit Tests
Both the API and App are unit tested. To run all tests, use the command
`npm run test`. For only one set, run `npm run test:api` or `npm run test:app`.
To have the app tests run & update as you work, use the command
`npm run test:watch`.
If you want to run a single API test, add the following comment block before
the test function and use the command `npm run test:api-single`.
``` php
/**
* @group single
*/
```
If you want to run a single App test, change the test from
`it('should do something', ...);` to `fit('should do something', ...);` and
only that test will run.
These tests are run by [Travis CI](https://travis-ci.org/) on PRs and commits.
A PR with failing or missing tests will not be merged.
## Contributing
Fork the repository and make your changes on the `dev` branch.
Create a pull request against the `dev` branch to merge your changes with the
main repository.
Make sure to include/update unit tests.
## Feedback
Constructive feedback is appreciated! If you have ideas for improvement, please
[add an issue](https://github.com/kiswa/TaskBoard/issues) or implement it and
submit a pull request.
If you find a bug, please post it on the [Issue Tracker](https://github.com/kiswa/TaskBoard/issues).
##Lines of Code
## How It's Made
It's silly to use [LOC](http://en.wikipedia.org/wiki/Source_lines_of_code) as a metric, but it can be interesting to see what goes into a project.
This is only for TaskBoard files (library code is excluded), using [CLOC](http://cloc.sourceforge.net/).
### Front End
Count was done from parent directory of TaskBoard as `cloc TaskBoard --exclude-dir=lib,vendor`.
* [Angular](https://angular.io/) single-page app (not AngularJS)
* [Bourbon](http://bourbon.io/) and [Neat](http://neat.bourbon.io/) SCSS
library & grid
* [scss-base](https://www.npmjs.com/package/scss-base) for the base styling
* [marked](https://github.com/chjj/marked) Markdown parser
* [Chartist.js](https://gionkunz.github.io/chartist-js/) for all charts
Language | Files | Blank Lines | Comments | Code
-------------------|-------:|-------------:|---------:|---------:
Javascript | 23 | 220 | 34 | 2087
PHP | 9 | 235 | 55 | 1220
HTML | 24 | 12 | 10 | 1160
CSS | 1 | 13 | 26 | 703
Bourne Again Shell | 4 | 12 | 0 | 58
JSON | 1 | 0 | 0 | 17
XML | 1 | 0 | 0 | 12
__SUM:__ | __63__ | __492__ | __125__ | __5257__
### Back End
Counts Last Updated: Nov 8, 2015
* [Slim Framework](http://www.slimframework.com/) and
[RedBeanPHP](http://www.redbeanphp.com/) for a RESTful API
* [PHPMailer](https://github.com/PHPMailer/PHPMailer) for sending emails
* [JWT](https://jwt.io/) authentication
* [SQLite](https://www.sqlite.org/) database
## Lines of Code
Because I like seeing the numbers.
### `src`
Language | Files | Blank | Comment | Code
-----------|--------:|---------:|---------:|---------:
TypeScript | 66 | 958 | 126 | 4057
PHP | 20 | 752 | 40 | 2265
HTML | 21 | 257 | 0 | 1540
SASS | 14 | 298 | 10 | 1344
**SUM:** | **121** | **2265** | **176** | **9206**
Command: `cloc --exclude-dir=vendor --exclude-ext=json,svg,ini src/`
### `test`
Language | Files | Blank | Comment | Code
-----------|-------:|---------:|---------:|---------:
TypeScript | 38 | 1009 | 8 | 3523
PHP | 11 | 795 | 19 | 2300
**SUM:** | **49** | **1804** | **27** | **5823**
Command: `cloc --exclude-ext=xml test/`

View File

@ -1,9 +0,0 @@
v0.3.1
Changelog
* Fix automatic action color change bug.
* Fix new user default board bug.
* Fix boards button bug.
* Fix modal display bug.
* Allow email to be reset to nothing.

136
angular.json Normal file
View File

@ -0,0 +1,136 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"TaskBoard": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist",
"aot": true,
"index": "src/index.html",
"main": "src/main.ts",
"tsConfig": "tsconfig.app.json",
"showCircularDependencies": false,
"polyfills": "src/polyfills.ts",
"assets": [
{
"glob": "**/!(composer*)*.*",
"input": "src/api",
"output": "/api"
},
{
"glob": "*.json",
"input": "src/json",
"output": "/strings"
},
"src/.htaccess",
"src/images",
"src/fonts"
],
"styles": [
"src/scss/main.scss"
],
"scripts": []
},
"configurations": {
"production": {
"aot": true,
"optimization": true,
"outputHashing": "all",
"sourceMap": true,
"extractCss": true,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "TaskBoard:build"
},
"configurations": {
"production": {
"browserTarget": "TaskBoard:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "TaskBoard:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"karmaConfig": "./karma.conf.js",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"scripts": [],
"styles": [
"src/scss/main.scss"
],
"assets": [
{
"glob": "**/!(composer*)*.*",
"input": "src/api",
"output": "/api"
},
{
"glob": "*.json",
"input": "src/json",
"output": "/strings"
},
"src/.htaccess",
"src/images",
"src/fonts"
]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"defaultProject": "TaskBoard",
"cli": {
"warnings": {
"typescriptMismatch": false
}
},
"schematics": {
"@schematics/angular:component": {
"prefix": "tb",
"styleext": "scss"
},
"@schematics/angular:directive": {
"prefix": "tb"
}
}
}

View File

@ -1,10 +0,0 @@
ExpiresActive Off
RewriteEngine On
RewriteCond %{REQUEST_URI}::$1 ^(.*?/)(.*)::\2$
RewriteRule ^(.*)$ - [E=BASE:%1]
RewriteRule ^taskboard.db$ %{ENV:BASE}api.php [QSA,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ %{ENV:BASE}api.php [QSA,L]

View File

@ -1,53 +0,0 @@
<?php
require_once('jsonResponse.php');
require_once(__DIR__.'/../vendor/autoload.php');
use Slim\Slim;
use RedBeanPHP\R;
$app = new Slim();
$app->response->headers->set('Content-Type', 'application/json');
$jsonResponse = new JsonResponse();
require_once('helpers.php'); // Must come after $jsonResponse exists.
// Catch Exception if connection to DB failed
function exceptionHandler($exception) {
global $jsonResponse;
header('Content-Type: application/json');
http_response_code(503);
$jsonResponse->message = 'API Error.';
$jsonResponse->data = $exception->getMessage();
$jsonResponse->trace = $exception->getTrace();
echo $jsonResponse->asJson();
};
set_exception_handler('exceptionHandler');
R::setup('sqlite:'.__DIR__.'/taskboard.db');
R::setAutoResolve(TRUE);
createInitialUser();
$app->notFound(function() use ($app, $jsonResponse) {
$app->response->setStatus(404);
$jsonResponse->message = 'Matching API call Not found.';
$app->response->setBody($jsonResponse->asJson());
});
$app->get('/authenticate', function() use($app, $jsonResponse) {
if (validateToken()) {
$jsonResponse->message = 'Token is authenticated.';
}
$app->response->setBody($jsonResponse->asJson());
});
require_once('mailFactory.php');
require_once('userRoutes.php');
require_once('boardRoutes.php');
require_once('itemRoutes.php');
$app->run();
R::close();

View File

@ -1,177 +0,0 @@
<?php
use RedBeanPHP\R;
// Get the list of active boards
$app->get('/boards', function() use($app, $jsonResponse) {
if (validateToken()) {
$jsonResponse->addBeans(getBoards());
}
$app->response->setBody($jsonResponse->asJson());
});
// Create new board.
$app->post('/boards', function() use($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
if (validateToken(true)) {
$board = R::dispense('board');
loadBoardData($board, $data);
$actor = getUser();
logAction($actor->username . ' added board ' . $board->name, null, $board->export());
$jsonResponse->addBeans(getBoards());
$jsonResponse->addAlert('success', 'New board ' . $board->name . ' created.');
foreach($board->sharedUser as $user) {
$body = getNewBoardEmailBody($board->id, $user->username, $board->name);
$subject = 'TaskBoard: New board created!';
$recipient = $user->username;
$email = $user->email;
sendEmail($email, $recipient, $subject, $body);
}
}
$app->response->setBody($jsonResponse->asJson());
});
// Update existing board.
$app->post('/boards/update', function() use($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
if (validateToken(true)) {
$board = R::load('board', $data->boardId);
if ($board->id) {
$before = $board->export();
loadBoardData($board, $data);
$jsonResponse->addAlert('success', 'Board ' . $board->name . ' edited.');
$actor = getUser();
logAction($actor->username . ' updated board ' . $board->name, $before, $board->export());
}
$jsonResponse->addBeans(getBoards());
foreach($board->sharedUser as $user) {
$body = getEditBoardEmailBody($board->id, $user->username, $board->name);
$subject = 'TaskBoard: Board updated!';
$recipient = $user->username;
$email = $user->email;
sendEmail($email, $recipient, $subject, $body);
}
}
$app->response->setBody($jsonResponse->asJson());
});
// Remove a board.
$app->post('/boards/remove', function() use($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
if (validateToken(true)) {
$board = R::load('board', $data->boardId);
if ($board->id == $data->boardId) {
$before = $board->export();
foreach($board->sharedUser as $user) {
if ($user->defaultBoard == $data->boardId) {
$user->defaultBoard = null;
R::store($user);
}
}
R::trashAll($board->xownLane);
R::trashAll($board->xownCategory);
R::trash($board);
R::exec('DELETE from board_user WHERE board_id = ?', [$data->boardId]);
$jsonResponse->addAlert('success', 'Removed board ' . $board->name . '.');
$actor = getUser();
logAction($actor->username . ' removed board ' . $board->name, $before, null);
}
$jsonResponse->addBeans(getBoards());
$jsonResponse->users = R::exportAll(getUsers());
}
$app->response->setBody($jsonResponse->asJson());
});
$app->post('/autoactions', function() use($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
if (validateToken(true)) {
$board = R::load('board', $data->boardId);
if ($board->id) {
$autoAction = R::dispense('autoaction');
$autoAction->triggerId = $data->triggerId;
$autoAction->secondaryId = $data->secondaryId;
$autoAction->actionId = $data->actionId;
$autoAction->color = $data->color;
$autoAction->categoryId = $data->categoryId;
$autoAction->assigneeId = $data->assigneeId;
}
$board->ownAutoaction[] = $autoAction;
R::store($board);
$jsonResponse->addAlert('success', 'Automatic action created.');
$actions = R::findAll('autoaction');
$jsonResponse->addBeans($actions);
}
$app->response->setBody($jsonResponse->asJson());
});
$app->get('/autoactions', function() use($app, $jsonResponse) {
if (validateToken()) {
$actions = R::findAll('autoaction');
$jsonResponse->addBeans($actions);
}
$app->response->setBody($jsonResponse->asJson());
});
$app->post('/autoactions/remove', function() use($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
if (validateToken(true)) {
$autoAction = R::load('autoaction', $data->actionId);
R::trash($autoAction);
$actions = R::findAll('autoaction');
$jsonResponse->addBeans($actions);
$jsonResponse->addAlert('success', 'Automatic action removed.');
}
$app->response->setBody($jsonResponse->asJson());
});
// Toggle the expand/collapse state of a lane for the current user.
$app->post('/lanes/:laneId/toggle', function($laneId) use($app, $jsonResponse) {
if (validateToken()) {
$user = getUser();
$lane = R::load('lane', $laneId);
$collapsed = R::findOne('collapsed', ' user_id = ? AND lane_id = ? ', [$user->id, $laneId]);
if (null != $collapsed) {
R::trash($collapsed);
$jsonResponse->message = 'Expanded lane ' . $lane->name;
} else {
$collapsed = R::dispense('collapsed');
$collapsed->userId = $user->id;
$collapsed->laneId = $laneId;
R::store($collapsed);
$jsonResponse->message = 'Collapsed lane ' . $lane->name;
}
$jsonResponse->addBeans(getBoards());
}
$app->response->setBody($jsonResponse->asJson());
})->conditions(['laneId' => '\d+']); // Numbers only.
$app->post('/boards/:boardId/toggleActive', function($boardId) use($app, $jsonResponse) {
if (validateToken()) {
$user = getUser();
if ($user->isAdmin) {
$board = R::load('board', $boardId);
$before = $board->export();
$board->active = !$board->active;
R::store($board);
$state = $board->active ? 'active' : 'inactive';
$jsonResponse->message = 'Set board ' . $board->name . ' ' . $state;
$jsonResponse->addBeans(getBoards());
logAction($user->username . ' changed active status of board ' . $board->name,
$before, $board->export());
}
}
$app->response->setBody($jsonResponse->asJson());
})->conditions(['boardId' => '\d+']); // Numbers only.

View File

@ -1,460 +0,0 @@
<?php
use RedBeanPHP\R;
// Patch for when using nginx instead of apache, source: http://php.net/manual/en/function.getallheaders.php#84262
if (!function_exists('getallheaders')) {
function getallheaders() {
$headers = '';
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$headers[str_replace(' ', '-', ucwords(strtolower(
str_replace('_', ' ', substr($name, 5))
)))] = $value;
}
}
return $headers;
}
}
// Log an action. If $itemId is set, it is an item action.
function logAction($comment, $oldValue, $newValue, $itemId=null) {
$activity = R::dispense('activity');
$activity->comment = $comment;
$activity->oldValue = json_encode($oldValue);
$activity->newValue = json_encode($newValue);
$activity->timestamp = time();
$activity->itemId = $itemId;
R::store($activity);
}
// Sets the JWT for the current user and stores in DB for lookup.
function setUserToken($user, $expires) {
$token = JWT::encode(array(
'exp' => time() + $expires,
'uid' => $user->id
), getJwtKey());
$dbToken = R::dispense('token');
$dbToken->token = $token;
if (null == $user->ownToken) {
$user->ownToken = [];
}
$user->ownToken[] = $dbToken;
R::store($user);
}
// Get the user making the current request.
function getUser() {
global $jsonResponse;
if (isset(getallheaders()['Authorization'])) {
$hash = getallheaders()['Authorization'];
try {
$payload = JWT::decode($hash, getJwtKey());
$user = R::load('user', $payload->uid);
if ($user->id) {
return $user;
}
} catch (Exception $e) {}
}
$jsonResponse->addAlert('error', 'Unable to load user. Please try again.');
return null;
}
function getUserByID($id) {
try {
$user = R::load('user', $id);
if ($user->id) {
return $user;
}
} catch (Exception $e) {}
return null;
}
function getLaneByID($id) {
try {
$lane = R::load('lane', $id);
if ($lane->id) {
return $lane;
}
} catch (Exception $e) {}
return null;
}
// Get all users.
function getUsers($sanitize = true) {
try {
$hash = getallheaders()['Authorization'];
$payload = JWT::decode($hash, getJwtKey());
$users = R::findAll('user');
if ($sanitize) {
foreach($users as &$user) {
sanitize($user);
}
}
return $users;
} catch (Exception $e) {}
$jsonResponse->addAlert('error', 'Unable to load users. Please try again.');
return null;
}
// Add a user to a board.
function addUserToBoard($boardId, $user) {
if ($user->isAdmin) {
$boards = R::findAll('board'); // DO NOT use getBoards here - it sanitizes the users which causes problems.
foreach($boards as $board) {
$board->sharedUser[] = $user;
R::store($board);
}
} else {
$board = R::load('board', $boardId);
if ($board->id) {
$board->sharedUser[] = $user;
R::store($board);
}
}
}
// Get all boards.
function getBoards() {
$user = getUser();
$boards = R::find('board');
foreach($boards as $board) {
foreach($board->sharedUser as $boardUser) {
sanitize($boardUser);
}
}
$collapsedLanes = R::find('collapsed', ' user_id = ' . $user->id);
foreach($boards as $board) {
foreach($board->xownLane as $lane) {
foreach($collapsedLanes as $collapsed) {
if ($lane->id == $collapsed->lane_id) {
$lane->collapsed = true;
}
}
}
}
if ($user->isAdmin) {
return $boards;
} else {
$filteredBoards = [];
foreach($boards as $board) {
foreach($board->sharedUser as $boardUser) {
if ($user->username == $boardUser->username) {
$filteredBoards[] = $board;
}
}
}
return $filteredBoards;
}
}
// Finds the removed IDs for updating a board.
function getIdsToRemove($boardList, $dataList) {
$retVal = [];
foreach($boardList as $item) {
$remove = true;
foreach($dataList as $newItem) {
if (intval($newItem->id) == $item->id) {
$remove = false;
}
}
if ($remove) {
$retVal[] = $item->id;
}
}
return $retVal;
}
// Load a board bean from provided data.
function loadBoardData($board, $data) {
$board->name = $data->name;
$board->active = true;
$removeIds = getIdsToRemove($board->xownLane, $data->lanes);
foreach($removeIds as $id) {
unset($board->xownLane[$id]);
}
// R::load works like R::dispense if the id is not found.
foreach($data->lanes as $item) {
$lane = R::load('lane', $item->id);
$lane->name = $item->name;
$lane->position = intval($item->position);
if (null == $lane->ownItems) {
$lane->ownItems = [];
}
// New lane, add it to the board
if (!$lane->id) {
$board->xownLane[] = $lane;
}
R::store($lane);
}
$removeIds = getIdsToRemove($board->xownCategory, $data->categories);
foreach($removeIds as $id) {
unset($board->xownCategory[$id]);
}
foreach($data->categories as $item) {
$category = R::load('category', $item->id);
$category->name = $item->name;
// New category, add it to the board.
if (!$category->id) {
$board->xownCategory[] = $category;
}
R::store($category);
}
// Add or remove users as selected.
for($i = 1; $i < count($data->users); $i++) {
$user = R::load('user', $i);
if ($data->users[$i] && $user->id && !in_array($user, $board->sharedUser)) {
$board->sharedUser[] = $user;
} else {
unset($board->sharedUser[$i]);
}
}
// Add all admin users.
foreach(getUsers(false) as $user) {
if ($user->isAdmin && !in_array($user, $board->sharedUser)) {
$board->sharedUser[] = $user;
}
}
R::store($board);
}
// Clean a user bean for return to front-end.
function sanitize($user) {
$user['salt'] = null;
$user->ownToken = [];
$user['password'] = null;
}
// Change username if available.
function updateUsername($user, $data) {
global $jsonResponse;
$nameTaken = R::findOne('user', ' username = ?', [$data->newUsername]);
if (null != $user && null == $nameTaken) {
$user->username = $data->newUsername;
$jsonResponse->addAlert('success', 'Username updated.');
} else {
$jsonResponse->addAlert('error', 'Username already in use.');
}
return $user;
}
// Change email if available.
function updateEmail($user, $data) {
global $jsonResponse;
$emailTaken = R::findOne('user', ' username = ?', [$data->newEmail]);
if (null != $user && null == $emailTaken) {
$user->email = $data->newEmail;
$jsonResponse->addAlert('success', 'Email updated.');
} else {
$jsonResponse->addAlert('error', 'Email already in use.');
}
return $user;
}
// Validate a provided JWT.
function validateToken($requireAdmin = false) {
global $jsonResponse, $app;
$retVal = true;
if ($retVal && $requireAdmin) {
$user = getUser();
if (!$user->isAdmin) {
clearDbToken();
$jsonResponse->message = 'Insufficient user privileges.';
$app->response->setStatus(401);
}
}
return $retVal;
}
// Retrieve user's token from DB and compare to header token.
function checkDbToken() {
$user = getUser();
$isValid = false;
if (null != $user) {
if (isset(getallheaders()['Authorization'])) {
$hash = getallheaders()['Authorization'];
foreach ($user->ownToken as $token) {
if ($hash == $token->token) {
$isValid = true;
}
}
}
}
return $isValid;
}
// Clear a user's token from the DB.
function clearDbToken() {
$payload = null;
try {
$payload = JWT::decode(getallheaders()['Authorization'], getJwtKey());
} catch (Exception $e) {}
if (null != $payload) {
$user = R::load('user', $payload->uid);
if (0 != $user->id) {
$hash = getallheaders()['Authorization'];
foreach ($user->ownToken as $token) {
if ($hash == $token->token) {
R::trash($token);
}
}
R::store($user);
}
}
}
// Get the application's JWT key (created on first run).
function getJwtKey() {
$key = R::load('jwt', 1);
if (!$key->id) {
$key->token = password_hash(strval(time()), PASSWORD_BCRYPT);
R::store($key);
}
return $key->token;
}
// If there are no users, create the admin user.
function createInitialUser() {
if (!R::count('user')) {
$admin = R::dispense('user');
$admin->username = 'admin';
$admin->isAdmin = true;
$admin->logins = 0;
$admin->lastLogin = time(); //date('Y-m-d H:i:s');
$admin->defaultBoard = null;
$admin->salt = password_hash($admin->username . time(), PASSWORD_BCRYPT);
$admin->password = password_hash('admin', PASSWORD_BCRYPT, array('salt' => $admin->salt));
$admin->email = '';
$options = R::dispense('option');
$options->tasksOrder = 0;
$options->showAnimations = true;
$options->showAssignee = true;
$admin->xownOptionList[] = $options;
R::store($admin);
}
}
// Gets the position for a new item in a column.
function getNextItemPosition($columnId) {
$retVal = 0;
$column = R::load('lane', $columnId);
if ($column->id) {
$options = R::exportAll(getUser()->ownOption);
if ($options[0]['tasks_order'] == 1) {
// Tasks at top of column.
renumberItems($columnId, 0, false);
} else {
try {
$retVal = $column->countOwn('item');
} catch (Exception $e) {
// Ignore, just means there are no items.
}
}
}
return $retVal;
}
function renumberItems($columnId, $itemPosition, $isRemoved = true) {
$items = R::find('item', 'lane_id = ' . $columnId);
foreach ($items as $item) {
if ($item->position >= $itemPosition) {
$item->position += $isRemoved ? -1 : 1;
R::store($item);
}
}
}
function runAutoActions(&$item) {
$lane = R::load('lane', $item->laneId);
$board = R::load('board', $lane->boardId);
foreach($board->ownAutoaction as $action) {
switch($action->triggerId) {
case 0: // Item moves to lane
if ($item->laneId == $action->secondaryId) {
updateItemFromAction($item, $action);
}
break;
case 1: // Item assigned to user
if ($item->assignee == $action->secondaryId ||
($action->secondaryId == 0 && $item->assignee == null)) {
updateItemFromAction($item, $action);
}
break;
case 2: // Item assigned to category
if ($item->category == $action->secondaryId ||
($action->secondaryId == 0 && $item->category == null)) {
updateItemFromAction($item, $action);
}
break;
}
}
}
function updateItemFromAction(&$item, $action) {
switch($action->actionId) {
case 0: // Set item color
$item->color = $action->color;
break;
case 1: // Set item category
$item->category = $action->categoryId;
break;
case 2: // Set item assignee
$item->assignee = $action->assigneeId;
break;
case 3: // Clear item due date
$item->dueDate = null;
break;
}
R::store($item);
}

View File

@ -1,361 +0,0 @@
<?php
use RedBeanPHP\R;
// Create new item
$app->post('/boards/:id/items', function($id) use($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
if (validateToken()) {
$board = R::load('board', $id);
if ($board->id) {
$item = R::dispense('item');
$item->title = $data->title;
$item->description = $data->description;
$item->assignee = $data->assignee;
$item->category = $data->category;
$item->color = $data->color;
$item->dueDate = $data->dueDate;
$item->points = $data->points;
$item->position = getNextItemPosition($data->lane);
$board->xownLane[$data->lane]->xownItem[] = $item;
R::store($board);
runAutoActions($item);
if ($item->id) {
$actor = getUser();
logAction($actor->username . ' added item ' . $item->title . ' to board ' . $board->name,
null, $item->export(), $item->id);
$jsonResponse->addAlert('success', 'New board item created.');
$jsonResponse->addBeans(getBoards());
} else {
$jsonResponse->addAlert('error', 'Failed to create board item.');
}
foreach($board->sharedUser as $user) {
$actor = getUser();
$assignee = 'Unassigned';
if ($item->assignee > 0) {
$assignee = getUserByID($item->assignee)->username;
}
$body = getNewItemEmailBody(
$board->id,
$actor->username,
$board->name,
$item->title,
$item->description,
$assignee,
$item->category,
$item->dueDate,
$item->points,
$item->position
);
$subject = 'TaskBoard: New item created!';
$recipient = $user->username;
$email = $user->email;
sendEmail($email, $recipient, $subject, $body);
}
}
}
$app->response->setBody($jsonResponse->asJson());
})->conditions(['id' => '\d+']); // Numbers only.
//Update existing item
$app->post('/items/:itemId', function($itemId) use ($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
if (validateToken()) {
$user = getUser();
$item = R::load('item', $itemId);
$before = $item->export();
if ($item->id) {
$item->title = $data->title;
$item->description = $data->description;
$item->assignee = $data->assignee;
$item->category = $data->category;
$item->color = $data->color;
$item->dueDate = $data->dueDate;
$item->points = $data->points;
if ($data->lane != $item->lane_id) {
$item->position = getNextItemPosition($data->lane);
$item->lane = R::load('lane', $data->lane);
} else {
$item->position = $data->position;
}
runAutoActions($item);
R::store($item);
logAction($user->username . ' updated item ' . $item->title, $before, $item->export(), $itemId);
$jsonResponse->addAlert('success', 'Updated item ' . $item->title . '.');
$jsonResponse->addBeans(getBoards());
$lane = R::load('lane', $item->lane_id);
$board = R::load('board', $lane->boardId);
foreach($board->sharedUser as $user) {
$actor = getUser();
$assignee = 'Unassigned';
if ($item->assignee > 0) {
$assignee = getUserByID($item->assignee)->username;
}
$body = getEditItemEmailBody(
$board->id,
$actor->username,
$board->name,
$item->title,
$item->description,
$assignee,
$item->category,
$item->dueDate,
$item->points,
$item->position
);
$subject = 'TaskBoard: Item edited';
$recipient = $user->username;
$email = $user->email;
sendEmail($email, $recipient, $subject, $body);
}
}
}
$app->response->setBody($jsonResponse->asJson());
})->conditions(['itemId' => '\d+']);
// Update item positions
$app->post('/items/positions', function() use ($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
if (validateToken()) {
$user = getUser();
$movedItem = null;
$beforeItem = null;
$afterItem = null;
R::begin();
foreach($data->positions as $posItem) {
$item = R::load('item', $posItem->item);
$before = $item->export();
$oldLane = $item->lane->id;
$item->lane = R::load('lane', $posItem->lane);
if ($oldLane != $item->lane->id) {
$movedItem = $item;
$beforeItem = $before;
$afterItem = $item->export();
}
$item->position = $posItem->position;
R::store($item);
runAutoActions($item);
R::store($item);
}
R::commit();
// If an item changed lanes, log the action.
if (null != $movedItem) {
logAction($user->username . ' moved item ' . $movedItem->title . ' to lane ' . $movedItem->lane->name,
$beforeItem, $afterItem, $movedItem->id);
}
$jsonResponse->addBeans(getBoards());
}
$app->response->setBody($jsonResponse->asJson());
});
// Add a comment to an item.
$app->post('/items/:itemId/comment', function($itemId) use ($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
if (validateToken()) {
$user = getUser();
$item = R::load('item', $itemId);
if ($item->id) {
$comment = R::dispense('comment');
$comment->text = $data->text;
$comment->userId = $user->id;
$comment->timestamp = time();
$item->ownComment[] = $comment;
R::store($item);
logAction($user->username . ' added a comment to item ' . $item->title, null, $comment->export(), $itemId);
$jsonResponse->addAlert('success', 'Comment added to item ' . $item->title . '.');
$jsonResponse->addBeans(R::load('item', $itemId));
$lane = R::load('lane', $item->lane_id);
$board = R::load('board', $lane->boardId);
foreach($board->sharedUser as $user) {
$body = getNewCommentEmailBody(
$board->id,
$user->username,
$board->name,
$item->title,
$comment->text
);
$subject = 'TaskBoard: New comment';
$recipient = $user->username;
$email = $user->email;
sendEmail($email, $recipient, $subject, $body);
}
}
}
$app->response->setBody($jsonResponse->asJson());
})->conditions(['itemId' => '\d+']);
// Update an existing comment
$app->post('/comments/:commentId', function($commentId) use ($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
if(validateToken()) {
$user = getUser();
$comment = R::load('comment', $commentId);
$before = $comment->export();
$comment->text = $data->text;
$comment->userId = $user->id;
$comment->timestamp = time();
$comment->isEdited = true;
R::store($comment);
logAction($user->username . ' edited comment ' . $comment->id, $before, $comment->export(), $comment->id);
$jsonResponse->addAlert('success', 'Comment edited.');
$jsonResponse->addBeans(R::load('item', $comment->item_id));
$item = R::load('item', $comment->item_id);
$lane = R::load('lane', $item->lane_id);
$board = R::load('board', $lane->boardId);
foreach($board->sharedUser as $user) {
$body = getEditCommentEmailBody(
$board->id,
$user->username,
$board->name,
$item->title,
$comment->text
);
$subject = 'TaskBoard: Comment updated';
$recipient = $user->username;
$email = $user->email;
sendEmail($email, $recipient, $subject, $body);
}
}
$app->response->setBody($jsonResponse->asJson());
})->conditions(['commentId' => '\d+']);
// Remove a comment from an item.
$app->post('/items/:itemId/comment/remove', function($itemId) use ($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
if (validateToken()) {
$user = getUser();
$comment = R::load('comment', $data->id);
if ($comment->id) {
$before = $comment->export();
R::trash($comment);
$item = R::load('item', $itemId);
logAction($user->username . ' removed comment from item ' . $item->title . '.', $before, null, $item->id);
$jsonResponse->addAlert('success', 'Comment was deleted.');
$jsonResponse->addBeans(R::load('item', $itemId));
}
}
$app->response->setBody($jsonResponse->asJson());
})->conditions(['itemId' => '\d+']);
// Add an attachment to an item.
$app->post('/items/:itemId/upload', function($itemId) use ($app, $jsonResponse) {
$upload = $_FILES['file'];
if (!file_exists('uploads/')) {
mkdir('uploads', 0777, true);
}
if (validateToken()) {
$file = R::dispense('attachment');
$item = R::load('item', $itemId);
$before = $item->export();
$user = getUser();
$file->filename = sha1($upload['tmp_name']);
$file->name = $upload['name'];
$file->type = $upload['type'];
$file->userId = $user->id;
$file->timestamp = time();
$item->ownAttachment[] = $file;
R::store($item);
move_uploaded_file($upload['tmp_name'], 'uploads/' . $file->filename);
logAction($user->username . ' uploaded attachment ' . $file->name . ' to item ' . $item->name,
$before, $item, $itemId);
$jsonResponse->addAlert('success', $file->name . ' was added.');
$jsonResponse->addBeans($item);
}
$app->response->setBody($jsonResponse->asJson());
})->conditions(['itemId' => '\d+']);
// Get an item attachment's information.
$app->get('/items/:itemId/upload/:attachmentId', function($itemId, $attachmentId) use ($app, $jsonResponse) {
if (validateToken()) {
$file = R::load('attachment', $attachmentId);
if ($file->id) {
$file->username = 'unknown';
$user = R::load('user', $file->userId);
if ($user->id) {
$file->username = $user->username;
}
$jsonResponse->addBeans($file);
}
}
$app->response->setBody($jsonResponse->asJson());
})->conditions(['itemId' => '\d+', 'attachmentId' => '\d+']);
// Remove an attachment from an item.
$app->post('/items/:itemId/upload/remove', function($itemId) use ($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
if (validateToken()) {
$item = R::load('item', $itemId);
$before = $item->export();
$actor = getUser();
$file = R::load('attachment', $data->fileId);
if ($file->id) {
$filename = $file->name;
$before = $item->export();
unlink('uploads/' . $file->filename);
R::trash($file);
R::store($item);
logAction($actor->username . ' removed attachment ' . $filename . ' from item ' . $item->title,
$before, $item, $itemId);
$jsonResponse->addAlert('success', $filename . ' was deleted.');
$jsonResponse->addBeans($item);
}
}
$app->response->setBody($jsonResponse->asJson());
})->conditions(['itemId' => '\d+']);
// Remove an item.
$app->post('/items/remove', function() use ($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
if (validateToken(true)) {
$item = R::load('item', $data->itemId);
if ($item->id) {
$before = $item->export();
R::trash($item);
renumberItems($item->lane_id, $item->position);
$actor = getUser();
logAction($actor->username . ' removed item ' . $item->title, $before, null, $data->itemId);
$jsonResponse->addAlert('success', $item->title . ' was deleted.');
$jsonResponse->addBeans(getBoards());
}
}
$app->response->setBody($jsonResponse->asJson());
});

View File

@ -1,22 +0,0 @@
<?php
use RedBeanPHP\R;
// Provides default structure and JSON encoded response for the API.
class JsonResponse {
var $message;
var $data;
var $alerts;
function asJson() {
return json_encode($this);
}
function addBeans($beans) {
if (null == $beans) return array();
$this->data = R::exportAll($beans);
}
function addAlert($type, $text) {
$this->alerts[] = ['type' => $type, 'text' => $text];
}
}

View File

@ -1,9 +0,0 @@
<?php
$MAIL_HOST = 'smtp.somehost.com'; // Specify main and backup SMTP servers
$MAIL_USERNAME = 'user@somehost.com'; // SMTP username
$MAIL_PASSWORD = 'secretpassword'; // SMTP password
$MAIL_SMTPSECURE = 'ssl'; // Enable TLS encryption, `ssl` also accepted
$MAIL_PORT = 465; // TCP port to connect to
$MAIL_FROM = 'user@somehost.com';
$MAIL_FROMNAME = 'TaskBoard';

View File

@ -1,124 +0,0 @@
<?php
require_once('mailConfig.php');
function getServerHost() {
$headers = apache_request_headers();
foreach($headers as $header => $value) {
if (strtolower($header) == 'host') {
return $value;
}
}
}
function createMailObject() {
global $MAIL_HOST, $MAIL_USERNAME, $MAIL_PASSWORD, $MAIL_SMTPSECURE, $MAIL_PORT, $MAIL_FROM, $MAIL_FROMNAME;
$mail = new PHPMailer;
$mail->isSMTP();
$mail->Host = $MAIL_HOST;
$mail->SMTPAuth = true;
$mail->Username = $MAIL_USERNAME;
$mail->Password = $MAIL_PASSWORD;
$mail->SMTPSecure = $MAIL_SMTPSECURE;
$mail->Port = $MAIL_PORT;
$mail->From = $MAIL_FROM;
$mail->FromName = $MAIL_FROMNAME;
return $mail;
}
function sendEmail($email, $recipient, $subject, $body) {
$mail = createMailObject();
$mail->addAddress($email, $recipient); // Add a recipient
$mail->isHTML(true);
$mail->Subject = $subject;
$mail->Body = $body;
$mail->send();
}
function getNewBoardEmailBody($boardid, $username, $boardname) {
$message = file_get_contents('mail_templates/newBoard.html');
$message = str_replace('%hostname%', getServerHost(), $message);
$message = str_replace('%boardid%', $boardid, $message);
$message = str_replace('%username%', $username, $message);
$message = str_replace('%boardname%', $boardname, $message);
return $message;
}
function getEditBoardEmailBody($boardid, $username, $boardname) {
$message = file_get_contents('mail_templates/editBoard.html');
$message = str_replace('%hostname%', getServerHost(), $message);
$message = str_replace('%boardid%', $boardid, $message);
$message = str_replace('%username%', $username, $message);
$message = str_replace('%boardname%', $boardname, $message);
return $message;
}
function getNewItemEmailBody($boardid, $username, $boardname, $title, $description, $assignee, $category, $dueDate, $points, $position)
{
$message = file_get_contents('mail_templates/newItem.html');
$message = str_replace('%hostname%', getServerHost(), $message);
$message = str_replace('%boardid%', $boardid, $message);
$message = str_replace('%username%', $username, $message);
$message = str_replace('%boardname%', $boardname, $message);
$message = str_replace('%title%', $title, $message);
$message = str_replace('%description%', $description, $message);
$message = str_replace('%assignee%', $assignee, $message);
$message = str_replace('%category%', $category, $message);
$message = str_replace('%duedate%', $dueDate, $message);
$message = str_replace('%points%', $points, $message);
$message = str_replace('%position%', $position, $message);
return $message;
}
function getEditItemEmailBody($boardid, $username, $boardname, $title, $description, $assignee, $category, $dueDate, $points, $position)
{
$message = file_get_contents('mail_templates/editItem.html');
$message = str_replace('%hostname%', getServerHost(), $message);
$message = str_replace('%boardid%', $boardid, $message);
$message = str_replace('%username%', $username, $message);
$message = str_replace('%boardname%', $boardname, $message);
$message = str_replace('%title%', $title, $message);
$message = str_replace('%description%', $description, $message);
$message = str_replace('%assignee%', $assignee, $message);
$message = str_replace('%category%', $category, $message);
$message = str_replace('%duedate%', $dueDate, $message);
$message = str_replace('%points%', $points, $message);
$message = str_replace('%position%', $position, $message);
return $message;
}
function getNewCommentEmailBody($boardid, $username, $boardname, $title, $comment)
{
$message = file_get_contents('mail_templates/newComment.html');
$message = str_replace('%hostname%', getServerHost(), $message);
$message = str_replace('%boardid%', $boardid, $message);
$message = str_replace('%username%', $username, $message);
$message = str_replace('%boardname%', $boardname, $message);
$message = str_replace('%title%', $title, $message);
$message = str_replace('%comment%', $comment, $message);
return $message;
}
function getEditCommentEmailBody($boardid, $username, $boardname, $title, $comment)
{
$message = file_get_contents('mail_templates/editComment.html');
$message = str_replace('%hostname%', getServerHost(), $message);
$message = str_replace('%boardid%', $boardid, $message);
$message = str_replace('%username%', $username, $message);
$message = str_replace('%boardname%', $boardname, $message);
$message = str_replace('%title%', $title, $message);
$message = str_replace('%comment%', $comment, $message);
return $message;
}

View File

@ -1,7 +0,0 @@
<!DOCTYPE html>
<html>
<body>
%username% updated board %boardname%!
<a href="http://%hostname%/#/boards/%boardid%">Navigate to board!</a>
</body>
</html>

View File

@ -1,10 +0,0 @@
<!DOCTYPE html>
<html>
<body>
%username% edited comment at board %boardname%:<br>
%title%<br>
%comment%<br>
<br>
<a href="http://%hostname%/#/boards/%boardid%">Navigate to board!</a>
</body>
</html>

View File

@ -1,15 +0,0 @@
<!DOCTYPE html>
<html>
<body>
%username% edited item at board %boardname%:<br>
%title%<br>
%description%<br>
%duedate%<br>
%assignee%<br>
%category%<br>
%points%<br>
%position%<br>
<a href="http://%hostname%/#/boards/%boardid%">Navigate to board!</a>
</body>
</html>

View File

@ -1,7 +0,0 @@
<!DOCTYPE html>
<html>
<body>
%username% created new board %boardname%!<br>
<a href="http://%hostname%/#/boards/%boardid%">Navigate to board!</a>
</body>
</html>

View File

@ -1,10 +0,0 @@
<!DOCTYPE html>
<html>
<body>
%username% created new comment at board %boardname%:<br>
%title%<br>
%comment%<br>
<br>
<a href="http://%hostname%/#/boards/%boardid%">Navigate to board!</a>
</body>
</html>

View File

@ -1,15 +0,0 @@
<!DOCTYPE html>
<html>
<body>
%username% created new item at board %boardname%:<br>
%title%<br>
%description%<br>
%duedate%<br>
%assignee%<br>
%category%<br>
%points%<br>
%position%<br>
<a href="http://%hostname%/#/boards/%boardid%">Navigate to board!</a>
</body>
</html>

View File

@ -1,280 +0,0 @@
<?php
use RedBeanPHP\R;
// Validate a user and store token (and return in response).
$app->post('/login', function() use ($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
$expires = ($data->rememberme)
? (2 * 7 * 24 * 60 * 60) /* Two weeks */
: (1.5 * 60 * 60) /* One and a half hours */;
$lookup = R::findOne('user', ' username = ? ', [$data->username]);
$jsonResponse->message = 'Invalid username or password.';
$app->response->setStatus(401);
if (null != $lookup) {
$hash = password_hash($data->password, PASSWORD_BCRYPT, array('salt' => $lookup->salt));
if ($lookup->password == $hash) {
if ($lookup->logins == 0 && $lookup->username == 'admin') {
$jsonResponse->addAlert('warning', "This is your first login, don't forget to change your password.");
$jsonResponse->addAlert('success', 'Go to Settings to add your first board.');
}
setUserToken($lookup, $expires);
$lookup->logins = $lookup->logins + 1;
$lookup->lastLogin = time();
//R::store($lookup);
logAction($lookup->username . ' logged in.', null, null);
$jsonResponse->message = 'Login successful.';
$jsonResponse->data = R::findOne('token', ' user_id = ? ORDER BY id DESC ', [$lookup->id])->token;
$app->response->setStatus(200);
}
}
$app->response->setBody($jsonResponse->asJson());
});
// Log out a user by clearing tokens.
$app->get('/logout', function() use ($app, $jsonResponse) {
if (validateToken()) {
clearDbToken();
$jsonResponse->message = 'Logout complete.';
$actor = getUser();
logAction($actor->username . ' logged out.', null, null);
}
$app->response->setStatus(200); // Doesn't matter if the token was no good.
$app->response->setBody($jsonResponse->asJson());
});
// Update current user's password.
$app->post('/updatepassword', function() use($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
if (validateToken()) {
$user = getUser();
if (null != $user) {
$checkPass = password_hash($data->currentPass, PASSWORD_BCRYPT, array('salt' => $user->salt));
if ($user->password == $checkPass) {
$before = $user->export();
$user->password = password_hash($data->newPass, PASSWORD_BCRYPT, array('salt' => $user->salt));
//R::store($user);
logAction($user->username . ' changed their password.', $before, $user->export());
$jsonResponse->addAlert('success', 'Password changed.');
} else {
$jsonResponse->addAlert('error', 'Invalid current password. Password not changed.');
}
}
}
$app->response->setBody($jsonResponse->asJson());
});
// Update current user's username if not taken.
$app->post('/updateusername', function() use($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
if (validateToken()) {
$user = getUser();
$before = $user->export();
$user = updateUsername($user, $data);
R::store($user);
if ($jsonResponse->alerts[0]['type'] == 'success') {
logAction($before['username'] . ' changed username to ' . $user->username, $before, $user->export());
}
$jsonResponse->addBeans(getUsers());
}
$app->response->setBody($jsonResponse->asJson());
});
// Update current user's email if not taken.
$app->post('/updateemail', function() use($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
if (validateToken()) {
$user = getUser();
$before = $user->export();
$user = updateEmail($user, $data);
R::store($user);
if ($jsonResponse->alerts[0]['type'] == 'success') {
logAction($before['username'] . ' changed email to ' . $user->email, $before, $user->export());
}
$jsonResponse->addBeans(getUsers());
}
$app->response->setBody($jsonResponse->asJson());
});
// Update current user's default board.
$app->post('/updateboard', function() use($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
if (validateToken()) {
$user = getUser();
$before = $user->export();
$user->defaultBoard = $data->defaultBoard;
R::store($user);
logAction($user->username . ' changed their default board.', $before, $user->export());
$jsonResponse->addBeans(getUsers());
}
$app->response->setBody($jsonResponse->asJson());
});
// Get all user actions
$app->get('/actions', function() use($app, $jsonResponse) {
if (validateToken(true)) {
$actions = R::findAll('activity', ' order by timestamp desc ');
$jsonResponse->addBeans($actions);
}
$app->response->setBody($jsonResponse->asJson());
});
// Get current user
$app->get('/users/current', function() use($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
if (validateToken()) {
$user = getUser();
if (null != $user) {
$userOptions = R::exportAll($user->xownOptionList);
$options = [
'tasksOrder' => $userOptions[0]['tasks_order'],
'showAssignee' => $userOptions[0]['show_assignee'] == 1,
'showAnimations' => $userOptions[0]['show_animations'] == 1
];
$jsonResponse->data = [
'userId' => $user->id,
'username' => $user->username,
'isAdmin' => $user->isAdmin,
'email' => $user->email,
'defaultBoard' => $user->defaultBoard,
'options' => $options
];
}
}
$app->response->setBody($jsonResponse->asJson());
});
$app->post('/users/current/options', function() use ($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
if (validateToken()) {
$user = getUser();
$user->xownOptionList[0]->tasksOrder = $data->tasksOrder;
$user->xownOptionList[0]->showAssignee = $data->showAssignee;
$user->xownOptionList[0]->showAnimations = $data->showAnimations;
R::store($user);
$jsonResponse->data = $data;
}
$app->response->setBody($jsonResponse->asJson());
});
// Get all users
$app->get('/users', function() use($app, $jsonResponse) {
if (validateToken()) {
$jsonResponse->addBeans(getUsers());
}
$app->response->setBody($jsonResponse->asJson());
});
// Create a new user
$app->post('/users', function() use($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
if (validateToken(true)) {
$nameTaken = R::findOne('user', ' username = ?', [$data->username]);
if (null != $nameTaken) {
$jsonResponse->addAlert('error', 'Username already in use.');
} else {
$user = R::dispense('user');
$user->username = $data->username;
$user->isAdmin = $data->isAdmin;
$user->email = $data->email;
$user->defaultBoard = $data->defaultBoard;
$user->salt = password_hash($data->username . time(), PASSWORD_BCRYPT);
$user->password = password_hash($data->password, PASSWORD_BCRYPT, array('salt' => $user->salt));
$options = R::dispense('option');
$options->tasksOrder = 0; // Bottom of column (1 == top of column)
$options->showAnimations = true;
$options->showAssignee = true;
$user->xownOptionList[] = $options;
R::store($user);
addUserToBoard($data->defaultBoard, $user);
if (property_exists($data, 'boardAccess') && is_array($data->boardAccess)) {
foreach($data->boardAccess as $board) {
addUserToBoard($board, $user);
}
}
$actor = getUser();
logAction($actor->username . ' added user ' . $user->username, null, $user->export());
$jsonResponse->addAlert('success', 'New user ' . $user->username . ' created.');
}
$jsonResponse->addBeans(getUsers());
$jsonResponse->boards = R::exportAll(getBoards());
}
$app->response->setBody($jsonResponse->asJson());
});
// Update a user
$app->post('/users/update', function() use($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
if (validateToken(true)) {
$user = R::load('user', $data->userId);
$actor = getUser();
if ($user->id && $actor->isAdmin) {
$before = $user->export();
if ($data->newUsername != $user->username) {
$user = updateUsername($user, $data);
}
if ($data->password != '' && $data->password != null) {
$user->password = password_hash($data->password, PASSWORD_BCRYPT, array('salt' => $user->salt));
}
$user->isAdmin = $data->isAdmin;
$user->email = $data->email;
$user->defaultBoard = $data->defaultBoard;
R::store($user);
addUserToBoard($data->defaultBoard, $user);
foreach($data->boardAccess as $board) {
addUserToBoard($board, $user);
}
logAction($actor->username . ' updated user ' . $user->username, $before, $user->export());
$jsonResponse->addAlert('success', 'User updated.');
}
$jsonResponse->addBeans(getUsers());
$jsonResponse->boards = R::exportAll(getBoards());
}
$app->response->setBody($jsonResponse->asJson());
});
// Remove a user.
$app->post('/users/remove', function() use($app, $jsonResponse) {
$data = json_decode($app->environment['slim.input']);
if (validateToken(true)) {
$user = R::load('user', $data->userId);
$actor = getUser();
if ($user->id == $data->userId && $actor->isAdmin) {
$before = $user->export();
R::trash($user);
R::exec('DELETE from board_user WHERE user_id = ?', [$data->userId]);
logAction($actor->username . ' removed user ' . $before['username'], $before, null);
$jsonResponse->addAlert('success', 'Removed user ' . $user->username . '.');
}
$jsonResponse->addBeans(getUsers());
$jsonResponse->boards = R::exportAll(getBoards());
}
$app->response->setBody($jsonResponse->asJson());
});

View File

@ -1,22 +0,0 @@
# Dockerfile for Taskboard with nginx and sqlite.
FROM ubuntu:trusty
MAINTAINER Alex van den Hoogen <alex.van.den.hoogen@geodan.nl>
RUN apt-get update && \
apt-get install -yq --no-install-recommends git wget nginx php5-fpm php5-sqlite sqlite3 ca-certificates pwgen && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN echo "cgi.fix_pathinfo = 0;" >> /etc/php5/fpm/php.ini && \
echo "daemon off;" >> /etc/nginx/nginx.conf && \
mkdir -p /var/www
RUN git clone https://github.com/kiswa/TaskBoard.git /var/www && \
chmod 777 $(find /var/www -type d)
ADD nginx.conf /etc/nginx/sites-available/default
EXPOSE 80
CMD service php5-fpm start && nginx

View File

@ -1,42 +0,0 @@
#!/bin/bash
set -e
echo Building...
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd $DIR
echo ' Installing dependencies'
cd ..
./build/composer.phar install
cd build
echo ' Compiling lib JS files...'
./minify-libs
echo ' Minifying lib CSS files...'
cd ../lib/css/
cat bootstrap.min.css > combined.css
cat font-awesome.min.css >> combined.css
cat jquery-ui.min.css >> combined.css
cat spectrum.css >> combined.css
curl -X POST -s --data-urlencode 'input@combined.css' http://cssminifier.com/raw > combined.min.css
rm combined.css
cd ../../build/
echo ' Compiling app JS files...'
./minify-app
echo ' Minifying app CSS files...'
cd ../css/
curl -X POST -s --data-urlencode 'input@styles.css' http://cssminifier.com/raw > styles.min.css
cd ../build/
echo ' Updating index.html...'
cd ../
if [ -f indexProd.html ]; then
mv index.html indexDev.html
mv indexProd.html index.html
fi
cd build/
echo Build Complete

View File

@ -1,22 +0,0 @@
#!/bin/bash
echo Cleaning...
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd $DIR
echo ' Cleaning CSS files...'
rm -f ../lib/css/combined.min.css
rm -f ../css/styles.min.css
echo ' Cleaning JS files...'
rm -f ../js/app.min.js
rm -f ../lib/libs.min.js
echo ' Restoring index.html...'
cd ../
if [ -f indexDev.html ]; then
mv index.html indexProd.html
mv indexDev.html index.html
fi
cd build/
echo Clean Complete

Binary file not shown.

Binary file not shown.

View File

@ -1,3 +0,0 @@
#!/bin/bash
cd ../js/
java -jar ../build/compiler.jar -W QUIET --js app.js controllers/*.js services/*.js directives/*.js --js_output_file app.min.js

View File

@ -1,3 +0,0 @@
#!/bin/bash
cd ../lib/
java -jar ../build/compiler.jar -W QUIET --js jquery-1.11.3.min.js jquery-ui.min.js jquery.noty.min.js bootstrap.min.js angular.min.js angular-route.min.js angular-sanitize.min.js ng-context-menu.min.js marked.min.js spectrum.js prefixfree.min.js --js_output_file libs.min.js

View File

@ -1,34 +0,0 @@
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
root /var/www;
index index.php index.html;
# Make site accessible from http://localhost/
server_name localhost;
location / {
try_files $uri $uri/ /index.php?$args;
}
location /api {
if (!-e $request_filename) {
rewrite ^(.*)$ /api/api.php last; break;
}
}
location /api/taskboard.db {
rewrite ^(.*)$ /api/api.php last; break;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
# NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;
include fastcgi_params;
}
}

View File

@ -1,17 +0,0 @@
{
"name": "kiswa/taskboard",
"require": {
"slim/slim": "=2.6.1",
"firebase/php-jwt": "=1.0.0",
"ircmaxell/password-compat": "~1.0.3",
"gabordemooij/redbean": "=4.2.0",
"phpmailer/phpmailer": "~5.2.9"
},
"license": "MIT",
"authors": [
{
"name": "Matthew Ross"
}
],
"minimum-stability": "stable"
}

253
composer.lock generated
View File

@ -1,253 +0,0 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "bcd0d77c65d6fda9ece3a2600d80347c",
"content-hash": "99f3562842898771c354cfc411491902",
"packages": [
{
"name": "firebase/php-jwt",
"version": "1.0.0",
"target-dir": "Firebase/PHP-JWT",
"source": {
"type": "git",
"url": "https://github.com/firebase/php-jwt.git",
"reference": "58c1e61d0dff797f2989c54415229f4016d57a6d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/58c1e61d0dff797f2989c54415229f4016d57a6d",
"reference": "58c1e61d0dff797f2989c54415229f4016d57a6d",
"shasum": ""
},
"require": {
"php": ">=5.2.0"
},
"type": "library",
"autoload": {
"classmap": [
"Authentication/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Neuman Vong",
"email": "neuman+pear@twilio.com",
"role": "Developer"
},
{
"name": "Anant Narayanan",
"email": "anant@php.net",
"role": "Developer"
}
],
"description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
"homepage": "https://github.com/firebase/php-jwt",
"time": "2014-08-28 17:14:17"
},
{
"name": "gabordemooij/redbean",
"version": "v4.2.0",
"source": {
"type": "git",
"url": "https://github.com/gabordemooij/redbean.git",
"reference": "840a2e0d856042e75a701453f37896c597c6fbc2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/gabordemooij/redbean/zipball/840a2e0d856042e75a701453f37896c597c6fbc2",
"reference": "840a2e0d856042e75a701453f37896c597c6fbc2",
"shasum": ""
},
"require": {
"php": ">=5.3.4"
},
"type": "library",
"autoload": {
"psr-4": {
"RedBeanPHP\\": "RedBeanPHP"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"New BSD and GPLv2"
],
"authors": [
{
"name": "Gabor de Mooij",
"email": "gabor@redbeanphp.com",
"homepage": "http://redbeanphp.com"
}
],
"description": "RedBeanPHP ORM",
"homepage": "http://redbeanphp.com/",
"keywords": [
"orm"
],
"time": "2015-04-01 19:03:42"
},
{
"name": "ircmaxell/password-compat",
"version": "v1.0.4",
"source": {
"type": "git",
"url": "https://github.com/ircmaxell/password_compat.git",
"reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ircmaxell/password_compat/zipball/5c5cde8822a69545767f7c7f3058cb15ff84614c",
"reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c",
"shasum": ""
},
"require-dev": {
"phpunit/phpunit": "4.*"
},
"type": "library",
"autoload": {
"files": [
"lib/password.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Anthony Ferrara",
"email": "ircmaxell@php.net",
"homepage": "http://blog.ircmaxell.com"
}
],
"description": "A compatibility library for the proposed simplified password hashing algorithm: https://wiki.php.net/rfc/password_hash",
"homepage": "https://github.com/ircmaxell/password_compat",
"keywords": [
"hashing",
"password"
],
"time": "2014-11-20 16:49:30"
},
{
"name": "phpmailer/phpmailer",
"version": "v5.2.14",
"source": {
"type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "e774bc9152de85547336e22b8926189e582ece95"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/e774bc9152de85547336e22b8926189e582ece95",
"reference": "e774bc9152de85547336e22b8926189e582ece95",
"shasum": ""
},
"require": {
"php": ">=5.0.0"
},
"require-dev": {
"phpdocumentor/phpdocumentor": "*",
"phpunit/phpunit": "4.7.*"
},
"suggest": {
"league/oauth2-client": "Needed for XOAUTH2 authentication",
"league/oauth2-google": "Needed for Gmail XOAUTH2"
},
"type": "library",
"autoload": {
"classmap": [
"class.phpmailer.php",
"class.phpmaileroauth.php",
"class.phpmaileroauthgoogle.php",
"class.smtp.php",
"class.pop3.php",
"extras/EasyPeasyICS.php",
"extras/ntlm_sasl_client.php"
]
},
"notification-url": "http://packagist.org/downloads/",
"license": [
"LGPL-2.1"
],
"authors": [
{
"name": "Jim Jagielski",
"email": "jimjag@gmail.com"
},
{
"name": "Marcus Bointon",
"email": "phpmailer@synchromedia.co.uk"
},
{
"name": "Andy Prevost",
"email": "codeworxtech@users.sourceforge.net"
},
{
"name": "Brent R. Matzelle"
}
],
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"time": "2015-11-01 10:15:28"
},
{
"name": "slim/slim",
"version": "2.6.1",
"source": {
"type": "git",
"url": "https://github.com/slimphp/Slim.git",
"reference": "365dbfa0c02a31e76888eaec693dacd9dca1c82a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slimphp/Slim/zipball/365dbfa0c02a31e76888eaec693dacd9dca1c82a",
"reference": "365dbfa0c02a31e76888eaec693dacd9dca1c82a",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"suggest": {
"ext-mcrypt": "Required for HTTP cookie encryption"
},
"type": "library",
"autoload": {
"psr-0": {
"Slim": "."
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Josh Lockhart",
"email": "info@joshlockhart.com",
"homepage": "http://www.joshlockhart.com/"
}
],
"description": "Slim Framework, a PHP micro framework",
"homepage": "http://github.com/codeguy/Slim",
"keywords": [
"microframework",
"rest",
"router"
],
"time": "2015-03-02 19:09:48"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": []
}

View File

@ -1,745 +0,0 @@
/*
Contents:
#General - Styles that apply throughout.
#Login - Login page specific styles.
#PageTop - Styles for the top bar across pages (includes board navigation).
#SiteNav - Site navigation section styles.
#BoardLayout - Styles specific to the board layout.
#ViewItem - Styles for item view modal.
#SettingsPage - Styles specific to the settings page.
#FilesPage - Styles for the file viewer page.
#3rdParty - Styles for 3rd party widgets (noty, calendar, etc.)
*/
/* #General */
html, body {
height: 100%;width: 100%;
}
body {
background: url(../images/bg.png);
font-family: "Pontano Sans", sans-serif;
font-size: 12pt;
}
th, h1, h2, h3, h4, h5, h6, #board-nav a {
font-family: "Raleway";
font-weight: 500;
}
th {
font-size: 91%;
}
a, button {
transition: background-color 0.3s, color 0.3s;
}
a:hover.fa {
text-decoration: none;
}
textarea {
resize: vertical;
}
.green {
color: green;
}
.red {
color: darkred;
}
.dateNear {
text-shadow: 0px 0px 3px rgba(255, 163, 84, 1);
}
.datePast {
text-shadow: 0px 0px 3px rgba(255, 81, 28, 1);
}
.unbold {
font-weight: normal;
}
.fa-edit, .fa-trash-o {
cursor: pointer;
}
.pull-up {
margin-top: -10px;
}
#wrapper {
display: table;
table-layout: fixed;
border-spacing: 5px;
width: 100%;
height: 100%;
overflow-x: scroll;
margin: 0;
padding: 0;
}
/* #Login */
.form-signin {
max-width: 350px;
padding: 15px;
margin: 0 auto;
}
.form-signin .form-signin-heading,
.form-signin .checkbox {
margin-bottom: 10px;
font-weight: normal;
text-align: center;
}
.form-signin .form-signin-heading {
margin-top: .5em;
}
.form-signin .form-control {
position: relative;
height: auto;
box-sizing: border-box;
padding: 10px;
font-size: 16px;
}
.form-signin .form-control:focus {
z-index: 2;
}
.form-signin input[type="text"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
text-align: center;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
text-align: center;
}
.login-container {
background: #fff;
border: 1px solid #ccc;
width: 350px;
height: 275px;
margin: 2em auto;
position: relative;
}
.login-error {
text-align: center;
font-weight: bold;
margin-bottom: -2.1em;
}
.login-container::before, .login-container::after {
content: "";
width: 100%; height: 100%;
position: absolute;
top: 0; left: 0;
background: #ededed;
border: 1px solid #ccc;
z-index: -1;
transform: rotateZ(4deg);
}
.login-container::after {
top: 4px;
z-index: -2;
background: #efefef;
transform: rotateZ(-4deg);
}
/* #PageTop */
#header {
display: table-row;
margin: 0;
}
#header h2 {
border-bottom: 1px solid rgba(51,51,51,.2);
margin-top: 5px;
padding-bottom: 10px;
}
#board-nav {
margin-top: -10px;
overflow: hidden;
}
#board-nav p {
margin-bottom: 2px;
margin-left: 3px;
}
#board-nav .form-control {
height: auto;
padding: 3px 6px;
margin-right: 7px;
}
#board-nav :last-child.form-control {
margin-right: 0;
}
.version {
margin-top: 5px;
font-size: small !important;
}
/* #SiteNav*/
.nav {
margin-top: -.7em;
padding: 0;
}
.nav>li.active>a {
cursor: default;
}
.nav>li>a {
border: 1px solid #ccc;
background-color: #eee;
}
.nav>li>a:hover,.nav>li>a:focus {
background-color: #fff;
}
.content {
display: table-row;
height: 100%;
}
/* #BoardLayout */
#board {
overflow-x: auto;
width: 100%;
height: 100%;
}
#boardColumns {
display: table;
width: 100%;
height: 100%;
}
.boardColumn {
display: table-cell;
overflow: auto;
min-width: 260px;
max-width: 300px;
height: 100%;
margin: 5px;
border: 1px solid rgba(51,51,51,.3);
box-shadow: 0px 0px 3px 0px rgba(51,51,51,.2);
border-radius: 3px;
background-color: #fff;
}
.boardColumn > h3 {
margin: 0;
padding: 5px;
background-color: #e0edf2;
border-bottom: 1px solid rgba(51,51,51,.05);
}
.itemContainer {
height: 94%;
overflow: auto;
}
.default-cursor {
cursor: default;
}
.pointer-cursor {
cursor: pointer;
}
.filtered {
opacity: 0.4;
}
.boardColumn.collapsed {
min-width: 0;
width: 1.9em;
padding: 0;
margin: 0;
background-color: #e0edf2;
}
.boardColumn.collapsed > h3 {
border: none;
width: 1.5em;
white-space: nowrap;
transform: rotate(90deg) translateX(-30px);
transform-origin: left bottom;
}
.shrink {
float: right;
margin-top: 5px;
cursor: pointer;
}
.expand {
display: none;
cursor: pointer;
transform: translateY(-3px) translateX(-10px);
}
.boardColumn.collapsed .shrink {
display: none;
}
.boardColumn.collapsed .expand {
display: inline-block;
margin-left: 1em;
}
.boardColumn.collapsed .itemContainer {
display: none;
}
.boardColumn.collapsed .badge {
transform: rotate(-90deg) translateX(4px);
}
.boardItem {
background: lightyellow;
padding: 3px;
margin: 5px;
border: 1px solid rgba(51,51,51,.1);
border-radius: 3px;
box-shadow: 1px 1px 2px -1px rgba(51,51,51,.2);
}
.itemHeader {
position: relative;
}
.itemHeader h4 {
border-bottom: 1px dotted rgba(51,51,51,.3);
cursor: move;
padding-bottom: 3px;
margin: 0;
padding-right: 25px;
}
.itemHeader .badge {
position: absolute;
right: 0;
top: 0;
margin: 0;
font-family: "Pontano Sans";
border-radius: 7px;
cursor: default;
}
.boardItem .category {
font-weight: 700;
text-align: right;
display: inline-block;
width: 100%;
padding-right: 3px;
}
.boardItem .description {
margin: 3px;
max-height: 12em;
overflow: auto;
}
.boardItem .assignee {
font-size: .8em;
font-family: Raleway;
}
.boardItem p:last-child {
margin-bottom: 0;
}
.addItem {
margin: 5px;
text-align: right;
}
.draggable-placeholder {
height: 80px;
box-shadow: inset 0px 0px 10px 0px rgba(51,51,51,.25);
border-radius: 5px;
margin: 5px;
}
span.fa {
margin-right: 5px;
}
.lane-placeholder {
width: 100%;
height: 1.2em;
box-shadow: inset 0px 0px 10px 0px rgba(0,0,0,.25);
list-style: none;
}
.dropdown.fixed {
position: fixed;
}
.contextItemWrap {
margin: -5px 10px 0 19px;
display: block;
}
/* #ViewItem */
.itemViewModal .modal-dialog {
width: 700px;
}
.itemViewModal .modal-content, .view-item-details {
overflow: hidden;
}
.itemViewModal h4 {
padding-top: 1em;
}
.due-date {
font-weight: bold;
font-size: 1.1em;
margin: .9em;
}
.view-item-details {
padding-bottom: .5em;
font-size: 91%;
border: 1px solid rgba(51,51,51,.1);
border-radius: 5px;
}
.view-item-details .description {
margin: 1em;
max-height: 500px;
overflow: auto;
}
.view-item-actions {
overflow: hidden;
padding: .5em 0;
}
.view-item-actions a {
display: block;
}
.alternate {
background-color: rgba(51,51,51,.04);
}
.view-item-attachments .list-group {
margin-bottom: 0;
}
.view-item-comment-form {
overflow: hidden;
}
.edit-item-comment-form {
overflow: hidden;
}
.edit-item-comment-form button {
margin-left: 5px;
margin-top: 5px;
}
.detail {
display: block;
font-size: 80%;
margin-left: 1em;
}
p.detail {
margin-top: -10px;
margin-bottom: 0;
}
.links {
margin-top: -2em;
}
.links a {
margin-left: 3px;
}
.commentText {
display: inline-block;
width: 95%;
overflow: hidden;
}
.commentActions {
position: absolute;
top: .8em;
right: .4em;
}
.commentActions a {
margin-left: 3px;
}
#add-comment {
margin-top: 5px;
}
.sidebar {
position: fixed;
top: 30px;
left: 0;
width: 240px;
height: 85%;
background: #fff;
border-radius: 0;
border-top-right-radius: 6px;
border-bottom-right-radius: 6px;
border: 1px solid rgba(0,0,0,0.2);
box-shadow: 0 5px 15px rgba(0,0,0,0.5);
transition: all 0.3s ease;
}
.sidebar .modal-header {
position: fixed;
width: 238px;
}
.sidebar .sidebar-content {
margin-top: 2.55em;
overflow: auto;
height: 93.6%;
}
.sidebar.toggle {
left:-240px;
}
.sidebar .fa {
cursor: pointer;
}
.sidebar .showtab {
border-radius: 0;
border-top-right-radius: 6px;
border-bottom-right-radius: 6px;
border: 1px solid rgba(0,0,0,0.2);
box-shadow: 0 5px 15px rgba(0,0,0,0.5);
position: fixed;
top: 30px;
left: 0;
width: auto;
}
.showtab.toggle {
display:none;
}
.sidebar .action {
margin: 5px;
}
/* #SettingsPage */
.form-control.wide {
width: 85%;
margin-bottom: 5px;
}
.short-top {
margin-top: -1em;
}
label.wide {
font-weight: normal;
width: 100%;
}
.fa-arrows-v {
cursor: move;
padding: 0 6px 0 5px;
margin: 0 3px 0 0;
border-right: 1px solid #ccc;
}
#widget-container {
display: table-cell;
}
.min-padding { padding: 5px; }
.settings-widget {
border: 1px solid rgba(51,51,51,.2);
border-radius: 5px;
background-color: #fff;
box-shadow: 0px 0px 3px 0px rgba(51,51,51,.2);
}
.widget-header, .modal-header {
margin: 0 0 5px 0;
padding: 7px;
border-radius: 5px 5px 0 0;
background-color: #e0edf2;
border-bottom: 1px solid rgba(51,51,51,.05);
}
.widget-content {
margin: 5px;
cursor: auto;
}
.widget-content h4 {
clear: both;
}
.widget-content .list-group-item {
padding: 3px 7px;
background: transparent;
min-width: 125px;
}
.list-group-item.small {
padding: 3px;
}
.list-group.editable > .list-group-item {
overflow: hidden;
}
td > .list-group {
margin: 0;
padding: 0;
}
fieldset > .list-group {
margin-bottom: 10px;
}
span.filter {
margin-left: 5px;
}
.board-status {
font-weight: normal;
display: inline-block;
min-width: 70px;
}
#change-password, #default-board, #change-username, #change-email {
width: 48%;
float: left;
margin-bottom: 1em;
}
#change-username, #change-email {
margin-left: 4%;
margin-bottom: .6em;
}
.board-options p {
width: 48%;
float: left;
margin-bottom: 1em;
}
.board-options label {
font-weight: normal;
}
#changeUserSettings hr, .board-options hr {
clear: both;
}
.badge { margin: 0 3px; }
.half-width {
width: 50%;
float: left;
padding-right: 10px;
margin-right: 0;
}
.half-width > ul {
margin-right: 1em;
}
.list-group-item > a {
margin-right: 5px;
margin-top: 3px;
vertical-align: top;
}
input.custom-inline {
height: auto;
padding: 0 6px;
margin-top: -3px;
}
/* Activity Log slide out */
.slide-menu {
position: fixed;
width: 240px;
height: 100%;
top: 90px;
z-index: 10000;
right: -240px;
transition: all 0.3s ease;
}
.slide-menu .fa-times {
cursor: pointer;
}
.slide-menu-open {
right: 0;
}
.slide-menu > .settings-widget {
height: 85%;
}
.slide-menu .widget-header {
position: fixed;
width: 240px;
}
.slide-menu .widget-content {
margin-top: 3em;
height: 92%;
overflow: auto;
}
.action {
border: 1px solid rgba(51,51,51,.2);
border-radius: 5px;
box-shadow: inset 0px 0px 5px 0px rgba(51,51,51,.2);
background-color: rgba(51,51,51,.005);
padding: 5px;
margin: 0;
margin-bottom: 5px;
}
.action .timestamp {
margin-bottom: -5px;
font-size: .8em;
}
/* #FilesPage */
.files-wrapper {
height: 100%;
}
.files-widget {
border: 1px solid rgba(51,51,51,.2);
border-radius: 5px;
background-color: #fff;
box-shadow: 0px 0px 3px 0px rgba(51,51,51,.2);
height: 100%;
}
.files-widget .widget-content {
height: 90%;
}
.files-widget .small.pull-right {
margin-top: .3em;
}
#iframe-wrapper {
width: 100%;
height: 100%;
}
#iframe-wrapper iframe {
display: block;
border: 1px solid rgba(51,51,51,.3);
width: 100%;
height: 100%;
}
/* #3rdParty */
/* Bootstrap fix */
.table-responsive {
overflow-x: inherit;
}
/* noty notifications */
.noty_bar {
color: #fff;
font-weight: bold;
text-shadow: 0 0 2px #333;
}
.noty_type_error {
background-color: #d9534f;
border-color: #d43f3a;
}
.noty_type_warning {
background-color: #f0ad4e;
border-color: #eea236;
}
.noty_type_information {
background-color: #5bc0de;
border-color: #46b8da;
}
.noty_type_success {
background-color: #5cb85c;
border-color: #4cae4c;
}
.noty_type_notification {
background-color: #428bca;
border-color: #357ebd;
}
/* spectrum color picker */
.sp-replacer {
display: block;
height: 34px;
padding: 6px 12px;
background-color: #fff;
border: solid 1px #ccc;
border-radius: 4px;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
}
.sp-dd {
display: none;
}
.sp-container {
width: 202px;
}
.sp-disabled {
background-color: #eee;
}
.sp-preview {
width: 100%;
}
.sp-replacer:hover, .sp-replacer.sp-active {
border-color: rgba(51,51,51,.3);
}
.sp-container {
border: 1px solid #ccc;
background-color: #fff;
border-radius: 4px;
}
.sp-cf:before, .sp-cf:after {
display: none !important;
}
.sp-palette-container {
border: none;
padding-bottom: 290px;
}
/* jQueryUI Datepicker */
.ui-datepicker {
border: 1px solid rgba(51,51,51,.5);
background-color: #fff;
border-radius: 5px;
}
.ui-datepicker .ui-datepicker-header {
border-bottom: 1px dotted rgba(51,51,51,.5);
}
.ui-datepicker-week-end {
background-color: rgba(51,51,51,.05);
}
.ui-state-highlight {
background-color: rgba(51,51,51,.1);
}
.ui-state-active {
background-color: rgba(51,51,51,.2);
}
.ui-icon-circle-triangle-e,.ui-icon-circle-triangle-w {
text-indent: 0 !important;
cursor: pointer;
}
.ui-icon-circle-triangle-e {
margin-left: -1.2em !important;
}
td a.ui-state-hover {
background-color: rgba(51,51,51,.15);
}

View File

@ -1,17 +0,0 @@
<?php
$root = __DIR__;
$file = $_SERVER['REQUEST_URI'];
if(file_exists($root.'/'.$file)){
return false;
}
$_SERVER['REQUEST_URI'] = str_replace('/api/', '/', $_SERVER['REQUEST_URI']);
$_SERVER['REQUEST_URI'] = str_replace('/api', '/', $_SERVER['REQUEST_URI']);
$_SERVER['SCRIPT_NAME'] = '/';
$_SERVER['PHP_SELF'] = $_SERVER['REQUEST_URI'];
$_SERVER['PATH_INFO'] = $_SERVER['PHP_SELF'];
require_once 'api/api.php';

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square70x70logo src="icons/mstile-70x70.png"/>
<square150x150logo src="icons/mstile-150x150.png"/>
<square310x310logo src="icons/mstile-310x310.png"/>
<wide310x150logo src="icons/mstile-310x150.png"/>
<TileColor>#2b5797</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 534 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1,83 +0,0 @@
<!doctype html>
<html lang="en" data-ng-app="TaskBoard">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="Matthew Ross">
<meta name="description" content="A Kanban-inspired app for keeping track of things that need to get done.">
<title>TaskBoard {{ display.smallText }}</title>
<link rel="stylesheet" href="lib/css/bootstrap.min.css">
<link rel="stylesheet" href="lib/css/font-awesome.min.css">
<link rel="stylesheet" href="lib/css/jquery-ui.min.css">
<link rel="stylesheet" href="lib/css/jquery-ui.structure.min.css">
<link rel="stylesheet" href="lib/css/spectrum.css">
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Pontano+Sans|Raleway:500" data-noprefix>
<link rel="stylesheet" href="css/styles.css">
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<!--favicons -->
<link rel="shortcut icon" href="favicons/favicon.ico">
<link rel="apple-touch-icon" sizes="57x57" href="favicons/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="114x114" href="favicons/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="72x72" href="favicons/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="144x144" href="favicons/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="60x60" href="favicons/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="120x120" href="favicons/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="76x76" href="favicons/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="152x152" href="favicons/apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="favicons/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" href="favicons/favicon-192x192.png" sizes="192x192">
<link rel="icon" type="image/png" href="favicons/favicon-160x160.png" sizes="160x160">
<link rel="icon" type="image/png" href="favicons/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="favicons/favicon-16x16.png" sizes="16x16">
<link rel="icon" type="image/png" href="favicons/favicon-32x32.png" sizes="32x32">
<meta name="msapplication-TileColor" content="#2b5797">
<meta name="msapplication-TileImage" content="favicons/mstile-144x144.png">
<meta name="msapplication-config" content="favicons/browserconfig.xml">
</head>
<body>
<div id="wrapper" data-ng-view data-ng-cloak>
</div>
<script src="lib/jquery-1.11.3.min.js"></script>
<script src="lib/jquery-ui.min.js"></script>
<script src="lib/jquery.noty.min.js"></script>
<script src="lib/bootstrap.min.js"></script>
<script src="lib/angular.js"></script>
<script src="lib/angular-route.js"></script>
<script src="lib/angular-sanitize.js"></script>
<script src="lib/ng-context-menu.min.js"></script>
<script src="lib/marked.min.js"></script>
<script src="lib/prefixfree.min.js"></script>
<script src="lib/spectrum.js"></script>
<script src="js/app.js"></script>
<script src="js/controllers/boards.js"></script>
<script src="js/controllers/boardsItemForm.js"></script>
<script src="js/controllers/boardsItemView.js"></script>
<script src="js/controllers/files.js"></script>
<script src="js/controllers/settings.js"></script>
<script src="js/controllers/settingsAutoActions.js"></script>
<script src="js/controllers/settingsUser.js"></script>
<script src="js/controllers/settingsUserForm.js"></script>
<script src="js/controllers/settingsBoard.js"></script>
<script src="js/controllers/settingsBoardForm.js"></script>
<script src="js/controllers/login.js"></script>
<script src="js/controllers/header.js"></script>
<script src="js/services/token.js"></script>
<script src="js/services/user.js"></script>
<script src="js/services/board.js"></script>
<script src="js/services/auth.js"></script>
<script src="js/services/alerts.js"></script>
<script src="js/directives/includeReplace.js"></script>
<script src="js/directives/clickToEdit.js"></script>
<script src="js/directives/onLoadCallback.js"></script>
<script src="js/directives/fileUpload.js"></script>
<script src="js/directives/focus.js"></script>
</body>
</html>

View File

@ -1,47 +0,0 @@
<!doctype html>
<html lang="en" data-ng-app="TaskBoard">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="Matthew Ross">
<meta name="description" content="A Kanban-inspired app for keeping track of things that need to get done.">
<title>TaskBoard {{ display.smallText }}</title>
<link rel="stylesheet" href="lib/css/combined.min.css">
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Pontano+Sans|Raleway:500" data-noprefix>
<link rel="stylesheet" href="css/styles.min.css">
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<!--favicons -->
<link rel="shortcut icon" href="favicons/favicon.ico">
<link rel="apple-touch-icon" sizes="57x57" href="favicons/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="114x114" href="favicons/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="72x72" href="favicons/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="144x144" href="favicons/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="60x60" href="favicons/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="120x120" href="favicons/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="76x76" href="favicons/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="152x152" href="favicons/apple-touch-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="favicons/apple-touch-icon-180x180.png">
<link rel="icon" type="image/png" href="favicons/favicon-192x192.png" sizes="192x192">
<link rel="icon" type="image/png" href="favicons/favicon-160x160.png" sizes="160x160">
<link rel="icon" type="image/png" href="favicons/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="favicons/favicon-16x16.png" sizes="16x16">
<link rel="icon" type="image/png" href="favicons/favicon-32x32.png" sizes="32x32">
<meta name="msapplication-TileColor" content="#2b5797">
<meta name="msapplication-TileImage" content="favicons/mstile-144x144.png">
<meta name="msapplication-config" content="favicons/browserconfig.xml">
</head>
<body>
<div id="wrapper" data-ng-view data-ng-cloak>
</div>
<script src="lib/libs.min.js"></script>
<script src="js/app.min.js"></script>
</body>
</html>

View File

@ -1,82 +0,0 @@
var taskBoardServices = angular.module('TaskBoardServices', []);
var taskBoardControllers = angular.module('TaskBoardControllers', []);
var taskBoardDirectives = angular.module('TaskBoardDirectives', []);
var taskBoard = angular.module('TaskBoard',
['ngRoute', 'ngSanitize',
'ng-context-menu',
'TaskBoardServices',
'TaskBoardControllers',
'TaskBoardDirectives']);
taskBoard.config(['$routeProvider', '$httpProvider',
function($routeProvider, $httpProvider) {
$routeProvider.when('/', {
controller: 'LoginCtrl',
templateUrl: 'partials/login.html'
}).when('/boards', {
controller: 'BoardCtrl',
templateUrl: 'partials/boardSelect.html',
authRequired: true
}).when('/boards/:boardId', {
controller: 'BoardCtrl',
templateUrl: 'partials/board.html',
authRequired: true,
resolve: {
validation: ['$q', '$route', function($q, $route) {
var deferred = $q.defer(),
id = parseInt($route.current.params.boardId);
if (isNaN(id)) {
deferred.reject('INVALID BOARD ID');
} else {
deferred.resolve();
}
return deferred.promise;
}]
}
}).when('/settings', {
controller: 'SettingsCtrl',
templateUrl: 'partials/settings.html',
authRequired: true
}).when('/files/:fileId', {
controller: 'FilesCtrl',
templateUrl: 'partials/files.html',
authRequired: true
}).otherwise({
redirectTo: '/'
});
// Inject the auth token with each API call.
$httpProvider.interceptors.push('TokenInterceptor');
}]);
// Custom handlers for route authentication and rejection of invalid board id
taskBoard.run(['$rootScope', '$location', '$window', 'AuthenticationService',
function($rootScope, $location, $window, AuthenticationService) {
$rootScope.version = 'v0.3.1';
$rootScope.$on('$routeChangeStart', function(event, nextRoute, currentRoute) {
// Redirect to default path if authentication is required but not present.
if (nextRoute !== null && nextRoute.authRequired !== null &&
nextRoute.authRequired && !AuthenticationService.isAuthenticated &&
!$window.localStorage.token) {
$location.path('/');
}
if (nextRoute !== null && nextRoute.controller === 'LoginCtrl' && $window.localStorage.token) {
$location.path('/boards');
}
});
$rootScope.$on('$routeChangeSuccess', function(event, route, previousRoute) {
if (route.controller === 'LoginCtrl' && previousRoute && previousRoute.originalPath !== '') {
AuthenticationService.attemptedRoute = previousRoute;
}
});
$rootScope.$on('$routeChangeError', function(event, current, previous, rejection) {
// Custom rejection from /boards/:boardId route
if (rejection === 'INVALID BOARD ID') {
$location.path('/boards');
}
});
}]);

View File

@ -1,280 +0,0 @@
taskBoardControllers.controller('BoardCtrl',
['$scope', '$routeParams', '$location', '$interval', '$window',
'UserService', 'BoardService', 'AlertService', 'AuthenticationService',
function ($scope, $routeParams, $location, $interval, $window,
UserService, BoardService, AlertService, AuthenticationService) {
// This is here because the BoardCtrl is the default redirect from login.
// If the user was trying to go somewhere else first, they are redirected now.
if (AuthenticationService.attemptedRoute) {
var tmp = AuthenticationService.attemptedRoute,
path = tmp.originalPath;
tmp.keys.forEach(function(key) {
path = path.replace(':' + key.name, tmp.params[key.name]);
});
AuthenticationService.attemptedRoute = null;
$location.path(path);
}
$scope.alerts = AlertService;
$scope.marked = function(text) {
if (text) {
return $window.marked(text);
} else {
return '';
}
};
$scope.boardId = $routeParams.boardId;
$scope.filter = {
user: null,
category: null,
hide: false
};
$scope.filterChanged = function() {
$scope.currentBoard.ownLane.forEach(function (lane) {
if (lane.ownItem) {
lane.ownItem.forEach(function (item) {
item.filtered = false;
if ($scope.filter.user !== null) {
if ($scope.filter.user != item.assignee) {
item.filtered = true;
}
}
if ($scope.filter.category !== null) {
if ($scope.filter.category != item.category) {
item.filtered = true;
}
}
});
}
});
};
$scope.selectBoard = function() {
$location.path('boards/' + $scope.boardNames.current);
};
$scope.openEditItem = function() {
$scope.itemFormData.loadItem($scope.contextItem);
$('.itemModal textarea').css('height', 'auto');
$('.itemModal').modal('show');
};
$scope.openAddItem = function() {
$scope.itemFormData.reset($scope.contextLaneId);
$('.itemModal textarea').css('height', 'auto');
$('.itemModal').modal('show');
};
$scope.removeItem = function() {
$scope.openItem($scope.contextItem, false);
$scope.deleteItem();
};
$scope.changeItemLane = function() {
$scope.itemFormData.loadItem($scope.contextItem);
$scope.itemFormData.isAdd = false;
$scope.submitItem($scope.itemFormData);
};
$scope.contextItem = {}; // Needs to exist prior to onContextMenu call.
$scope.onContextMenu = function(laneId, item) {
$scope.contextItem = item;
$scope.contextLaneId = laneId;
};
// This is called every 250ms until the boards are loaded.
// Once loaded, the repetitive calling is canceled.
// If a default is found the user is redirected to that board.
var checkDefaultBoard = function() {
if ($scope.boardId) {
$interval.cancel($scope.interval);
}
if ($scope.boardsLoaded && !$scope.boardId && $scope.currentUser && parseInt($scope.currentUser.defaultBoard)) {
$interval.cancel($scope.interval);
$location.path('boards/' + $scope.currentUser.defaultBoard);
}
};
$scope.interval = $interval(checkDefaultBoard, 250);
$scope.$on('$destroy', function () { $interval.cancel($scope.interval); });
$scope.boards = [];
$scope.boardsLoaded = false;
$scope.boardNames = [];
$scope.userNames = [];
$scope.laneNames = [];
$scope.categories = [];
$scope.currentBoard = {
loading: true,
name: 'Kanban Board App'
};
var pendingResponse = false,
updateCounter = 0;
$scope.isActiveFilter = function(element) {
var retVal = false;
$scope.boards.forEach(function(board) {
if (board.id === element.id) {
retVal = (board.active === '1');
}
}, this);
return retVal;
};
$scope.loadBoards = function() {
// Don't update the boards if an update is pending.
if (pendingResponse || updateCounter) {
return;
}
pendingResponse = true;
BoardService.getBoards()
.success(function(data) {
pendingResponse = false;
$scope.updateBoards(data);
});
};
$scope.loadBoards();
$scope.boardInterval = $interval($scope.loadBoards, 10000);
$scope.$on('$destroy', function () { $interval.cancel($scope.boardInterval); });
$scope.updateBoards = function(data) {
// Don't update the boards if a position update is pending.
if (0 !== updateCounter) {
return;
}
$scope.boards = data.data;
$scope.boardsLoaded = true;
var boardFound = false;
if ($scope.boards) {
$scope.boardNames = [];
$scope.boards.forEach(function(board) {
if (parseInt(board.active) === 1) {
// Add each board's name to the list.
$scope.boardNames.push({id: board.id, name: board.name});
}
// If the board is the current board, process and assign it.
if (board.id == $scope.boardId) {
board.sharedUser.unshift({ id: 0, username: 'Unassigned' });
board.sharedUser.forEach(function(user) {
$scope.userNames[user.id] = user.username;
});
board.ownLane.forEach(function(lane) {
$scope.laneNames[lane.id] = lane.name;
if (lane.ownItem) {
lane.ownItem.forEach(function(item) {
var date = new Date(item.due_date),
diff = date - Date.now();
if (diff < 0) {
item.datePast = true;
} else if (diff < (1000 * 60 * 60 * 24 * 3)) { // Three days
item.dateNear = true;
}
item.position = parseInt(item.position);
});
}
});
if (board.ownCategory) {
board.ownCategory.unshift({ id: 0, name: 'Uncategorized' });
board.ownCategory.forEach(function(category) {
$scope.categories[category.id] = category.name;
});
}
$scope.currentBoard = board;
$scope.boardNames.current = board.id;
boardFound = true;
}
});
}
if (boardFound) {
$scope.filterChanged(); // Make sure any filters are still applied.
$scope.currentBoard.loading = false;
if ($scope.currentBoard.active === '0') {
$scope.currentBoard = {
loading: true,
name: 'Kanban Board App',
error: true
};
}
} else {
$scope.currentBoard.error = true;
}
};
$scope.toggleLane = function(lane) {
lane.collapsed = !lane.collapsed;
updateCounter++;
BoardService.toggleLane(lane.id)
.success(function(data) {
updateCounter--;
$scope.updateBoards(data);
});
};
// This is not the Angular way.
$scope.updateSortables = function() {
var that = this.$parent;
$('.itemContainer').sortable({
connectWith: '.itemContainer',
placeholder: 'draggable-placeholder',
items: 'div:not(.addItem, .itemHeader, .description)',
change: function(event, ui) {
var parent = $(ui).parent(),
addItem = parent.find('.addItem');
addItem.detach();
parent.append(addItem);
},
stop: function(event, ui) {
var lanes = $.find('.boardColumn'),
positionArray = [];
$(lanes).each(function() {
var laneId = $(this).attr('data-lane-id');
$(this).find('.boardItem').each(function(index) {
var itemId = $(this).attr('data-item-id');
positionArray.push({
item: itemId,
lane: laneId,
position: index
});
});
});
that.updatePositions(positionArray);
}
});
};
$scope.updatePositions = function(positionArray) {
updateCounter++;
BoardService.updateItemPositions(positionArray)
.success(function(data) {
updateCounter--;
$scope.updateBoards(data);
});
};
$scope.currentUser = {};
$scope.userLoaded = false;
$scope.updateCurrentUser = function() {
UserService.currentUser()
.success(function(data) {
$scope.userLoaded = true;
$scope.currentUser = data.data;
});
};
$scope.updateCurrentUser();
}]);

View File

@ -1,132 +0,0 @@
taskBoardControllers.controller('ItemFormBoardCtrl',
['$scope', 'BoardService',
function ($scope, BoardService) {
var defaultColor = '#ffffe0';
$scope.itemFormData = {
setFocus: false,
isSaving: false,
isAdd: true,
itemId: 0,
title: '',
titleError: false,
description: '',
assignee: 0,
category: 0,
lane: 0,
color: defaultColor,
dueDate: null,
points: null,
pointsError: false,
reset: function(laneId) {
$('.popover-dismiss').popover({html:true});
this.setFocus = true;
this.isSaving = false;
this.isAdd = true;
this.itemId = 0;
this.title = '';
this.titleError = false;
this.description = '';
this.assignee = 0;
this.category = 0;
this.lane = laneId || 0;
this.color = defaultColor;
this.spectrum(); // Reset the color plugin to default as well.
this.dueDate = null;
this.points = null;
this.pointsError = false;
var that = this;
$('.itemModal').on('hidden.bs.modal', function (e) {
that.reset();
});
},
loadItem: function(item) {
this.reset(item.lane_id);
this.isAdd = false;
this.itemId = item.id;
this.title = item.title;
this.description = item.description;
this.assignee = item.assignee === "0" ? 0 : item.assignee;
this.category = item.category === "0" ? 0 : item.category;
this.color = item.color;
this.spectrum(this.color);
this.dueDate = item.due_date;
this.points = parseInt(item.points);
this.position = item.position;
},
// Uses jQuery to close the modal and reset colorpicker.
cancel: function() {
$('.itemModal').modal('hide');
$('#spectrum').spectrum('hide');
$('#spectrum').spectrum('enable');
},
// Uses jQuery to set the colorpicker.
spectrum: function(color) {
color = color || defaultColor;
$('#spectrum').spectrum({
color: color,
allowEmpty: false,
localStorageKey: 'taskboard.colorPalette',
showPalette: true,
palette: [ ['#fff', '#ececec', '#ffffe0', '#ffe0fa', '#bee7f4', '#c3f4b5', '#debee8', '#ffdea9', '#ffbaba'] ],
showSelectionPalette: true,
showButtons: false,
showInput: true,
preferredFormat: 'hex3',
appendTo: '#addEdit'
});
},
// Uses jQuery to set the datepicker.
datePicker: function() {
$('#datepicker').datepicker();
}
};
$scope.$parent.itemFormData = $scope.itemFormData;
$scope.submitItem = function (itemFormData) {
itemFormData.isSaving = true;
$('#spectrum').spectrum('disable');
itemFormData.titleError = false;
if (itemFormData.title === '') {
$scope.alerts.showAlert({
type:'error',
text: 'Title is required to add a new item.'
});
itemFormData.isSaving = false;
itemFormData.titleError = true;
return;
}
itemFormData.pointsError = false;
if (itemFormData.points === undefined) { // It is undefined if invalid
$scope.alerts.showAlert({
type:'error',
text: 'Points must be greater than or equal to zero (or left empty).'
});
itemFormData.isSaving = false;
itemFormData.pointsError = true;
return;
}
if (itemFormData.isAdd) {
BoardService.addItem(itemFormData, $scope.currentBoard.id)
.success(function(data) {
isSuccess(data);
});
} else {
BoardService.updateItem(itemFormData)
.success(function(data) {
isSuccess(data);
});
}
};
$scope.$parent.submitItem = $scope.submitItem;
var isSuccess = function(data) {
$scope.alerts.showAlerts(data.alerts);
if (data.alerts[0].type == 'success') {
$scope.updateBoards(data);
$scope.itemFormData.cancel();
}
};
}]);

View File

@ -1,244 +0,0 @@
taskBoardControllers.controller('ItemViewBoardCtrl',
['$scope', '$window', 'BoardService',
function ($scope, $window, BoardService) {
$scope.viewItem = {};
$scope.comment = {};
$scope.toggle = {
sidebar: false
};
$scope.fileReset = false;
$scope.comments = {
options: [
{id: 0, text: 'Oldest First'},
{id: 1, text: 'Newest First'}
],
sorting: 0
};
$scope.markedComment = function(text) {
if (text) {
return $window.marked(text);
} else {
return "<p>No text</p>";
}
};
// Takes an array of timestamps and converts them to display dates.
var convertDates = function(timestampArray) {
if (undefined === timestampArray) {
return;
}
var date = new Date();
timestampArray.forEach(function(item) {
date.setTime(item.timestamp * 1000);
item.date = date.toLocaleString();
});
},
updateItem = function(item) {
$scope.viewItem = item;
$scope.viewItem.laneName = $scope.laneNames[item.lane_id];
convertDates($scope.viewItem.ownComment);
convertDates($scope.viewItem.ownAttachment);
convertDates($scope.viewItem.ownActivity);
};
$scope.openItem = function(item, openModal) {
if (undefined === openModal) {
openModal = true;
}
updateItem(item);
$scope.viewItem.disabled = false;
if (undefined === $scope.viewItem.ownComment) {
$scope.viewItem.ownComment = [];
}
if (undefined === $scope.viewItem.ownAttachment) {
$scope.viewItem.ownAttachment = [];
}
$scope.fileReset = true;
if (openModal) {
$('.itemViewModal textarea').css('height', 'auto');
$('.itemViewModal').modal({ show: true, keyboard:false });
}
};
$scope.$parent.openItem = $scope.openItem;
$scope.editItem = function() {
$scope.itemFormData.loadItem($scope.viewItem);
$('.itemViewModal').modal('hide');
$('.itemModal').modal('show');
}
$scope.deleteItem = function() {
noty({
text: 'Deleting an item cannot be undone.<br>Continue?',
layout: 'center',
type: 'information',
modal: true,
buttons: [
{
addClass: 'btn btn-default',
text: 'Ok',
onClick: function($noty) {
$noty.close();
$scope.viewItem.disabled = true;
BoardService.removeItem($scope.viewItem.id)
.success(function(data) {
$scope.alerts.showAlerts(data.alerts);
if (data.alerts[0].type == 'success') {
$scope.updateBoards(data);
$('.itemViewModal').modal('hide');
}
});
}
},
{
addClass: 'btn btn-info',
text: 'Cancel',
onClick: function($noty) {
$noty.close();
}
}
]
});
};
$scope.$parent.deleteItem = $scope.deleteItem;
$scope.deleteComment = function(commentId) {
noty({
text: 'Deleting a comment cannot be undone.<br>Continue?',
layout: 'center',
type: 'information',
modal: true,
buttons: [
{
addClass: 'btn btn-default',
text: 'Ok',
onClick: function($noty) {
$noty.close();
BoardService.removeItemComment($scope.viewItem.id, commentId)
.success(function(data) {
$scope.alerts.showAlerts(data.alerts);
if (data.alerts[0].type == 'success') {
updateItem(data.data[0]);
}
$scope.loadBoards();
});
}
},
{
addClass: 'btn btn-info',
text: 'Cancel',
onClick: function($noty) {
$noty.close();
}
}
]
});
};
$scope.attachmentDeleting = [];
$scope.deleteAttachment = function(fileId) {
noty({
text: 'Deleting an attachment cannot be undone.<br>Continue?',
layout: 'center',
type: 'information',
modal: true,
buttons: [
{
addClass: 'btn btn-default',
text: 'Ok',
onClick: function($noty) {
$noty.close();
$scope.attachmentDeleting[fileId] = true;
BoardService.removeItemAttachment($scope.viewItem.id, fileId)
.success(function(data) {
$scope.alerts.showAlerts(data.alerts);
if (data.alerts[0].type == 'success') {
updateItem(data.data[0]);
}
$scope.loadBoards();
});
}
},
{
addClass: 'btn btn-info',
text: 'Cancel',
onClick: function($noty) {
$noty.close();
}
}
]
});
};
$scope.addItemComment = function(comment) {
if (comment === "" || undefined === comment) {
$scope.alerts.showAlert({ type: 'error', text: 'Comment cannot be empty.' });
return;
}
$scope.viewItem.disabled = true;
BoardService.addItemComment($scope.viewItem.id, comment)
.success(function(data) {
updateItem(data.data[0]);
$scope.loadBoards();
$scope.viewItem.disabled = false;
});
// Reset input
$scope.comment.text = "";
};
$scope.beginEditComment = function(commentId, comment) {
$scope.comment.isEdit = true;
$scope.comment.editText = comment;
$scope.comment.id = commentId;
};
$scope.editComment = function(commentId, comment) {
$scope.comment.isEdit = false;
if (comment === '' || undefined === comment) {
$scope.alerts.showAlert({ type: 'error', text: 'Comment cannot be empty.' });
return;
}
$scope.viewItem.disabled = true;
BoardService.updateItemComment(commentId, comment)
.success(function(data) {
updateItem(data.data[0]);
$scope.loadBoards();
$scope.viewItem.disabled = false;
});
};
$scope.addItemAttachment = function() {
if ($scope.itemUpload === undefined || $scope.itemUpload.name === '') {
$scope.alerts.showAlert({ type: 'error', text: 'Select a file before uploading.' });
return;
}
$scope.viewItem.disabled = true;
BoardService.addItemAttachment($scope.viewItem.id, $scope.itemUpload)
.success(function(data) {
updateItem(data.data[0]);
$scope.loadBoards();
$scope.viewItem.disabled = false;
// Reset input
$scope.fileReset = true;
})
.error(function(data) {
$scope.viewItem.disabled = false;
$scope.fileReset = true;
console.log(data);
$scope.alerts.showAlert({ type: 'error', text: 'There was an error with your attachment. ' +
'The file may be too large.' });
});
};
}]);

View File

@ -1,27 +0,0 @@
taskBoardControllers.controller('FilesCtrl',
['$scope', '$routeParams', '$http', '$window',
'BoardService',
function ($scope, $routeParams, $http, $window, BoardService) {
$scope.file = {
id: $routeParams.fileId,
loading: true
};
$http.get('api/items/1/upload/' + $scope.file.id)
.success(function(data) {
$scope.file.loading = false;
if (data.data) {
$scope.file.data = data.data[0];
var date = new Date();
date.setTime($scope.file.data.timestamp * 1000);
$scope.file.data.date = date.toLocaleString();
$scope.file.data.filename = 'api/uploads/' + $scope.file.data.filename;
} else {
$scope.file.error = true;
}
})
.error(function(data) {
$scope.file.loading = false;
$scope.file.error = true;
});
}]);

View File

@ -1,48 +0,0 @@
taskBoardControllers.controller('HeaderCtrl',
['$scope', '$window', '$location', 'UserService', 'AuthenticationService', 'AlertService',
function ($scope, $window, $location, UserService, AuthenticationService, AlertService) {
UserService.validateToken()
.error($scope.logout);
$scope.display = {
username: '',
smallText: ' - Settings'
};
$scope.$parent.display = $scope.display;
$scope.$watch('currentUser', function(newValue, oldValue) {
if (undefined !== newValue && undefined !== newValue.username) {
$scope.display.username = '(' + newValue.username + ')';
}
});
$scope.page = {
boards: false,
settings: true
};
if ($location.path().indexOf('boards') > -1) {
$scope.page.boards = true;
$scope.page.settings = false;
$scope.$watch('currentBoard.name', function(newValue, oldValue) {
$scope.display.smallText = ' - ' + newValue;
});
}
if ($location.path().indexOf('files') > -1) {
$scope.page.boards = false;
$scope.page.settings = false;
$scope.display.smallText = ' - File Viewer';
}
try {
$.noty.closeAll(); // Clear any alerts on page load.
} catch(e) {}
$scope.logout = function() {
UserService.logOut()
.then(function(data) {
AuthenticationService.reset();
delete $window.localStorage.token;
$location.path('/');
});
};
}]);

View File

@ -1,38 +0,0 @@
taskBoardControllers.controller('LoginCtrl',
['$rootScope', '$scope', '$location', '$window', 'UserService', 'AuthenticationService', 'AlertService',
function ($rootScope, $scope, $location, $window, UserService, AuthenticationService, AlertService) {
$scope.formdata = {
username: '',
password: '',
rememberme: false
};
$scope.isSaving = false;
// Uses jQuery to handle clearing of any open modals.
$scope.clear = function() {
$('[name~=Modal]').modal('hide');
$('.modal-backdrop').hide();
};
$scope.logIn = function (formdata) {
$scope.errors = [];
$scope.isSaving = true;
UserService.logIn(formdata.username, formdata.password, formdata.rememberme)
.success(function(data) {
if (null !== data.alerts) {
AlertService.showAlerts(data.alerts);
}
AuthenticationService.isAuthenticated = true;
$window.localStorage.token = data.data;
$location.path('/boards');
}).error(function(data, status) {
$scope.isSaving = false;
$scope.errors.push(data.message);
if (status === 503) {
$scope.errors[0] = $scope.errors[0] + ' Ensure api directory is writable.';
}
});
};
}
]);

View File

@ -1,99 +0,0 @@
taskBoardControllers.controller('SettingsCtrl',
['$scope', 'UserService', 'AlertService',
function ($scope, UserService, AlertService) {
$scope.alerts = AlertService;
$scope.users = [];
$scope.boards = [];
$scope.boardNames = [];
$scope.boardLookup = {};
$scope.currentUser = {};
$scope.slide = {
open: false
};
$scope.loadingCurrentUser = true;
$scope.loadingBoards = true;
$scope.loadingUsers = true;
$scope.loadCurrentUser = function() {
UserService.currentUser()
.success(function(data) {
$scope.currentUser = data.data;
loadOptionsData(data.data);
$scope.loadingCurrentUser = false;
});
};
$scope.loadCurrentUser();
loadOptionsData = function (data) {
$scope.currentUser.options.tasksOrder = parseInt(data.options.tasksOrder);
$scope.currentUser.options.showAnimations = data.options.showAnimations;
$scope.currentUser.options.showAssignee = data.options.showAssignee;
};
$scope.saveOptions = function() {
UserService.saveOptions($scope.currentUser.options.tasksOrder,
$scope.currentUser.options.showAnimations,
$scope.currentUser.options.showAssignee)
.success(function(data) {
loadOptionsData({options: data.data});
});
};
$scope.updateBoardsList = function(data) {
if (undefined === data) {
return;
}
$scope.loadingBoards = false;
if (null === data) {
$scope.boards = [];
return;
}
$scope.boards = data;
var boardNames = [];
data.forEach(function(board) {
boardNames.push({ 'id': board.id, 'name':board.name, 'active':board.active });
});
$scope.boardNames = boardNames;
for (var i = 0, len = boardNames.length; i < len; i++) {
$scope.boardLookup[boardNames[i].id] = boardNames[i].name;
}
$scope.updateActions();
};
$scope.updateUsers = function(data) {
if (undefined === data || null === data) {
return;
}
$scope.users = data;
$scope.loadingUsers = false;
$scope.updateActions();
};
$scope.actions = [];
$scope.actionsLoading = true;
$scope.updateActions = function() {
if ('1' !== $scope.currentUser.isAdmin) {
return;
}
UserService.actions()
.success(function(data) {
$scope.actions = data.data;
if ($scope.actions) {
var date = new Date();
$scope.actions.forEach(function(action) {
date.setTime(action.timestamp * 1000);
action.date = date.toLocaleString();
});
}
$scope.actionsLoading = false;
});
};
$scope.updateActions();
}]);

View File

@ -1,319 +0,0 @@
taskBoardControllers.controller('AutomaticActionsCtrl',
['$scope', '$interval', 'BoardService',
function ($scope, $interval, BoardService) {
$scope.loadingActions = true;
$scope.actions = [];
$scope.secondarySelection = [];
$scope.boardCategories = [{ id: 0, name: 'Uncategorized' }];
$scope.userList = [{ id: 0, name: 'Unassigned', username: 'Unassigned' }];
$scope.actionData = {
isSaving: false,
board: null,
trigger: 0,
triggerWord: '',
secondary: null,
action: 0,
color: null,
category: null,
assignee: null
};
$scope.actionDeleting = [];
$scope.actionTypes = [
{ id: 0, action: 'Set item color' },
{ id: 1, action: 'Set item category'},
{ id: 2, action: 'Set item assignee' },
{ id: 3, action: 'Clear item due date' }
];
$scope.actionOptions = {
triggers: [
{ id: 0, trigger: 'Item moves to column' },
{ id: 1, trigger: 'Item assigned to user' },
{ id: 2, trigger: 'Item set to category' }
]
};
$scope.updateTriggers = function() {
var foundCategories = false;
$scope.actionOptions.triggers.forEach(function(trigger) {
if (trigger.id === 2) {
foundCategories = true;
}
}, this);
if (!foundCategories) {
$scope.actionOptions.triggers.push({ id: 2, trigger: 'Item set to category' });
}
if ($scope.boardCategories.length === 1) {
$scope.actionOptions.triggers.forEach(function(trigger, index) {
if (trigger.id === 2) {
$scope.actionOptions.triggers.splice(index, 1);
}
});
$scope.actionTypes.forEach(function(type, index) {
if (type.id === 1) {
$scope.actionTypes.splice(index, 1);
}
});
}
};
var getBoardData = function(boardId) {
if (null === boardId || undefined === boardId)
{
return;
}
var boardData;
$scope.boards.forEach(function(board) {
if (board.id === boardId) {
boardData = board;
}
});
return boardData;
},
getCategories = function(boardData) {
var categories = [{ id: '0', name: 'Uncategorized' }];
if (boardData && boardData.ownCategory) {
boardData.ownCategory.forEach(function(category) {
categories.push(category);
});
}
return categories;
},
getUsers = function(boardData) {
var userList = [{ id: '0', name: 'Unassigned', username: 'Unassigned' }];
if (boardData) {
boardData.sharedUser.forEach(function(user) {
userList.push({ id: user.id, name: user.username });
});
}
return userList;
},
getSecondaryText = function(action) {
var text = ': ',
actionBoard = getBoardData(action.board_id),
boardCategories = getCategories(actionBoard),
userList = getUsers(actionBoard);
switch(parseInt(action.trigger_id)) {
case 0: // Lane
actionBoard.ownLane.forEach(function(lane) {
if (lane.id === action.secondary_id) {
text += lane.name;
}
});
break;
case 1: // User
userList.forEach(function(user) {
if (user.id === action.secondary_id) {
text += user.name;
}
});
break;
case 2: // Category
boardCategories.forEach(function(category) {
if (category.id === action.secondary_id) {
text += category.name;
}
});
break;
}
return text;
},
getActionText = function(action) {
var text = '',
actionBoard = getBoardData(action.board_id),
boardCategories = getCategories(actionBoard),
userList = getUsers(actionBoard);
switch(parseInt(action.action_id)) {
case 0: // Color
text = ': ' + action.color;
break;
case 1: // Category
boardCategories.forEach(function(category) {
if (category.id === action.category_id) {
text = ': ' + category.name;
}
});
break;
case 2: // Assignee
userList.forEach(function(user) {
if (user.id === action.assignee_id) {
text = ': ' + user.name;
}
});
break;
}
return text;
},
updateAutoActions = function(actions) {
if (!actions) {
$scope.actions = [];
return;
}
var mappedActions = [];
actions.forEach(function(action) {
var actionTrigger, actionType;
$scope.actionOptions.triggers.forEach(function(trigger) {
if (trigger.id === parseInt(action.trigger_id)) {
actionTrigger = trigger.trigger;
}
});
$scope.actionTypes.forEach(function(type) {
if (type.id === parseInt(action.action_id)) {
actionType = type.action;
}
});
mappedActions.push({
id: action.id,
board: $scope.boardLookup[action.board_id],
trigger: actionTrigger + getSecondaryText(action),
action: actionType + getActionText(action)
});
});
$scope.actions = mappedActions;
};
$scope.loadActions = function() {
BoardService.getAutoActions()
.success(function(data) {
updateAutoActions(data.data);
$scope.loadingActions = false;
});
};
$interval($scope.loadActions, 2000);
// Wait until boards are loaded to load the actions.
$scope.$watch('loadingBoards', function() {
if (!$scope.loadingBoards) {
$scope.loadActions();
}
});
$scope.addAction = function() {
if ($scope.actionData.secondary === null ||
($scope.actionData.action !== 3 &&
($scope.actionData.color === null && $scope.actionData.category === null &&
$scope.actionData.assignee === null))) {
$scope.alerts.showAlert({
type: 'error',
text: 'One or more required fields are not entered. Automatic Action not added.'
});
return;
}
$scope.actionData.isSaving = true;
BoardService.addAutoAction($scope.actionData)
.success(function(data) {
$scope.actionData.isSaving = false;
$scope.alerts.showAlerts(data.alerts);
if (data.alerts[0].type == 'success') {
updateAutoActions(data.data);
}
});
};
$scope.removeAction = function(actionId) {
$scope.actionDeleting[actionId] = true;
BoardService.removeAutoAction(actionId)
.success(function(data) {
$scope.alerts.showAlerts(data.alerts);
updateAutoActions(data.data);
});
};
$scope.updateSecondary = function() {
$scope.secondarySelection = [];
$scope.actionData.secondary = null;
$scope.actionData.action = 0;
var boardData = getBoardData($scope.actionData.board);
$scope.boardCategories = getCategories(boardData);
$scope.updateTriggers();
$scope.userList = getUsers(boardData);
if (boardData) {
switch($scope.actionData.trigger) {
case 0:
$scope.secondarySelection = boardData.ownLane;
break;
case 1:
$scope.secondarySelection = $scope.userList;
break;
case 2:
$scope.secondarySelection = $scope.boardCategories;
break;
}
}
};
$scope.getTriggerWord = function() {
if ($scope.actionData.trigger !== null) {
var word = $scope.actionOptions.triggers[$scope.actionData.trigger].trigger.split(" ").pop();
$scope.actionData.triggerWord = word.charAt(0).toUpperCase() + word.slice(1);
}
$scope.updateSecondary();
};
$scope.getTriggerWord();
$scope.resetActionSecondary = function() {
$scope.actionData.color = null;
$scope.actionData.category = null;
$scope.actionData.assignee = null;
};
var defaultColor = '#ffffe0';
$scope.spectrum = function(color) {
color = color || defaultColor;
$('#spectrum').spectrum({
color: color,
allowEmpty: false,
localStorageKey: 'taskboard.colorPalette',
showPalette: true,
palette: [ ['#fff', '#ececec', '#ffffe0', '#ffe0fa', '#bee7f4', '#c3f4b5', '#debee8', '#ffdea9', '#ffbaba'] ],
showSelectionPalette: true,
showButtons: false,
showInput: true,
preferredFormat: 'hex3',
disabled: $scope.actionData.board === null
});
};
$scope.updateColorpicker = function() {
if (null !== $scope.actionData.board) {
$('#spectrum').spectrum('enable');
$scope.actionData.color = $('#spectrum').spectrum('option', 'color');
$scope.updateSecondary();
return;
}
$('#spectrum').spectrum('disable');
$scope.actionData.color = null;
};
// Check every 500ms to see if a board has been chosen.
var updateIfBoardChosen = function() {
if ($scope.actionData.board !== null) {
$interval.cancel($scope.interval);
$scope.getTriggerWord();
}
};
$scope.interval = $interval(updateIfBoardChosen, 500);
$scope.$on('$destroy', function () { $interval.cancel($scope.interval); });
}]);

View File

@ -1,134 +0,0 @@
taskBoardControllers.controller('BoardSettingsCtrl',
['$scope', '$interval', 'BoardService',
function ($scope, $interval, BoardService) {
var pendingResponse = false,
retryCount = 3,
loadBoards = function() {
if (pendingResponse) {
return;
}
pendingResponse = true;
BoardService.getBoards()
.success(function(data) {
$scope.updateBoardsList(data.data);
pendingResponse = false;
retryCount = 3;
}).error(function(data) {
if (retryCount--) {
pendingResponse = false;
return;
}
$interval.cancel($scope.interval);
$scope.$parent.loadingBoards = false;
});
};
loadBoards();
$scope.interval = $interval(loadBoards, 5000);
$scope.$on('$destroy', function () { $interval.cancel($scope.interval); });
$scope.userOptions = {
tasksAt: [
{ id: 0, text: 'bottom of column'},
{ id: 1, text: 'top of column'}
],
taskOrder: 0,
showAnimations: true,
showAssignee: true
};
$scope.boardSort = {
options: [
{ sort: 'id', name: 'Creation Date' },
{ sort: 'name', name: 'Board Name' }
],
sort: 'name'
};
$scope.boardFilter = {
options: [
{ filter: 'all', name: 'All Boards' },
{ filter: 'active', name: 'Active' },
{ filter: 'inactive', name: 'Inactive' }
],
filter: 'all',
userFilter: null
};
$scope.boardsFilter = function(element) {
switch ($scope.boardFilter.filter) {
case 'all':
return true;
case 'active':
return element.active === '1';
case 'inactive':
return element.active === '0';
}
};
$scope.boardsUserFilter = function(element) {
if (null === $scope.boardFilter.userFilter) {
return true;
}
var retVal = false;
element.sharedUser.forEach(function(user) {
console.log(user.id === $scope.boardFilter.userFilter);
if (user.id === $scope.boardFilter.userFilter) {
retVal = true;
}
});
return retVal;
};
$scope.toggleActiveState = function(boardId) {
BoardService.toggleActiveState(boardId)
.success(function(data) {
$scope.alerts.showAlerts(data.alerts);
$scope.boards = data.data;
});
};
$scope.isDeleting = [];
$scope.removeBoard = function(boardId) {
noty({
text: 'Deleting a board cannot be undone.<br>Continue?',
layout: 'center',
type: 'information',
modal: true,
buttons: [
{
addClass: 'btn btn-default',
text: 'Ok',
onClick: function($noty) {
$noty.close();
$scope.boards.forEach(function(board) {
if (board.id === boardId) {
$scope.isDeleting[boardId] = true;
}
});
BoardService.removeBoard(boardId)
.success(function(data) {
$scope.alerts.showAlerts(data.alerts);
$scope.boards = data.data;
$scope.updateUsers(data.users);
});
}
},
{
addClass: 'btn btn-info',
text: 'Cancel',
onClick: function($noty) {
$noty.close();
}
}
]
});
};
}]);

View File

@ -1,218 +0,0 @@
taskBoardControllers.controller('BoardFormSettingsCtrl',
['$scope', 'BoardService',
function ($scope, BoardService) {
$scope.boardFormData = {
setFocus: false,
boardId: 0,
isAdd: true,
name: '',
lanes: [],
laneName: '',
categories: [],
categoryName: '',
users: [],
nameError: false,
lanesError: false,
categoriesError: false,
isSaving: false,
updateLanesSorting: function() {
var that = this;
$('.lanes').sortable({
placeholder: 'lane-placeholder',
stop: function(event, ui) {
that.lanes.length = 0;
$(ui.item).parent().children().each(function(index) {
that.lanes.push({
id: $(this).find('.hidden').text(),
name: $(this).find('.item-text').text(),
position: index
});
});
$scope.$apply();
}
});
},
setBoard: function(board) {
this.reset();
this.isAdd = false;
this.boardId = board.id;
this.name = board.name;
var that = this;
if (undefined !== board.ownLane) {
board.ownLane.forEach(function(lane) {
that.lanes.push({
id: lane.id,
name: lane.name,
position: lane.position
});
});
}
if (undefined !== board.ownCategory) {
board.ownCategory.forEach(function(cat) {
that.categories.push({
id: cat.id,
name: cat.name
});
});
}
if (undefined !== board.sharedUser) {
board.sharedUser.forEach(function(user) {
that.users[user.id] = true;
});
}
this.updateLanesSorting();
},
addLane: function() {
this.lanesError = false;
if (this.laneName === '') {
this.setAlert(false, true, false, 'Column name cannot be empty.');
return;
}
var that = this;
this.lanes.forEach(function(lane) {
if (that.laneName == lane.name) {
that.setAlert(false, true, false, 'That column name has already been added.');
that.lanesError = true;
}
});
// Add the new lane (if no error) and reset the input.
if (!this.lanesError) {
this.lanes.push({
id: 0,
name: this.laneName,
position: this.lanes.length
});
}
this.laneName = '';
this.updateLanesSorting();
},
removeLane: function(lane) {
if (this.isSaving) { return; }
this.lanes.splice(this.lanes.indexOf(lane), 1);
var pos = 0;
this.lanes.forEach(function(lane) {
lane.position = pos;
pos++;
});
},
addCategory: function() {
this.categoriesError = false;
if (this.categoryName === '') {
this.setAlert(false, false, true, 'Category name cannot be empty.');
return;
}
var that = this;
this.categories.forEach(function(category) {
if (that.categoryName == category) {
this.setAlert(false, false, true, 'That category name has already been added.');
}
});
// Add the new category (if no error) and reset the input.
if (!this.categoriesError) {
this.categories.push({
id: 0,
name: this.categoryName
});
}
this.categoryName = '';
},
removeCategory: function(category) {
if (this.isSaving) { return; }
this.categories.splice(this.categories.indexOf(category), 1);
},
setForSaving: function() {
this.nameError = false;
this.lanesError = false;
this.categoriesError = false;
this.isSaving = true;
},
setAlert: function(name, lane, cat, message) {
this.nameError = name;
this.lanesError = lane;
this.categoriesError = cat;
this.isSaving = false;
$scope.alerts.showAlert({ 'type': 'error', 'text': message });
},
reset: function() {
this.setFocus = true;
this.boardId = 0;
this.isAdd = true;
this.name = '';
this.lanes = [];
this.laneName = '';
this.categories = [];
this.categoryName = '';
this.users = [];
this.nameError = false;
this.lanesError = false;
this.categoriesError = false;
this.isSaving = false;
},
// Uses jQuery to close modal and reset form data.
cancel: function() {
$('.boardModal').modal('hide');
var that = this;
$('.boardModal').on('hidden.bs.modal', function (e) {
that.reset();
});
}
};
$scope.$parent.boardFormData = $scope.boardFormData;
$scope.addBoard = function(boardFormData) {
boardFormData.setForSaving();
if (!checkFormInputs(boardFormData)) {
return;
}
BoardService.addBoard(boardFormData)
.success(function(data) {
$scope.alerts.showAlerts(data.alerts);
$scope.updateBoardsList(data.data);
boardFormData.reset();
if (data.alerts[0].type == 'success') {
$('.boardModal').modal('hide');
}
});
};
$scope.editBoard = function(boardFormData) {
boardFormData.setForSaving();
if (!checkFormInputs(boardFormData)) {
return;
}
BoardService.editBoard(boardFormData)
.success(function(data) {
$scope.alerts.showAlerts(data.alerts);
$scope.updateBoardsList(data.data);
boardFormData.reset();
if (data.alerts[0].type == 'success') {
$('.boardModal').modal('hide');
}
});
};
var checkFormInputs = function(boardFormData) {
if ('' === boardFormData.name) {
boardFormData.setAlert(true, false, false, 'Board name cannot be empty.');
return false;
}
if (0 === boardFormData.lanes.length) {
boardFormData.setAlert(false, true, false, 'At least one lane is required.');
return false;
}
return true;
};
}]);

View File

@ -1,194 +0,0 @@
taskBoardControllers.controller('UserSettingsCtrl',
['$scope', '$interval', 'UserService',
function ($scope, $interval, UserService) {
var pendingResponse = false,
retryCount = 3,
loadUsers = function() {
if (pendingResponse) {
return;
}
pendingResponse = true;
UserService.getUsers()
.success(function(data) {
$scope.updateUsers(data.data);
pendingResponse = false;
retryCount = 3;
})
.error(function() {
if (retryCount--) {
pendingResponse = false;
return;
}
$interval.cancel($scope.interval);
$scope.$parent.loadingUsers = false;
});
};
loadUsers();
$scope.interval = $interval(loadUsers, 5000);
$scope.$on('$destroy', function () { $interval.cancel($scope.interval); });
$scope.passwordFormData = {
currentPass: '',
newPass: '',
verifyPass: '',
newPassError: false,
currentPassError: false,
isSaving: false,
setForSaving: function() {
this.newPassError = false;
this.currentPassError = false;
this.isSaving = true;
},
setAlert: function(newError, oldError, message) {
this.newPassError = newError;
this.currentPassError = oldError;
this.isSaving = false;
$scope.alerts.showAlert({ 'type': 'error', 'text': message });
},
reset: function() {
this.currentPass = '';
this.newPass = '';
this.verifyPass = '';
this.newPassError = false;
this.currentPassError = false;
this.isSaving = false;
}
};
$scope.changePassword = function(passwordFormData) {
passwordFormData.setForSaving();
if (passwordFormData.currentPass === '') {
passwordFormData.setAlert(false, true, 'Current password cannot be blank.');
} else if (passwordFormData.newPass === '' || passwordFormData.verifyPass === '') {
passwordFormData.setAlert(true, false, 'New password cannot be blank.');
} else {
if(passwordFormData.newPass == passwordFormData.verifyPass) {
UserService.changePassword(passwordFormData.currentPass, passwordFormData.newPass)
.success(function(data) {
$scope.alerts.showAlerts(data.alerts);
passwordFormData.isSaving = false;
passwordFormData.currentPass = '';
if (data.alerts[0].text == "Password changed.") {
passwordFormData.reset();
} else {
passwordFormData.currentPassError = true;
}
});
} else {
passwordFormData.setAlert(true, false, 'New passwords do not match.');
}
}
};
$scope.usernameFormData = {
newUsername: '',
usernameError: false,
isSaving: false,
setAlert: function(message) {
this.isSaving = false;
this.usernameError = true;
$scope.alerts.showAlert({ 'type': 'error', 'text': message });
},
reset: function() {
this.newUsername = '';
this.usernameError = false;
this.isSaving = false;
}
};
$scope.changeUsername = function(newUsernameFormData) {
$scope.usernameFormData.isSaving = true;
if (newUsernameFormData.newUsername === '') {
newUsernameFormData.setAlert('Username cannot be blank.');
newUsernameFormData.isSaving = false;
} else {
UserService.changeUsername(newUsernameFormData.newUsername)
.success(function(data) {
$scope.alerts.showAlerts(data.alerts);
$scope.updateUsers(data.data);
$scope.loadCurrentUser();
newUsernameFormData.isSaving = false;
newUsernameFormData.newUsername = '';
});
}
};
$scope.emailFormData = {
newEmail: '',
emailError: false,
isSaving: false,
setAlert: function(message) {
this.isSaving = false;
this.emailError = true;
$scope.alerts.showAlert({ 'type': 'error', 'text': message });
},
reset: function() {
this.newEmail = '';
this.emailError = false;
this.isSaving = false;
}
};
$scope.changeEmail = function(newEmailFormData) {
$scope.emailFormData.isSaving = true;
UserService.changeEmail(newEmailFormData.newEmail)
.success(function(data) {
$scope.alerts.showAlerts(data.alerts);
$scope.updateUsers(data.data);
$scope.loadCurrentUser();
newEmailFormData.isSaving = false;
newEmailFormData.newUsername = '';
});
};
$scope.updatingDefaultBoard = false;
$scope.setDefaultBoard = function() {
$scope.updatingDefaultBoard = true;
UserService.changeDefaultBoard($scope.currentUser.defaultBoard)
.success(function(data) {
$scope.updatingDefaultBoard = false;
$scope.updateUsers(data.data);
$scope.alerts.showAlert({ 'type': 'success', 'text': 'Default board changed.' });
});
};
$scope.isDeleting = [];
$scope.removeUser = function(userId) {
noty({
text: 'Deleting a user cannot be undone.<br>Continue?',
layout: 'center',
type: 'information',
modal: true,
buttons: [
{
addClass: 'btn btn-default',
text: 'Ok',
onClick: function($noty) {
$noty.close();
$scope.isDeleting[userId] = true;
UserService.removeUser(userId)
.success(function(data) {
$scope.alerts.showAlerts(data.alerts);
$scope.updateUsers(data.data);
$scope.updateBoardsList(data.boards);
});
}
},
{
addClass: 'btn btn-info',
text: 'Cancel',
onClick: function($noty) {
$noty.close();
}
}
]
});
};
}]);

View File

@ -1,118 +0,0 @@
taskBoardControllers.controller('UserFormSettingsCtrl',
['$scope', 'UserService',
function ($scope, UserService) {
$scope.userFormData = {
setFocus: false,
userId: 0,
isAdd: true,
username: '',
password: '',
email: '',
verifyPass: '',
defaultBoard: null,
isAdmin: false,
passError: false,
usernameError: false,
emailError: false,
isSaving: false,
setUser: function(user) {
this.reset();
this.isAdd = false;
this.userId = user.id;
this.username = user.username;
this.email = user.email;
this.defaultBoard = user.default_board;
this.isAdmin = user.is_admin == '1';
},
reset: function() {
$('.popover-dismiss').popover();
this.setFocus = true;
this.userId = 0;
this.isAdd = true;
this.username = '';
this.password = '';
this.email = '';
this.verifyPass = '';
this.defaultBoard = null;
this.isAdmin = false;
this.passError = false;
this.usernameError = false;
this.isSaving = false;
},
cancel: function() {
$('.userModal').modal('hide');
var that = this;
$('.userModal').on('hidden.bs.modal', function (e) {
that.reset();
});
},
setForSaving: function() {
this.isSaving = true;
this.usernameError = false;
this.passError = false;
},
setAlert: function(user, pass, message) {
this.isSaving = false;
this.usernameError = user;
this.passError = pass;
$scope.alerts.showAlert({ 'type': 'error', 'text': message });
}
};
$scope.$parent.userFormData = $scope.userFormData;
$scope.editUser = function(userFormData) {
userFormData.setForSaving();
if (userFormData.username === '') {
userFormData.setAlert(true, false, 'Username cannot be blank.');
} else {
userFormData.isSaving = true;
if(userFormData.password == userFormData.verifyPass) {
UserService.editUser(userFormData)
.success(function(data) {
$scope.alerts.showAlerts(data.alerts);
$scope.updateUsers(data.data);
$scope.updateBoardsList(data.boards);
if (data.alerts[0].type == 'success') {
$('.userModal').modal('hide');
userFormData.password = '';
userFormData.verifyPass = '';
}
});
} else {
userFormData.setAlert(false, true, 'Passwords do not match.');
}
}
};
$scope.addUser = function(userFormData) {
userFormData.setForSaving();
if (userFormData.username === '') {
userFormData.setAlert(true, false, 'Username cannot be blank.');
} else if (userFormData.password === '' || userFormData.verifyPass === '') {
userFormData.setAlert(false, true, 'Password cannot be blank.');
} else {
userFormData.isSaving = true;
if(userFormData.password == userFormData.verifyPass) {
UserService.addUser(userFormData)
.success(function(data) {
$scope.alerts.showAlerts(data.alerts);
$scope.updateUsers(data.data);
$scope.updateBoardsList(data.boards);
userFormData.reset();
if (data.alerts[0].type == 'success') {
$('.userModal').modal('hide');
}
});
} else {
userFormData.setAlert(false, true, 'Passwords do not match.');
}
}
};
}]);

View File

@ -1,36 +0,0 @@
taskBoardDirectives.directive('clickToEdit', function() {
return {
restrict: 'A',
replace: true,
templateUrl: 'partials/clickToEdit.html',
scope: {
value: '=clickToEdit'
},
controller: ['$scope', function($scope) {
$scope.view = {
editableValue: $scope.value,
editorEnabled: false
};
$scope.enableEditor = function() {
$scope.view.editableValue = $scope.value;
$scope.view.editorEnabled = true;
};
$scope.save = function() {
$scope.value = $scope.view.editableValue;
$scope.view.editorEnabled = false;
};
$scope.checkKeypress = function(e) {
if (e.which === 13) { // Enter key
$scope.save();
e.preventDefault();
} else if (e.which === 27) { // Escape key
$scope.view.editorEnabled = false;
e.preventDefault();
}
};
}]
};
});

View File

@ -1,24 +0,0 @@
taskBoardDirectives.directive('fileUpload', ['$parse', function($parse) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var fileModel = $parse(attrs.fileUpload),
resetModel = $parse(attrs.resetFlag);
// When the resetFlag attribute value changes, reset the input.
scope.$watch(resetModel, function(val) {
if (val) {
element[0].value = '';
resetModel.assign(scope, false);
}
});
// Bind the file to the model on change event.
element.bind('change', function() {
scope.$apply(function() {
fileModel.assign(scope, element[0].files[0]);
});
});
}
};
}]);

View File

@ -1,14 +0,0 @@
taskBoardDirectives.directive('focus', ['$timeout', function($timeout) {
return {
link: function(scope, elem, attrs) {
scope.$watch(attrs.focus, function(val) {
if (angular.isDefined(val) && val) {
$timeout(function() {
elem[0].focus();
scope.$eval(attrs.focus + ' = false');
}, 500);
}
}, true);
}
};
}]);

View File

@ -1,10 +0,0 @@
// Simple directive to include a partial and completely replace the element included from.
taskBoardDirectives.directive('includeReplace', function () {
return {
restrict: 'A',
replace: true,
templateUrl: function(element, attributes) {
return attributes.includeReplace;
}
};
});

View File

@ -1,14 +0,0 @@
// Used for loading third-party JS (like jQueryUI sortable) after
// all elements have been loaded into the DOM.
taskBoardDirectives.directive('onLoadCallback', function() {
return {
restrict: 'A',
terminal: true,
scope: {
callback: '=onLoadCallback'
},
link: function(scope, element, attrs) {
scope.callback();
}
};
});

View File

@ -1,29 +0,0 @@
taskBoardServices.factory('AlertService',[
function() {
var showNotyAlert = function(alert) {
// Assumes noty.js is loaded
noty({
layout: 'bottom',
type: alert.type,
text: alert.text,
timeout: 5000
});
};
return {
showAlert: function(alert) {
if (undefined === alert || null === alert) {
return;
}
showNotyAlert(alert);
},
showAlerts: function(alerts) {
if (undefined === alerts || null === alerts || !alerts.length) {
return;
}
alerts.forEach(function(alert) {
showNotyAlert(alert);
});
}
};
}]);

View File

@ -1,12 +0,0 @@
taskBoardServices.factory('AuthenticationService', [
function() {
return {
isAuthenticated: false,
attemptedRoute: null,
reset: function() {
this.isAuthenticated = false;
this.attemptedRoute = null;
}
};
}]);

View File

@ -1,139 +0,0 @@
taskBoardServices.factory('BoardService',
['$http',
function($http) {
return {
getBoards: function() {
return $http.get('api/boards');
},
removeBoard: function(boardId) {
return $http.post('api/boards/remove', {
boardId: boardId
});
},
addBoard: function(boardData) {
return $http.post('api/boards', {
name: boardData.name,
lanes: boardData.lanes,
categories: boardData.categories,
users: boardData.users
});
},
editBoard: function(boardData) {
return $http.post('api/boards/update', {
boardId: boardData.boardId,
name: boardData.name,
lanes: boardData.lanes,
categories: boardData.categories,
users: boardData.users
});
},
toggleLane: function(laneId) {
return $http.post('api/lanes/' + laneId + '/toggle');
},
toggleActiveState: function(boardId) {
return $http.post('api/boards/' + boardId + '/toggleActive');
},
getAutoActions: function() {
return $http.get('api/autoactions');
},
addAutoAction: function(action) {
return $http.post('api/autoactions', {
boardId: action.board,
triggerId: action.trigger,
secondaryId: action.secondary,
actionId: action.action,
color: action.color,
categoryId: action.category,
assigneeId: action.assignee
});
},
removeAutoAction: function(actionId) {
return $http.post('api/autoactions/remove', {
actionId: actionId
});
},
addItem: function(itemData, boardId) {
return $http.post('api/boards/' + boardId + '/items', {
title: itemData.title,
description: itemData.description,
assignee: itemData.assignee,
category: itemData.category,
color: itemData.color,
dueDate: itemData.dueDate,
points: itemData.points,
lane: itemData.lane
});
},
updateItem: function(itemData) {
return $http.post('api/items/' + itemData.itemId, {
title: itemData.title,
description: itemData.description,
assignee: itemData.assignee,
category: itemData.category,
color: itemData.color,
dueDate: itemData.dueDate,
points: itemData.points,
lane: itemData.lane,
position: itemData.position
});
},
updateItemPositions: function(positionArray) {
return $http.post('api/items/positions', {
positions: positionArray
});
},
removeItem: function(itemId) {
return $http.post('api/items/remove', {
itemId: itemId
});
},
addItemComment: function(itemId, comment) {
return $http.post('api/items/' + itemId + '/comment', {
text: comment
});
},
updateItemComment: function(commentId, comment) {
return $http.post('api/comments/' + commentId, {
text: comment
});
},
removeItemComment: function(itemId, commentId) {
return $http.post('api/items/' + itemId + '/comment/remove', {
id: commentId
});
},
addItemAttachment: function(itemId, file) {
var fd = new FormData();
fd.append('file', file);
return $http.post('api/items/' + itemId + '/upload', fd, {
// Just pass the data, don't serialize.
transformRequest: angular.identity,
// Let browser handle Content-Type.
headers: { 'Content-Type': undefined }
});
},
removeItemAttachment: function(itemId, fileId) {
return $http.post('api/items/' + itemId + '/upload/remove', {
fileId: fileId
});
}
};
}]);

View File

@ -1,35 +0,0 @@
taskBoardServices.factory('TokenInterceptor',
['$q', '$window', '$location', 'AuthenticationService',
function ($q, $window, $location, AuthenticationService) {
return {
request: function(config) {
config.headers = config.headers || {};
if ($window.localStorage.token) {
config.headers.Authorization = $window.localStorage.token;
}
return config;
},
requestError: function(rejection) {
return $q.reject(rejection);
},
response: function(response) {
if (response !== null && response.status === 200 &&
$window.localStorage.token && !AuthenticationService.isAuthenticated) {
AuthenticationService.isAuthenticated = true;
}
return response || $q.when(response);
},
responseError: function(rejection) {
if (rejection !== null && rejection.status === 401 &&
($window.localStorage.token || AuthenticationService.isAuthenticated)) {
delete $window.localStorage.token;
AuthenticationService.isAuthenticated = false;
$location.path('/');
}
return $q.reject(rejection);
}
};
}]);

View File

@ -1,95 +0,0 @@
taskBoardServices.factory('UserService',
['$http',
function($http) {
return {
currentUser: function() {
return $http.get('api/users/current');
},
saveOptions: function(tasksOrder, showAnimations, showAssignee) {
return $http.post('api/users/current/options', {
tasksOrder: tasksOrder,
showAnimations: showAnimations,
showAssignee: showAssignee
});
},
logIn: function(username, password, rememberme) {
return $http.post('api/login', {
username: username,
password: password,
rememberme: rememberme
});
},
logOut: function() {
return $http.get('api/logout');
},
validateToken: function() {
return $http.get('api/authenticate');
},
changePassword: function(currentPassword, newPassword) {
return $http.post('api/updatepassword', {
currentPass: currentPassword,
newPass: newPassword
});
},
changeUsername: function(newUsername) {
return $http.post('api/updateusername', {
newUsername: newUsername
});
},
changeEmail: function(newEmail) {
return $http.post('api/updateemail', {
newEmail: newEmail
});
},
changeDefaultBoard: function(newDefaultBoard) {
return $http.post('api/updateboard', {
defaultBoard: newDefaultBoard
});
},
actions: function() {
return $http.get('api/actions');
},
getUsers: function() {
return $http.get('api/users');
},
addUser: function(formData) {
return $http.post('api/users', {
username: formData.username,
password: formData.password,
email: formData.email,
defaultBoard: formData.defaultBoard,
boardAccess: formData.boardAccess,
isAdmin: formData.isAdmin
});
},
editUser: function(formData) {
return $http.post('api/users/update', {
userId: formData.userId,
newUsername: formData.username,
password: formData.password,
email: formData.email,
defaultBoard: formData.defaultBoard,
boardAccess: formData.boardAccess,
isAdmin: formData.isAdmin
});
},
removeUser: function(userId) {
return $http.post('api/users/remove', {
userId: userId
});
}
};
}]);

36
karma.conf.js Normal file
View File

@ -0,0 +1,36 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
const path = require('path');
process.env.CHROME_BIN = require('puppeteer').executablePath();
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client:{
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: path.join(__dirname, 'coverage/app'),
reports: ['html'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['ChromeHeadless'],
singleRun: false,
restartOnFileChange: true
});
};

989
lib/angular-route.js vendored
View File

@ -1,989 +0,0 @@
/**
* @license AngularJS v1.3.15
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {'use strict';
/**
* @ngdoc module
* @name ngRoute
* @description
*
* # ngRoute
*
* The `ngRoute` module provides routing and deeplinking services and directives for angular apps.
*
* ## Example
* See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
*
*
* <div doc-module-components="ngRoute"></div>
*/
/* global -ngRouteModule */
var ngRouteModule = angular.module('ngRoute', ['ng']).
provider('$route', $RouteProvider),
$routeMinErr = angular.$$minErr('ngRoute');
/**
* @ngdoc provider
* @name $routeProvider
*
* @description
*
* Used for configuring routes.
*
* ## Example
* See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
*
* ## Dependencies
* Requires the {@link ngRoute `ngRoute`} module to be installed.
*/
function $RouteProvider() {
function inherit(parent, extra) {
return angular.extend(Object.create(parent), extra);
}
var routes = {};
/**
* @ngdoc method
* @name $routeProvider#when
*
* @param {string} path Route path (matched against `$location.path`). If `$location.path`
* contains redundant trailing slash or is missing one, the route will still match and the
* `$location.path` will be updated to add or drop the trailing slash to exactly match the
* route definition.
*
* * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up
* to the next slash are matched and stored in `$routeParams` under the given `name`
* when the route matches.
* * `path` can contain named groups starting with a colon and ending with a star:
* e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name`
* when the route matches.
* * `path` can contain optional named groups with a question mark: e.g.`:name?`.
*
* For example, routes like `/color/:color/largecode/:largecode*\/edit` will match
* `/color/brown/largecode/code/with/slashes/edit` and extract:
*
* * `color: brown`
* * `largecode: code/with/slashes`.
*
*
* @param {Object} route Mapping information to be assigned to `$route.current` on route
* match.
*
* Object properties:
*
* - `controller` `{(string|function()=}` Controller fn that should be associated with
* newly created scope or the name of a {@link angular.Module#controller registered
* controller} if passed as a string.
* - `controllerAs` `{string=}` A controller alias name. If present the controller will be
* published to scope under the `controllerAs` name.
* - `template` `{string=|function()=}` html template as a string or a function that
* returns an html template as a string which should be used by {@link
* ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives.
* This property takes precedence over `templateUrl`.
*
* If `template` is a function, it will be called with the following parameters:
*
* - `{Array.<Object>}` - route parameters extracted from the current
* `$location.path()` by applying the current route
*
* - `templateUrl` `{string=|function()=}` path or function that returns a path to an html
* template that should be used by {@link ngRoute.directive:ngView ngView}.
*
* If `templateUrl` is a function, it will be called with the following parameters:
*
* - `{Array.<Object>}` - route parameters extracted from the current
* `$location.path()` by applying the current route
*
* - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
* be injected into the controller. If any of these dependencies are promises, the router
* will wait for them all to be resolved or one to be rejected before the controller is
* instantiated.
* If all the promises are resolved successfully, the values of the resolved promises are
* injected and {@link ngRoute.$route#$routeChangeSuccess $routeChangeSuccess} event is
* fired. If any of the promises are rejected the
* {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired. The map object
* is:
*
* - `key` `{string}`: a name of a dependency to be injected into the controller.
* - `factory` - `{string|function}`: If `string` then it is an alias for a service.
* Otherwise if function, then it is {@link auto.$injector#invoke injected}
* and the return value is treated as the dependency. If the result is a promise, it is
* resolved before its value is injected into the controller. Be aware that
* `ngRoute.$routeParams` will still refer to the previous route within these resolve
* functions. Use `$route.current.params` to access the new route parameters, instead.
*
* - `redirectTo` {(string|function())=} value to update
* {@link ng.$location $location} path with and trigger route redirection.
*
* If `redirectTo` is a function, it will be called with the following parameters:
*
* - `{Object.<string>}` - route parameters extracted from the current
* `$location.path()` by applying the current route templateUrl.
* - `{string}` - current `$location.path()`
* - `{Object}` - current `$location.search()`
*
* The custom `redirectTo` function is expected to return a string which will be used
* to update `$location.path()` and `$location.search()`.
*
* - `[reloadOnSearch=true]` - {boolean=} - reload route when only `$location.search()`
* or `$location.hash()` changes.
*
* If the option is set to `false` and url in the browser changes, then
* `$routeUpdate` event is broadcasted on the root scope.
*
* - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive
*
* If the option is set to `true`, then the particular route can be matched without being
* case sensitive
*
* @returns {Object} self
*
* @description
* Adds a new route definition to the `$route` service.
*/
this.when = function(path, route) {
//copy original route object to preserve params inherited from proto chain
var routeCopy = angular.copy(route);
if (angular.isUndefined(routeCopy.reloadOnSearch)) {
routeCopy.reloadOnSearch = true;
}
if (angular.isUndefined(routeCopy.caseInsensitiveMatch)) {
routeCopy.caseInsensitiveMatch = this.caseInsensitiveMatch;
}
routes[path] = angular.extend(
routeCopy,
path && pathRegExp(path, routeCopy)
);
// create redirection for trailing slashes
if (path) {
var redirectPath = (path[path.length - 1] == '/')
? path.substr(0, path.length - 1)
: path + '/';
routes[redirectPath] = angular.extend(
{redirectTo: path},
pathRegExp(redirectPath, routeCopy)
);
}
return this;
};
/**
* @ngdoc property
* @name $routeProvider#caseInsensitiveMatch
* @description
*
* A boolean property indicating if routes defined
* using this provider should be matched using a case insensitive
* algorithm. Defaults to `false`.
*/
this.caseInsensitiveMatch = false;
/**
* @param path {string} path
* @param opts {Object} options
* @return {?Object}
*
* @description
* Normalizes the given path, returning a regular expression
* and the original path.
*
* Inspired by pathRexp in visionmedia/express/lib/utils.js.
*/
function pathRegExp(path, opts) {
var insensitive = opts.caseInsensitiveMatch,
ret = {
originalPath: path,
regexp: path
},
keys = ret.keys = [];
path = path
.replace(/([().])/g, '\\$1')
.replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option) {
var optional = option === '?' ? option : null;
var star = option === '*' ? option : null;
keys.push({ name: key, optional: !!optional });
slash = slash || '';
return ''
+ (optional ? '' : slash)
+ '(?:'
+ (optional ? slash : '')
+ (star && '(.+?)' || '([^/]+)')
+ (optional || '')
+ ')'
+ (optional || '');
})
.replace(/([\/$\*])/g, '\\$1');
ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : '');
return ret;
}
/**
* @ngdoc method
* @name $routeProvider#otherwise
*
* @description
* Sets route definition that will be used on route change when no other route definition
* is matched.
*
* @param {Object|string} params Mapping information to be assigned to `$route.current`.
* If called with a string, the value maps to `redirectTo`.
* @returns {Object} self
*/
this.otherwise = function(params) {
if (typeof params === 'string') {
params = {redirectTo: params};
}
this.when(null, params);
return this;
};
this.$get = ['$rootScope',
'$location',
'$routeParams',
'$q',
'$injector',
'$templateRequest',
'$sce',
function($rootScope, $location, $routeParams, $q, $injector, $templateRequest, $sce) {
/**
* @ngdoc service
* @name $route
* @requires $location
* @requires $routeParams
*
* @property {Object} current Reference to the current route definition.
* The route definition contains:
*
* - `controller`: The controller constructor as define in route definition.
* - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for
* controller instantiation. The `locals` contain
* the resolved values of the `resolve` map. Additionally the `locals` also contain:
*
* - `$scope` - The current route scope.
* - `$template` - The current route template HTML.
*
* @property {Object} routes Object with all route configuration Objects as its properties.
*
* @description
* `$route` is used for deep-linking URLs to controllers and views (HTML partials).
* It watches `$location.url()` and tries to map the path to an existing route definition.
*
* Requires the {@link ngRoute `ngRoute`} module to be installed.
*
* You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API.
*
* The `$route` service is typically used in conjunction with the
* {@link ngRoute.directive:ngView `ngView`} directive and the
* {@link ngRoute.$routeParams `$routeParams`} service.
*
* @example
* This example shows how changing the URL hash causes the `$route` to match a route against the
* URL, and the `ngView` pulls in the partial.
*
* <example name="$route-service" module="ngRouteExample"
* deps="angular-route.js" fixBase="true">
* <file name="index.html">
* <div ng-controller="MainController">
* Choose:
* <a href="Book/Moby">Moby</a> |
* <a href="Book/Moby/ch/1">Moby: Ch1</a> |
* <a href="Book/Gatsby">Gatsby</a> |
* <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
* <a href="Book/Scarlet">Scarlet Letter</a><br/>
*
* <div ng-view></div>
*
* <hr />
*
* <pre>$location.path() = {{$location.path()}}</pre>
* <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
* <pre>$route.current.params = {{$route.current.params}}</pre>
* <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
* <pre>$routeParams = {{$routeParams}}</pre>
* </div>
* </file>
*
* <file name="book.html">
* controller: {{name}}<br />
* Book Id: {{params.bookId}}<br />
* </file>
*
* <file name="chapter.html">
* controller: {{name}}<br />
* Book Id: {{params.bookId}}<br />
* Chapter Id: {{params.chapterId}}
* </file>
*
* <file name="script.js">
* angular.module('ngRouteExample', ['ngRoute'])
*
* .controller('MainController', function($scope, $route, $routeParams, $location) {
* $scope.$route = $route;
* $scope.$location = $location;
* $scope.$routeParams = $routeParams;
* })
*
* .controller('BookController', function($scope, $routeParams) {
* $scope.name = "BookController";
* $scope.params = $routeParams;
* })
*
* .controller('ChapterController', function($scope, $routeParams) {
* $scope.name = "ChapterController";
* $scope.params = $routeParams;
* })
*
* .config(function($routeProvider, $locationProvider) {
* $routeProvider
* .when('/Book/:bookId', {
* templateUrl: 'book.html',
* controller: 'BookController',
* resolve: {
* // I will cause a 1 second delay
* delay: function($q, $timeout) {
* var delay = $q.defer();
* $timeout(delay.resolve, 1000);
* return delay.promise;
* }
* }
* })
* .when('/Book/:bookId/ch/:chapterId', {
* templateUrl: 'chapter.html',
* controller: 'ChapterController'
* });
*
* // configure html5 to get links working on jsfiddle
* $locationProvider.html5Mode(true);
* });
*
* </file>
*
* <file name="protractor.js" type="protractor">
* it('should load and compile correct template', function() {
* element(by.linkText('Moby: Ch1')).click();
* var content = element(by.css('[ng-view]')).getText();
* expect(content).toMatch(/controller\: ChapterController/);
* expect(content).toMatch(/Book Id\: Moby/);
* expect(content).toMatch(/Chapter Id\: 1/);
*
* element(by.partialLinkText('Scarlet')).click();
*
* content = element(by.css('[ng-view]')).getText();
* expect(content).toMatch(/controller\: BookController/);
* expect(content).toMatch(/Book Id\: Scarlet/);
* });
* </file>
* </example>
*/
/**
* @ngdoc event
* @name $route#$routeChangeStart
* @eventType broadcast on root scope
* @description
* Broadcasted before a route change. At this point the route services starts
* resolving all of the dependencies needed for the route change to occur.
* Typically this involves fetching the view template as well as any dependencies
* defined in `resolve` route property. Once all of the dependencies are resolved
* `$routeChangeSuccess` is fired.
*
* The route change (and the `$location` change that triggered it) can be prevented
* by calling `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on}
* for more details about event object.
*
* @param {Object} angularEvent Synthetic event object.
* @param {Route} next Future route information.
* @param {Route} current Current route information.
*/
/**
* @ngdoc event
* @name $route#$routeChangeSuccess
* @eventType broadcast on root scope
* @description
* Broadcasted after a route dependencies are resolved.
* {@link ngRoute.directive:ngView ngView} listens for the directive
* to instantiate the controller and render the view.
*
* @param {Object} angularEvent Synthetic event object.
* @param {Route} current Current route information.
* @param {Route|Undefined} previous Previous route information, or undefined if current is
* first route entered.
*/
/**
* @ngdoc event
* @name $route#$routeChangeError
* @eventType broadcast on root scope
* @description
* Broadcasted if any of the resolve promises are rejected.
*
* @param {Object} angularEvent Synthetic event object
* @param {Route} current Current route information.
* @param {Route} previous Previous route information.
* @param {Route} rejection Rejection of the promise. Usually the error of the failed promise.
*/
/**
* @ngdoc event
* @name $route#$routeUpdate
* @eventType broadcast on root scope
* @description
*
* The `reloadOnSearch` property has been set to false, and we are reusing the same
* instance of the Controller.
*/
var forceReload = false,
preparedRoute,
preparedRouteIsUpdateOnly,
$route = {
routes: routes,
/**
* @ngdoc method
* @name $route#reload
*
* @description
* Causes `$route` service to reload the current route even if
* {@link ng.$location $location} hasn't changed.
*
* As a result of that, {@link ngRoute.directive:ngView ngView}
* creates new scope and reinstantiates the controller.
*/
reload: function() {
forceReload = true;
$rootScope.$evalAsync(function() {
// Don't support cancellation of a reload for now...
prepareRoute();
commitRoute();
});
},
/**
* @ngdoc method
* @name $route#updateParams
*
* @description
* Causes `$route` service to update the current URL, replacing
* current route parameters with those specified in `newParams`.
* Provided property names that match the route's path segment
* definitions will be interpolated into the location's path, while
* remaining properties will be treated as query params.
*
* @param {!Object<string, string>} newParams mapping of URL parameter names to values
*/
updateParams: function(newParams) {
if (this.current && this.current.$$route) {
newParams = angular.extend({}, this.current.params, newParams);
$location.path(interpolate(this.current.$$route.originalPath, newParams));
// interpolate modifies newParams, only query params are left
$location.search(newParams);
} else {
throw $routeMinErr('norout', 'Tried updating route when with no current route');
}
}
};
$rootScope.$on('$locationChangeStart', prepareRoute);
$rootScope.$on('$locationChangeSuccess', commitRoute);
return $route;
/////////////////////////////////////////////////////
/**
* @param on {string} current url
* @param route {Object} route regexp to match the url against
* @return {?Object}
*
* @description
* Check if the route matches the current url.
*
* Inspired by match in
* visionmedia/express/lib/router/router.js.
*/
function switchRouteMatcher(on, route) {
var keys = route.keys,
params = {};
if (!route.regexp) return null;
var m = route.regexp.exec(on);
if (!m) return null;
for (var i = 1, len = m.length; i < len; ++i) {
var key = keys[i - 1];
var val = m[i];
if (key && val) {
params[key.name] = val;
}
}
return params;
}
function prepareRoute($locationEvent) {
var lastRoute = $route.current;
preparedRoute = parseRoute();
preparedRouteIsUpdateOnly = preparedRoute && lastRoute && preparedRoute.$$route === lastRoute.$$route
&& angular.equals(preparedRoute.pathParams, lastRoute.pathParams)
&& !preparedRoute.reloadOnSearch && !forceReload;
if (!preparedRouteIsUpdateOnly && (lastRoute || preparedRoute)) {
if ($rootScope.$broadcast('$routeChangeStart', preparedRoute, lastRoute).defaultPrevented) {
if ($locationEvent) {
$locationEvent.preventDefault();
}
}
}
}
function commitRoute() {
var lastRoute = $route.current;
var nextRoute = preparedRoute;
if (preparedRouteIsUpdateOnly) {
lastRoute.params = nextRoute.params;
angular.copy(lastRoute.params, $routeParams);
$rootScope.$broadcast('$routeUpdate', lastRoute);
} else if (nextRoute || lastRoute) {
forceReload = false;
$route.current = nextRoute;
if (nextRoute) {
if (nextRoute.redirectTo) {
if (angular.isString(nextRoute.redirectTo)) {
$location.path(interpolate(nextRoute.redirectTo, nextRoute.params)).search(nextRoute.params)
.replace();
} else {
$location.url(nextRoute.redirectTo(nextRoute.pathParams, $location.path(), $location.search()))
.replace();
}
}
}
$q.when(nextRoute).
then(function() {
if (nextRoute) {
var locals = angular.extend({}, nextRoute.resolve),
template, templateUrl;
angular.forEach(locals, function(value, key) {
locals[key] = angular.isString(value) ?
$injector.get(value) : $injector.invoke(value, null, null, key);
});
if (angular.isDefined(template = nextRoute.template)) {
if (angular.isFunction(template)) {
template = template(nextRoute.params);
}
} else if (angular.isDefined(templateUrl = nextRoute.templateUrl)) {
if (angular.isFunction(templateUrl)) {
templateUrl = templateUrl(nextRoute.params);
}
templateUrl = $sce.getTrustedResourceUrl(templateUrl);
if (angular.isDefined(templateUrl)) {
nextRoute.loadedTemplateUrl = templateUrl;
template = $templateRequest(templateUrl);
}
}
if (angular.isDefined(template)) {
locals['$template'] = template;
}
return $q.all(locals);
}
}).
// after route change
then(function(locals) {
if (nextRoute == $route.current) {
if (nextRoute) {
nextRoute.locals = locals;
angular.copy(nextRoute.params, $routeParams);
}
$rootScope.$broadcast('$routeChangeSuccess', nextRoute, lastRoute);
}
}, function(error) {
if (nextRoute == $route.current) {
$rootScope.$broadcast('$routeChangeError', nextRoute, lastRoute, error);
}
});
}
}
/**
* @returns {Object} the current active route, by matching it against the URL
*/
function parseRoute() {
// Match a route
var params, match;
angular.forEach(routes, function(route, path) {
if (!match && (params = switchRouteMatcher($location.path(), route))) {
match = inherit(route, {
params: angular.extend({}, $location.search(), params),
pathParams: params});
match.$$route = route;
}
});
// No route matched; fallback to "otherwise" route
return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}});
}
/**
* @returns {string} interpolation of the redirect path with the parameters
*/
function interpolate(string, params) {
var result = [];
angular.forEach((string || '').split(':'), function(segment, i) {
if (i === 0) {
result.push(segment);
} else {
var segmentMatch = segment.match(/(\w+)(?:[?*])?(.*)/);
var key = segmentMatch[1];
result.push(params[key]);
result.push(segmentMatch[2] || '');
delete params[key];
}
});
return result.join('');
}
}];
}
ngRouteModule.provider('$routeParams', $RouteParamsProvider);
/**
* @ngdoc service
* @name $routeParams
* @requires $route
*
* @description
* The `$routeParams` service allows you to retrieve the current set of route parameters.
*
* Requires the {@link ngRoute `ngRoute`} module to be installed.
*
* The route parameters are a combination of {@link ng.$location `$location`}'s
* {@link ng.$location#search `search()`} and {@link ng.$location#path `path()`}.
* The `path` parameters are extracted when the {@link ngRoute.$route `$route`} path is matched.
*
* In case of parameter name collision, `path` params take precedence over `search` params.
*
* The service guarantees that the identity of the `$routeParams` object will remain unchanged
* (but its properties will likely change) even when a route change occurs.
*
* Note that the `$routeParams` are only updated *after* a route change completes successfully.
* This means that you cannot rely on `$routeParams` being correct in route resolve functions.
* Instead you can use `$route.current.params` to access the new route's parameters.
*
* @example
* ```js
* // Given:
* // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
* // Route: /Chapter/:chapterId/Section/:sectionId
* //
* // Then
* $routeParams ==> {chapterId:'1', sectionId:'2', search:'moby'}
* ```
*/
function $RouteParamsProvider() {
this.$get = function() { return {}; };
}
ngRouteModule.directive('ngView', ngViewFactory);
ngRouteModule.directive('ngView', ngViewFillContentFactory);
/**
* @ngdoc directive
* @name ngView
* @restrict ECA
*
* @description
* # Overview
* `ngView` is a directive that complements the {@link ngRoute.$route $route} service by
* including the rendered template of the current route into the main layout (`index.html`) file.
* Every time the current route changes, the included view changes with it according to the
* configuration of the `$route` service.
*
* Requires the {@link ngRoute `ngRoute`} module to be installed.
*
* @animations
* enter - animation is used to bring new content into the browser.
* leave - animation is used to animate existing content away.
*
* The enter and leave animation occur concurrently.
*
* @scope
* @priority 400
* @param {string=} onload Expression to evaluate whenever the view updates.
*
* @param {string=} autoscroll Whether `ngView` should call {@link ng.$anchorScroll
* $anchorScroll} to scroll the viewport after the view is updated.
*
* - If the attribute is not set, disable scrolling.
* - If the attribute is set without value, enable scrolling.
* - Otherwise enable scrolling only if the `autoscroll` attribute value evaluated
* as an expression yields a truthy value.
* @example
<example name="ngView-directive" module="ngViewExample"
deps="angular-route.js;angular-animate.js"
animations="true" fixBase="true">
<file name="index.html">
<div ng-controller="MainCtrl as main">
Choose:
<a href="Book/Moby">Moby</a> |
<a href="Book/Moby/ch/1">Moby: Ch1</a> |
<a href="Book/Gatsby">Gatsby</a> |
<a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
<a href="Book/Scarlet">Scarlet Letter</a><br/>
<div class="view-animate-container">
<div ng-view class="view-animate"></div>
</div>
<hr />
<pre>$location.path() = {{main.$location.path()}}</pre>
<pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre>
<pre>$route.current.params = {{main.$route.current.params}}</pre>
<pre>$routeParams = {{main.$routeParams}}</pre>
</div>
</file>
<file name="book.html">
<div>
controller: {{book.name}}<br />
Book Id: {{book.params.bookId}}<br />
</div>
</file>
<file name="chapter.html">
<div>
controller: {{chapter.name}}<br />
Book Id: {{chapter.params.bookId}}<br />
Chapter Id: {{chapter.params.chapterId}}
</div>
</file>
<file name="animations.css">
.view-animate-container {
position:relative;
height:100px!important;
background:white;
border:1px solid black;
height:40px;
overflow:hidden;
}
.view-animate {
padding:10px;
}
.view-animate.ng-enter, .view-animate.ng-leave {
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
display:block;
width:100%;
border-left:1px solid black;
position:absolute;
top:0;
left:0;
right:0;
bottom:0;
padding:10px;
}
.view-animate.ng-enter {
left:100%;
}
.view-animate.ng-enter.ng-enter-active {
left:0;
}
.view-animate.ng-leave.ng-leave-active {
left:-100%;
}
</file>
<file name="script.js">
angular.module('ngViewExample', ['ngRoute', 'ngAnimate'])
.config(['$routeProvider', '$locationProvider',
function($routeProvider, $locationProvider) {
$routeProvider
.when('/Book/:bookId', {
templateUrl: 'book.html',
controller: 'BookCtrl',
controllerAs: 'book'
})
.when('/Book/:bookId/ch/:chapterId', {
templateUrl: 'chapter.html',
controller: 'ChapterCtrl',
controllerAs: 'chapter'
});
$locationProvider.html5Mode(true);
}])
.controller('MainCtrl', ['$route', '$routeParams', '$location',
function($route, $routeParams, $location) {
this.$route = $route;
this.$location = $location;
this.$routeParams = $routeParams;
}])
.controller('BookCtrl', ['$routeParams', function($routeParams) {
this.name = "BookCtrl";
this.params = $routeParams;
}])
.controller('ChapterCtrl', ['$routeParams', function($routeParams) {
this.name = "ChapterCtrl";
this.params = $routeParams;
}]);
</file>
<file name="protractor.js" type="protractor">
it('should load and compile correct template', function() {
element(by.linkText('Moby: Ch1')).click();
var content = element(by.css('[ng-view]')).getText();
expect(content).toMatch(/controller\: ChapterCtrl/);
expect(content).toMatch(/Book Id\: Moby/);
expect(content).toMatch(/Chapter Id\: 1/);
element(by.partialLinkText('Scarlet')).click();
content = element(by.css('[ng-view]')).getText();
expect(content).toMatch(/controller\: BookCtrl/);
expect(content).toMatch(/Book Id\: Scarlet/);
});
</file>
</example>
*/
/**
* @ngdoc event
* @name ngView#$viewContentLoaded
* @eventType emit on the current ngView scope
* @description
* Emitted every time the ngView content is reloaded.
*/
ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate'];
function ngViewFactory($route, $anchorScroll, $animate) {
return {
restrict: 'ECA',
terminal: true,
priority: 400,
transclude: 'element',
link: function(scope, $element, attr, ctrl, $transclude) {
var currentScope,
currentElement,
previousLeaveAnimation,
autoScrollExp = attr.autoscroll,
onloadExp = attr.onload || '';
scope.$on('$routeChangeSuccess', update);
update();
function cleanupLastView() {
if (previousLeaveAnimation) {
$animate.cancel(previousLeaveAnimation);
previousLeaveAnimation = null;
}
if (currentScope) {
currentScope.$destroy();
currentScope = null;
}
if (currentElement) {
previousLeaveAnimation = $animate.leave(currentElement);
previousLeaveAnimation.then(function() {
previousLeaveAnimation = null;
});
currentElement = null;
}
}
function update() {
var locals = $route.current && $route.current.locals,
template = locals && locals.$template;
if (angular.isDefined(template)) {
var newScope = scope.$new();
var current = $route.current;
// Note: This will also link all children of ng-view that were contained in the original
// html. If that content contains controllers, ... they could pollute/change the scope.
// However, using ng-view on an element with additional content does not make sense...
// Note: We can't remove them in the cloneAttchFn of $transclude as that
// function is called before linking the content, which would apply child
// directives to non existing elements.
var clone = $transclude(newScope, function(clone) {
$animate.enter(clone, null, currentElement || $element).then(function onNgViewEnter() {
if (angular.isDefined(autoScrollExp)
&& (!autoScrollExp || scope.$eval(autoScrollExp))) {
$anchorScroll();
}
});
cleanupLastView();
});
currentElement = clone;
currentScope = current.scope = newScope;
currentScope.$emit('$viewContentLoaded');
currentScope.$eval(onloadExp);
} else {
cleanupLastView();
}
}
}
};
}
// This directive is called during the $transclude call of the first `ngView` directive.
// It will replace and compile the content of the element with the loaded template.
// We need this directive so that the element content is already filled when
// the link function of another directive on the same element as ngView
// is called.
ngViewFillContentFactory.$inject = ['$compile', '$controller', '$route'];
function ngViewFillContentFactory($compile, $controller, $route) {
return {
restrict: 'ECA',
priority: -400,
link: function(scope, $element) {
var current = $route.current,
locals = current.locals;
$element.html(locals.$template);
var link = $compile($element.contents());
if (current.controller) {
locals.$scope = scope;
var controller = $controller(current.controller, locals);
if (current.controllerAs) {
scope[current.controllerAs] = controller;
}
$element.data('$ngControllerController', controller);
$element.children().data('$ngControllerController', controller);
}
link(scope);
}
};
}
})(window, window.angular);

View File

@ -1,15 +0,0 @@
/*
AngularJS v1.3.15
(c) 2010-2014 Google, Inc. http://angularjs.org
License: MIT
*/
(function(q,d,C){'use strict';function v(r,k,h){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(a,f,b,c,y){function z(){l&&(h.cancel(l),l=null);m&&(m.$destroy(),m=null);n&&(l=h.leave(n),l.then(function(){l=null}),n=null)}function x(){var b=r.current&&r.current.locals;if(d.isDefined(b&&b.$template)){var b=a.$new(),c=r.current;n=y(b,function(b){h.enter(b,null,n||f).then(function(){!d.isDefined(t)||t&&!a.$eval(t)||k()});z()});m=c.scope=b;m.$emit("$viewContentLoaded");
m.$eval(w)}else z()}var m,n,l,t=b.autoscroll,w=b.onload||"";a.$on("$routeChangeSuccess",x);x()}}}function A(d,k,h){return{restrict:"ECA",priority:-400,link:function(a,f){var b=h.current,c=b.locals;f.html(c.$template);var y=d(f.contents());b.controller&&(c.$scope=a,c=k(b.controller,c),b.controllerAs&&(a[b.controllerAs]=c),f.data("$ngControllerController",c),f.children().data("$ngControllerController",c));y(a)}}}q=d.module("ngRoute",["ng"]).provider("$route",function(){function r(a,f){return d.extend(Object.create(a),
f)}function k(a,d){var b=d.caseInsensitiveMatch,c={originalPath:a,regexp:a},h=c.keys=[];a=a.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?\*])?/g,function(a,d,b,c){a="?"===c?c:null;c="*"===c?c:null;h.push({name:b,optional:!!a});d=d||"";return""+(a?"":d)+"(?:"+(a?d:"")+(c&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");c.regexp=new RegExp("^"+a+"$",b?"i":"");return c}var h={};this.when=function(a,f){var b=d.copy(f);d.isUndefined(b.reloadOnSearch)&&(b.reloadOnSearch=!0);
d.isUndefined(b.caseInsensitiveMatch)&&(b.caseInsensitiveMatch=this.caseInsensitiveMatch);h[a]=d.extend(b,a&&k(a,b));if(a){var c="/"==a[a.length-1]?a.substr(0,a.length-1):a+"/";h[c]=d.extend({redirectTo:a},k(c,b))}return this};this.caseInsensitiveMatch=!1;this.otherwise=function(a){"string"===typeof a&&(a={redirectTo:a});this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$templateRequest","$sce",function(a,f,b,c,k,q,x){function m(b){var e=s.current;
(v=(p=l())&&e&&p.$$route===e.$$route&&d.equals(p.pathParams,e.pathParams)&&!p.reloadOnSearch&&!w)||!e&&!p||a.$broadcast("$routeChangeStart",p,e).defaultPrevented&&b&&b.preventDefault()}function n(){var u=s.current,e=p;if(v)u.params=e.params,d.copy(u.params,b),a.$broadcast("$routeUpdate",u);else if(e||u)w=!1,(s.current=e)&&e.redirectTo&&(d.isString(e.redirectTo)?f.path(t(e.redirectTo,e.params)).search(e.params).replace():f.url(e.redirectTo(e.pathParams,f.path(),f.search())).replace()),c.when(e).then(function(){if(e){var a=
d.extend({},e.resolve),b,g;d.forEach(a,function(b,e){a[e]=d.isString(b)?k.get(b):k.invoke(b,null,null,e)});d.isDefined(b=e.template)?d.isFunction(b)&&(b=b(e.params)):d.isDefined(g=e.templateUrl)&&(d.isFunction(g)&&(g=g(e.params)),g=x.getTrustedResourceUrl(g),d.isDefined(g)&&(e.loadedTemplateUrl=g,b=q(g)));d.isDefined(b)&&(a.$template=b);return c.all(a)}}).then(function(c){e==s.current&&(e&&(e.locals=c,d.copy(e.params,b)),a.$broadcast("$routeChangeSuccess",e,u))},function(b){e==s.current&&a.$broadcast("$routeChangeError",
e,u,b)})}function l(){var a,b;d.forEach(h,function(c,h){var g;if(g=!b){var k=f.path();g=c.keys;var m={};if(c.regexp)if(k=c.regexp.exec(k)){for(var l=1,n=k.length;l<n;++l){var p=g[l-1],q=k[l];p&&q&&(m[p.name]=q)}g=m}else g=null;else g=null;g=a=g}g&&(b=r(c,{params:d.extend({},f.search(),a),pathParams:a}),b.$$route=c)});return b||h[null]&&r(h[null],{params:{},pathParams:{}})}function t(a,b){var c=[];d.forEach((a||"").split(":"),function(a,d){if(0===d)c.push(a);else{var f=a.match(/(\w+)(?:[?*])?(.*)/),
h=f[1];c.push(b[h]);c.push(f[2]||"");delete b[h]}});return c.join("")}var w=!1,p,v,s={routes:h,reload:function(){w=!0;a.$evalAsync(function(){m();n()})},updateParams:function(a){if(this.current&&this.current.$$route)a=d.extend({},this.current.params,a),f.path(t(this.current.$$route.originalPath,a)),f.search(a);else throw B("norout");}};a.$on("$locationChangeStart",m);a.$on("$locationChangeSuccess",n);return s}]});var B=d.$$minErr("ngRoute");q.provider("$routeParams",function(){this.$get=function(){return{}}});
q.directive("ngView",v);q.directive("ngView",A);v.$inject=["$route","$anchorScroll","$animate"];A.$inject=["$compile","$controller","$route"]})(window,window.angular);
//# sourceMappingURL=angular-route.min.js.map

View File

@ -1,679 +0,0 @@
/**
* @license AngularJS v1.3.15
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {'use strict';
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Any commits to this file should be reviewed with security in mind. *
* Changes to this file can potentially create security vulnerabilities. *
* An approval from 2 Core members with history of modifying *
* this file is required. *
* *
* Does the change somehow allow for arbitrary javascript to be executed? *
* Or allows for someone to change the prototype of built-in objects? *
* Or gives undesired access to variables likes document or window? *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
var $sanitizeMinErr = angular.$$minErr('$sanitize');
/**
* @ngdoc module
* @name ngSanitize
* @description
*
* # ngSanitize
*
* The `ngSanitize` module provides functionality to sanitize HTML.
*
*
* <div doc-module-components="ngSanitize"></div>
*
* See {@link ngSanitize.$sanitize `$sanitize`} for usage.
*/
/*
* HTML Parser By Misko Hevery (misko@hevery.com)
* based on: HTML Parser By John Resig (ejohn.org)
* Original code by Erik Arvidsson, Mozilla Public License
* http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
*
* // Use like so:
* htmlParser(htmlString, {
* start: function(tag, attrs, unary) {},
* end: function(tag) {},
* chars: function(text) {},
* comment: function(text) {}
* });
*
*/
/**
* @ngdoc service
* @name $sanitize
* @kind function
*
* @description
* The input is sanitized by parsing the HTML into tokens. All safe tokens (from a whitelist) are
* then serialized back to properly escaped html string. This means that no unsafe input can make
* it into the returned string, however, since our parser is more strict than a typical browser
* parser, it's possible that some obscure input, which would be recognized as valid HTML by a
* browser, won't make it through the sanitizer. The input may also contain SVG markup.
* The whitelist is configured using the functions `aHrefSanitizationWhitelist` and
* `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider `$compileProvider`}.
*
* @param {string} html HTML input.
* @returns {string} Sanitized HTML.
*
* @example
<example module="sanitizeExample" deps="angular-sanitize.js">
<file name="index.html">
<script>
angular.module('sanitizeExample', ['ngSanitize'])
.controller('ExampleController', ['$scope', '$sce', function($scope, $sce) {
$scope.snippet =
'<p style="color:blue">an html\n' +
'<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
'snippet</p>';
$scope.deliberatelyTrustDangerousSnippet = function() {
return $sce.trustAsHtml($scope.snippet);
};
}]);
</script>
<div ng-controller="ExampleController">
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
<table>
<tr>
<td>Directive</td>
<td>How</td>
<td>Source</td>
<td>Rendered</td>
</tr>
<tr id="bind-html-with-sanitize">
<td>ng-bind-html</td>
<td>Automatically uses $sanitize</td>
<td><pre>&lt;div ng-bind-html="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
<td><div ng-bind-html="snippet"></div></td>
</tr>
<tr id="bind-html-with-trust">
<td>ng-bind-html</td>
<td>Bypass $sanitize by explicitly trusting the dangerous value</td>
<td>
<pre>&lt;div ng-bind-html="deliberatelyTrustDangerousSnippet()"&gt;
&lt;/div&gt;</pre>
</td>
<td><div ng-bind-html="deliberatelyTrustDangerousSnippet()"></div></td>
</tr>
<tr id="bind-default">
<td>ng-bind</td>
<td>Automatically escapes</td>
<td><pre>&lt;div ng-bind="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
<td><div ng-bind="snippet"></div></td>
</tr>
</table>
</div>
</file>
<file name="protractor.js" type="protractor">
it('should sanitize the html snippet by default', function() {
expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
});
it('should inline raw snippet if bound to a trusted value', function() {
expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).
toBe("<p style=\"color:blue\">an html\n" +
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
"snippet</p>");
});
it('should escape snippet without any filter', function() {
expect(element(by.css('#bind-default div')).getInnerHtml()).
toBe("&lt;p style=\"color:blue\"&gt;an html\n" +
"&lt;em onmouseover=\"this.textContent='PWN3D!'\"&gt;click here&lt;/em&gt;\n" +
"snippet&lt;/p&gt;");
});
it('should update', function() {
element(by.model('snippet')).clear();
element(by.model('snippet')).sendKeys('new <b onclick="alert(1)">text</b>');
expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
toBe('new <b>text</b>');
expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).toBe(
'new <b onclick="alert(1)">text</b>');
expect(element(by.css('#bind-default div')).getInnerHtml()).toBe(
"new &lt;b onclick=\"alert(1)\"&gt;text&lt;/b&gt;");
});
</file>
</example>
*/
function $SanitizeProvider() {
this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
return function(html) {
var buf = [];
htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) {
return !/^unsafe/.test($$sanitizeUri(uri, isImage));
}));
return buf.join('');
};
}];
}
function sanitizeText(chars) {
var buf = [];
var writer = htmlSanitizeWriter(buf, angular.noop);
writer.chars(chars);
return buf.join('');
}
// Regular Expressions for parsing tags and attributes
var START_TAG_REGEXP =
/^<((?:[a-zA-Z])[\w:-]*)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*(>?)/,
END_TAG_REGEXP = /^<\/\s*([\w:-]+)[^>]*>/,
ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,
BEGIN_TAG_REGEXP = /^</,
BEGING_END_TAGE_REGEXP = /^<\//,
COMMENT_REGEXP = /<!--(.*?)-->/g,
DOCTYPE_REGEXP = /<!DOCTYPE([^>]*?)>/i,
CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
// Match everything outside of normal chars and " (quote character)
NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;
// Good source of info about elements and attributes
// http://dev.w3.org/html5/spec/Overview.html#semantics
// http://simon.html5.org/html-elements
// Safe Void Elements - HTML5
// http://dev.w3.org/html5/spec/Overview.html#void-elements
var voidElements = makeMap("area,br,col,hr,img,wbr");
// Elements that you can, intentionally, leave open (and which close themselves)
// http://dev.w3.org/html5/spec/Overview.html#optional-tags
var optionalEndTagBlockElements = makeMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
optionalEndTagInlineElements = makeMap("rp,rt"),
optionalEndTagElements = angular.extend({},
optionalEndTagInlineElements,
optionalEndTagBlockElements);
// Safe Block Elements - HTML5
var blockElements = angular.extend({}, optionalEndTagBlockElements, makeMap("address,article," +
"aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," +
"h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul"));
// Inline Elements - HTML5
var inlineElements = angular.extend({}, optionalEndTagInlineElements, makeMap("a,abbr,acronym,b," +
"bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," +
"samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
// SVG Elements
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements
var svgElements = makeMap("animate,animateColor,animateMotion,animateTransform,circle,defs," +
"desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,hkern,image,linearGradient," +
"line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,radialGradient,rect,set," +
"stop,svg,switch,text,title,tspan,use");
// Special Elements (can contain anything)
var specialElements = makeMap("script,style");
var validElements = angular.extend({},
voidElements,
blockElements,
inlineElements,
optionalEndTagElements,
svgElements);
//Attributes that have href and hence need to be sanitized
var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap,xlink:href");
var htmlAttrs = makeMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
'scope,scrolling,shape,size,span,start,summary,target,title,type,' +
'valign,value,vspace,width');
// SVG attributes (without "id" and "name" attributes)
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes
var svgAttrs = makeMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
'attributeName,attributeType,baseProfile,bbox,begin,by,calcMode,cap-height,class,color,' +
'color-rendering,content,cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,' +
'font-size,font-stretch,font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,' +
'gradientUnits,hanging,height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,' +
'keySplines,keyTimes,lang,marker-end,marker-mid,marker-start,markerHeight,markerUnits,' +
'markerWidth,mathematical,max,min,offset,opacity,orient,origin,overline-position,' +
'overline-thickness,panose-1,path,pathLength,points,preserveAspectRatio,r,refX,refY,' +
'repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,' +
'stemv,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,stroke,' +
'stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,' +
'stroke-opacity,stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,' +
'underline-position,underline-thickness,unicode,unicode-range,units-per-em,values,version,' +
'viewBox,visibility,width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,' +
'xlink:show,xlink:title,xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,' +
'zoomAndPan');
var validAttrs = angular.extend({},
uriAttrs,
svgAttrs,
htmlAttrs);
function makeMap(str) {
var obj = {}, items = str.split(','), i;
for (i = 0; i < items.length; i++) obj[items[i]] = true;
return obj;
}
/**
* @example
* htmlParser(htmlString, {
* start: function(tag, attrs, unary) {},
* end: function(tag) {},
* chars: function(text) {},
* comment: function(text) {}
* });
*
* @param {string} html string
* @param {object} handler
*/
function htmlParser(html, handler) {
if (typeof html !== 'string') {
if (html === null || typeof html === 'undefined') {
html = '';
} else {
html = '' + html;
}
}
var index, chars, match, stack = [], last = html, text;
stack.last = function() { return stack[stack.length - 1]; };
while (html) {
text = '';
chars = true;
// Make sure we're not in a script or style element
if (!stack.last() || !specialElements[stack.last()]) {
// Comment
if (html.indexOf("<!--") === 0) {
// comments containing -- are not allowed unless they terminate the comment
index = html.indexOf("--", 4);
if (index >= 0 && html.lastIndexOf("-->", index) === index) {
if (handler.comment) handler.comment(html.substring(4, index));
html = html.substring(index + 3);
chars = false;
}
// DOCTYPE
} else if (DOCTYPE_REGEXP.test(html)) {
match = html.match(DOCTYPE_REGEXP);
if (match) {
html = html.replace(match[0], '');
chars = false;
}
// end tag
} else if (BEGING_END_TAGE_REGEXP.test(html)) {
match = html.match(END_TAG_REGEXP);
if (match) {
html = html.substring(match[0].length);
match[0].replace(END_TAG_REGEXP, parseEndTag);
chars = false;
}
// start tag
} else if (BEGIN_TAG_REGEXP.test(html)) {
match = html.match(START_TAG_REGEXP);
if (match) {
// We only have a valid start-tag if there is a '>'.
if (match[4]) {
html = html.substring(match[0].length);
match[0].replace(START_TAG_REGEXP, parseStartTag);
}
chars = false;
} else {
// no ending tag found --- this piece should be encoded as an entity.
text += '<';
html = html.substring(1);
}
}
if (chars) {
index = html.indexOf("<");
text += index < 0 ? html : html.substring(0, index);
html = index < 0 ? "" : html.substring(index);
if (handler.chars) handler.chars(decodeEntities(text));
}
} else {
// IE versions 9 and 10 do not understand the regex '[^]', so using a workaround with [\W\w].
html = html.replace(new RegExp("([\\W\\w]*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'),
function(all, text) {
text = text.replace(COMMENT_REGEXP, "$1").replace(CDATA_REGEXP, "$1");
if (handler.chars) handler.chars(decodeEntities(text));
return "";
});
parseEndTag("", stack.last());
}
if (html == last) {
throw $sanitizeMinErr('badparse', "The sanitizer was unable to parse the following block " +
"of html: {0}", html);
}
last = html;
}
// Clean up any remaining tags
parseEndTag();
function parseStartTag(tag, tagName, rest, unary) {
tagName = angular.lowercase(tagName);
if (blockElements[tagName]) {
while (stack.last() && inlineElements[stack.last()]) {
parseEndTag("", stack.last());
}
}
if (optionalEndTagElements[tagName] && stack.last() == tagName) {
parseEndTag("", tagName);
}
unary = voidElements[tagName] || !!unary;
if (!unary)
stack.push(tagName);
var attrs = {};
rest.replace(ATTR_REGEXP,
function(match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) {
var value = doubleQuotedValue
|| singleQuotedValue
|| unquotedValue
|| '';
attrs[name] = decodeEntities(value);
});
if (handler.start) handler.start(tagName, attrs, unary);
}
function parseEndTag(tag, tagName) {
var pos = 0, i;
tagName = angular.lowercase(tagName);
if (tagName)
// Find the closest opened tag of the same type
for (pos = stack.length - 1; pos >= 0; pos--)
if (stack[pos] == tagName)
break;
if (pos >= 0) {
// Close all the open elements, up the stack
for (i = stack.length - 1; i >= pos; i--)
if (handler.end) handler.end(stack[i]);
// Remove the open elements from the stack
stack.length = pos;
}
}
}
var hiddenPre=document.createElement("pre");
/**
* decodes all entities into regular string
* @param value
* @returns {string} A string with decoded entities.
*/
function decodeEntities(value) {
if (!value) { return ''; }
hiddenPre.innerHTML = value.replace(/</g,"&lt;");
// innerText depends on styling as it doesn't display hidden elements.
// Therefore, it's better to use textContent not to cause unnecessary reflows.
return hiddenPre.textContent;
}
/**
* Escapes all potentially dangerous characters, so that the
* resulting string can be safely inserted into attribute or
* element text.
* @param value
* @returns {string} escaped text
*/
function encodeEntities(value) {
return value.
replace(/&/g, '&amp;').
replace(SURROGATE_PAIR_REGEXP, function(value) {
var hi = value.charCodeAt(0);
var low = value.charCodeAt(1);
return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
}).
replace(NON_ALPHANUMERIC_REGEXP, function(value) {
return '&#' + value.charCodeAt(0) + ';';
}).
replace(/</g, '&lt;').
replace(/>/g, '&gt;');
}
/**
* create an HTML/XML writer which writes to buffer
* @param {Array} buf use buf.jain('') to get out sanitized html string
* @returns {object} in the form of {
* start: function(tag, attrs, unary) {},
* end: function(tag) {},
* chars: function(text) {},
* comment: function(text) {}
* }
*/
function htmlSanitizeWriter(buf, uriValidator) {
var ignore = false;
var out = angular.bind(buf, buf.push);
return {
start: function(tag, attrs, unary) {
tag = angular.lowercase(tag);
if (!ignore && specialElements[tag]) {
ignore = tag;
}
if (!ignore && validElements[tag] === true) {
out('<');
out(tag);
angular.forEach(attrs, function(value, key) {
var lkey=angular.lowercase(key);
var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
if (validAttrs[lkey] === true &&
(uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
out(' ');
out(key);
out('="');
out(encodeEntities(value));
out('"');
}
});
out(unary ? '/>' : '>');
}
},
end: function(tag) {
tag = angular.lowercase(tag);
if (!ignore && validElements[tag] === true) {
out('</');
out(tag);
out('>');
}
if (tag == ignore) {
ignore = false;
}
},
chars: function(chars) {
if (!ignore) {
out(encodeEntities(chars));
}
}
};
}
// define ngSanitize module and register $sanitize service
angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
/* global sanitizeText: false */
/**
* @ngdoc filter
* @name linky
* @kind function
*
* @description
* Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
* plain email address links.
*
* Requires the {@link ngSanitize `ngSanitize`} module to be installed.
*
* @param {string} text Input text.
* @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in.
* @returns {string} Html-linkified text.
*
* @usage
<span ng-bind-html="linky_expression | linky"></span>
*
* @example
<example module="linkyExample" deps="angular-sanitize.js">
<file name="index.html">
<script>
angular.module('linkyExample', ['ngSanitize'])
.controller('ExampleController', ['$scope', function($scope) {
$scope.snippet =
'Pretty text with some links:\n'+
'http://angularjs.org/,\n'+
'mailto:us@somewhere.org,\n'+
'another@somewhere.org,\n'+
'and one more: ftp://127.0.0.1/.';
$scope.snippetWithTarget = 'http://angularjs.org/';
}]);
</script>
<div ng-controller="ExampleController">
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
<table>
<tr>
<td>Filter</td>
<td>Source</td>
<td>Rendered</td>
</tr>
<tr id="linky-filter">
<td>linky filter</td>
<td>
<pre>&lt;div ng-bind-html="snippet | linky"&gt;<br>&lt;/div&gt;</pre>
</td>
<td>
<div ng-bind-html="snippet | linky"></div>
</td>
</tr>
<tr id="linky-target">
<td>linky target</td>
<td>
<pre>&lt;div ng-bind-html="snippetWithTarget | linky:'_blank'"&gt;<br>&lt;/div&gt;</pre>
</td>
<td>
<div ng-bind-html="snippetWithTarget | linky:'_blank'"></div>
</td>
</tr>
<tr id="escaped-html">
<td>no filter</td>
<td><pre>&lt;div ng-bind="snippet"&gt;<br>&lt;/div&gt;</pre></td>
<td><div ng-bind="snippet"></div></td>
</tr>
</table>
</file>
<file name="protractor.js" type="protractor">
it('should linkify the snippet with urls', function() {
expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' +
'another@somewhere.org, and one more: ftp://127.0.0.1/.');
expect(element.all(by.css('#linky-filter a')).count()).toEqual(4);
});
it('should not linkify snippet without the linky filter', function() {
expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()).
toBe('Pretty text with some links: http://angularjs.org/, mailto:us@somewhere.org, ' +
'another@somewhere.org, and one more: ftp://127.0.0.1/.');
expect(element.all(by.css('#escaped-html a')).count()).toEqual(0);
});
it('should update', function() {
element(by.model('snippet')).clear();
element(by.model('snippet')).sendKeys('new http://link.');
expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
toBe('new http://link.');
expect(element.all(by.css('#linky-filter a')).count()).toEqual(1);
expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText())
.toBe('new http://link.');
});
it('should work with the target property', function() {
expect(element(by.id('linky-target')).
element(by.binding("snippetWithTarget | linky:'_blank'")).getText()).
toBe('http://angularjs.org/');
expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank');
});
</file>
</example>
*/
angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
var LINKY_URL_REGEXP =
/((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"”’]/,
MAILTO_REGEXP = /^mailto:/;
return function(text, target) {
if (!text) return text;
var match;
var raw = text;
var html = [];
var url;
var i;
while ((match = raw.match(LINKY_URL_REGEXP))) {
// We can not end in these as they are sometimes found at the end of the sentence
url = match[0];
// if we did not match ftp/http/www/mailto then assume mailto
if (!match[2] && !match[4]) {
url = (match[3] ? 'http://' : 'mailto:') + url;
}
i = match.index;
addText(raw.substr(0, i));
addLink(url, match[0].replace(MAILTO_REGEXP, ''));
raw = raw.substring(i + match[0].length);
}
addText(raw);
return $sanitize(html.join(''));
function addText(text) {
if (!text) {
return;
}
html.push(sanitizeText(text));
}
function addLink(url, text) {
html.push('<a ');
if (angular.isDefined(target)) {
html.push('target="',
target,
'" ');
}
html.push('href="',
url.replace(/"/g, '&quot;'),
'">');
addText(text);
html.push('</a>');
}
};
}]);
})(window, window.angular);

View File

@ -1,16 +0,0 @@
/*
AngularJS v1.3.15
(c) 2010-2014 Google, Inc. http://angularjs.org
License: MIT
*/
(function(n,h,p){'use strict';function E(a){var e=[];r(e,h.noop).chars(a);return e.join("")}function g(a){var e={};a=a.split(",");var d;for(d=0;d<a.length;d++)e[a[d]]=!0;return e}function F(a,e){function d(a,b,d,l){b=h.lowercase(b);if(s[b])for(;f.last()&&t[f.last()];)c("",f.last());u[b]&&f.last()==b&&c("",b);(l=v[b]||!!l)||f.push(b);var m={};d.replace(G,function(a,b,e,c,d){m[b]=q(e||c||d||"")});e.start&&e.start(b,m,l)}function c(a,b){var c=0,d;if(b=h.lowercase(b))for(c=f.length-1;0<=c&&f[c]!=b;c--);
if(0<=c){for(d=f.length-1;d>=c;d--)e.end&&e.end(f[d]);f.length=c}}"string"!==typeof a&&(a=null===a||"undefined"===typeof a?"":""+a);var b,k,f=[],m=a,l;for(f.last=function(){return f[f.length-1]};a;){l="";k=!0;if(f.last()&&w[f.last()])a=a.replace(new RegExp("([\\W\\w]*)<\\s*\\/\\s*"+f.last()+"[^>]*>","i"),function(a,b){b=b.replace(H,"$1").replace(I,"$1");e.chars&&e.chars(q(b));return""}),c("",f.last());else{if(0===a.indexOf("\x3c!--"))b=a.indexOf("--",4),0<=b&&a.lastIndexOf("--\x3e",b)===b&&(e.comment&&
e.comment(a.substring(4,b)),a=a.substring(b+3),k=!1);else if(x.test(a)){if(b=a.match(x))a=a.replace(b[0],""),k=!1}else if(J.test(a)){if(b=a.match(y))a=a.substring(b[0].length),b[0].replace(y,c),k=!1}else K.test(a)&&((b=a.match(z))?(b[4]&&(a=a.substring(b[0].length),b[0].replace(z,d)),k=!1):(l+="<",a=a.substring(1)));k&&(b=a.indexOf("<"),l+=0>b?a:a.substring(0,b),a=0>b?"":a.substring(b),e.chars&&e.chars(q(l)))}if(a==m)throw L("badparse",a);m=a}c()}function q(a){if(!a)return"";A.innerHTML=a.replace(/</g,
"&lt;");return A.textContent}function B(a){return a.replace(/&/g,"&amp;").replace(M,function(a){var d=a.charCodeAt(0);a=a.charCodeAt(1);return"&#"+(1024*(d-55296)+(a-56320)+65536)+";"}).replace(N,function(a){return"&#"+a.charCodeAt(0)+";"}).replace(/</g,"&lt;").replace(/>/g,"&gt;")}function r(a,e){var d=!1,c=h.bind(a,a.push);return{start:function(a,k,f){a=h.lowercase(a);!d&&w[a]&&(d=a);d||!0!==C[a]||(c("<"),c(a),h.forEach(k,function(d,f){var k=h.lowercase(f),g="img"===a&&"src"===k||"background"===
k;!0!==O[k]||!0===D[k]&&!e(d,g)||(c(" "),c(f),c('="'),c(B(d)),c('"'))}),c(f?"/>":">"))},end:function(a){a=h.lowercase(a);d||!0!==C[a]||(c("</"),c(a),c(">"));a==d&&(d=!1)},chars:function(a){d||c(B(a))}}}var L=h.$$minErr("$sanitize"),z=/^<((?:[a-zA-Z])[\w:-]*)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*(>?)/,y=/^<\/\s*([\w:-]+)[^>]*>/,G=/([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,K=/^</,J=/^<\//,H=/\x3c!--(.*?)--\x3e/g,x=/<!DOCTYPE([^>]*?)>/i,
I=/<!\[CDATA\[(.*?)]]\x3e/g,M=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,N=/([^\#-~| |!])/g,v=g("area,br,col,hr,img,wbr");n=g("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr");p=g("rp,rt");var u=h.extend({},p,n),s=h.extend({},n,g("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")),t=h.extend({},p,g("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
n=g("animate,animateColor,animateMotion,animateTransform,circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,radialGradient,rect,set,stop,svg,switch,text,title,tspan,use");var w=g("script,style"),C=h.extend({},v,s,t,u,n),D=g("background,cite,href,longdesc,src,usemap,xlink:href");n=g("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,size,span,start,summary,target,title,type,valign,value,vspace,width");
p=g("accent-height,accumulate,additive,alphabetic,arabic-form,ascent,attributeName,attributeType,baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan");
var O=h.extend({},D,p,n),A=document.createElement("pre");h.module("ngSanitize",[]).provider("$sanitize",function(){this.$get=["$$sanitizeUri",function(a){return function(e){var d=[];F(e,r(d,function(c,b){return!/^unsafe/.test(a(c,b))}));return d.join("")}}]});h.module("ngSanitize").filter("linky",["$sanitize",function(a){var e=/((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/,d=/^mailto:/;return function(c,b){function k(a){a&&g.push(E(a))}function f(a,c){g.push("<a ");
h.isDefined(b)&&g.push('target="',b,'" ');g.push('href="',a.replace(/"/g,"&quot;"),'">');k(c);g.push("</a>")}if(!c)return c;for(var m,l=c,g=[],n,p;m=l.match(e);)n=m[0],m[2]||m[4]||(n=(m[3]?"http://":"mailto:")+n),p=m.index,k(l.substr(0,p)),f(n,m[0].replace(d,"")),l=l.substring(p+m[0].length);k(l);return a(g.join(""))}}])})(window,window.angular);
//# sourceMappingURL=angular-sanitize.min.js.map

26309
lib/angular.js vendored

File diff suppressed because it is too large Load Diff

251
lib/angular.min.js vendored
View File

@ -1,251 +0,0 @@
/*
AngularJS v1.3.15
(c) 2010-2014 Google, Inc. http://angularjs.org
License: MIT
*/
(function(Q,W,t){'use strict';function R(b){return function(){var a=arguments[0],c;c="["+(b?b+":":"")+a+"] http://errors.angularjs.org/1.3.15/"+(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 Sa(b){if(null==b||Ta(b))return!1;var a=b.length;return b.nodeType===
qa&&a?!0:C(b)||H(b)||0===a||"number"===typeof a&&0<a&&a-1 in b}function r(b,a,c){var d,e;if(b)if(G(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(H(b)||Sa(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!==r)b.forEach(a,c,b);else for(d in b)b.hasOwnProperty(d)&&a.call(c,b[d],d,b);return b}function Ed(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 mc(b){return function(a,c){b(c,a)}}function Fd(){return++ob}function nc(b,a){a?b.$$hashKey=a:delete b.$$hashKey}function w(b){for(var a=b.$$hashKey,c=1,d=arguments.length;c<d;c++){var e=arguments[c];if(e)for(var f=Object.keys(e),g=0,h=f.length;g<h;g++){var l=f[g];b[l]=e[l]}}nc(b,a);return b}function aa(b){return parseInt(b,10)}function Ob(b,a){return w(Object.create(b),a)}function E(){}function ra(b){return b}function ea(b){return function(){return b}}function x(b){return"undefined"===
typeof b}function y(b){return"undefined"!==typeof b}function J(b){return null!==b&&"object"===typeof b}function C(b){return"string"===typeof b}function Y(b){return"number"===typeof b}function ga(b){return"[object Date]"===Ca.call(b)}function G(b){return"function"===typeof b}function Ua(b){return"[object RegExp]"===Ca.call(b)}function Ta(b){return b&&b.window===b}function Va(b){return b&&b.$evalAsync&&b.$watch}function Wa(b){return"boolean"===typeof b}function oc(b){return!(!b||!(b.nodeName||b.prop&&
b.attr&&b.find))}function Gd(b){var a={};b=b.split(",");var c;for(c=0;c<b.length;c++)a[b[c]]=!0;return a}function va(b){return z(b.nodeName||b[0]&&b[0].nodeName)}function Xa(b,a){var c=b.indexOf(a);0<=c&&b.splice(c,1);return a}function Da(b,a,c,d){if(Ta(b)||Va(b))throw Ja("cpws");if(a){if(b===a)throw Ja("cpi");c=c||[];d=d||[];if(J(b)){var e=c.indexOf(b);if(-1!==e)return d[e];c.push(b);d.push(a)}if(H(b))for(var f=a.length=0;f<b.length;f++)e=Da(b[f],null,c,d),J(b[f])&&(c.push(b[f]),d.push(e)),a.push(e);
else{var g=a.$$hashKey;H(a)?a.length=0:r(a,function(b,c){delete a[c]});for(f in b)b.hasOwnProperty(f)&&(e=Da(b[f],null,c,d),J(b[f])&&(c.push(b[f]),d.push(e)),a[f]=e);nc(a,g)}}else if(a=b)H(b)?a=Da(b,[],c,d):ga(b)?a=new Date(b.getTime()):Ua(b)?(a=new RegExp(b.source,b.toString().match(/[^\/]*$/)[0]),a.lastIndex=b.lastIndex):J(b)&&(e=Object.create(Object.getPrototypeOf(b)),a=Da(b,e,c,d));return a}function sa(b,a){if(H(b)){a=a||[];for(var c=0,d=b.length;c<d;c++)a[c]=b[c]}else if(J(b))for(c in a=a||{},
b)if("$"!==c.charAt(0)||"$"!==c.charAt(1))a[c]=b[c];return a||b}function ha(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(H(b)){if(!H(a))return!1;if((c=b.length)==a.length){for(d=0;d<c;d++)if(!ha(b[d],a[d]))return!1;return!0}}else{if(ga(b))return ga(a)?ha(b.getTime(),a.getTime()):!1;if(Ua(b))return Ua(a)?b.toString()==a.toString():!1;if(Va(b)||Va(a)||Ta(b)||Ta(a)||H(a)||ga(a)||Ua(a))return!1;c={};for(d in b)if("$"!==
d.charAt(0)&&!G(b[d])){if(!ha(b[d],a[d]))return!1;c[d]=!0}for(d in a)if(!c.hasOwnProperty(d)&&"$"!==d.charAt(0)&&a[d]!==t&&!G(a[d]))return!1;return!0}return!1}function Ya(b,a,c){return b.concat(Za.call(a,c))}function pc(b,a){var c=2<arguments.length?Za.call(arguments,2):[];return!G(a)||a instanceof RegExp?a:c.length?function(){return arguments.length?a.apply(b,Ya(c,arguments,0)):a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}}function Hd(b,a){var c=a;"string"===typeof b&&
"$"===b.charAt(0)&&"$"===b.charAt(1)?c=t:Ta(a)?c="$WINDOW":a&&W===a?c="$DOCUMENT":Va(a)&&(c="$SCOPE");return c}function $a(b,a){if("undefined"===typeof b)return t;Y(a)||(a=a?2:null);return JSON.stringify(b,Hd,a)}function qc(b){return C(b)?JSON.parse(b):b}function wa(b){b=A(b).clone();try{b.empty()}catch(a){}var c=A("<div>").append(b).html();try{return b[0].nodeType===pb?z(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+z(b)})}catch(d){return z(c)}}function rc(b){try{return decodeURIComponent(b)}catch(a){}}
function sc(b){var a={},c,d;r((b||"").split("&"),function(b){b&&(c=b.replace(/\+/g,"%20").split("="),d=rc(c[0]),y(d)&&(b=y(c[1])?rc(c[1]):!0,tc.call(a,d)?H(a[d])?a[d].push(b):a[d]=[a[d],b]:a[d]=b))});return a}function Pb(b){var a=[];r(b,function(b,d){H(b)?r(b,function(b){a.push(Ea(d,!0)+(!0===b?"":"="+Ea(b,!0)))}):a.push(Ea(d,!0)+(!0===b?"":"="+Ea(b,!0)))});return a.length?a.join("&"):""}function qb(b){return Ea(b,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function Ea(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 Id(b,a){var c,d,e=rb.length;b=A(b);for(d=0;d<e;++d)if(c=rb[d]+a,C(c=b.attr(c)))return c;return null}function Jd(b,a){var c,d,e={};r(rb,function(a){a+="app";!c&&b.hasAttribute&&b.hasAttribute(a)&&(c=b,d=b.getAttribute(a))});r(rb,function(a){a+="app";var e;!c&&(e=b.querySelector("["+a.replace(":","\\:")+"]"))&&(c=e,d=e.getAttribute(a))});c&&(e.strictDi=null!==Id(c,"strict-di"),
a(c,d?[d]:[],e))}function uc(b,a,c){J(c)||(c={});c=w({strictDi:!1},c);var d=function(){b=A(b);if(b.injector()){var d=b[0]===W?"document":wa(b);throw Ja("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=ab(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!/;Q&&e.test(Q.name)&&(c.debugInfoEnabled=!0,Q.name=Q.name.replace(e,""));if(Q&&!f.test(Q.name))return d();Q.name=Q.name.replace(f,"");ca.resumeBootstrap=function(b){r(b,function(b){a.push(b)});return d()};G(ca.resumeDeferredBootstrap)&&ca.resumeDeferredBootstrap()}function Kd(){Q.name="NG_ENABLE_DEBUG_INFO!"+Q.name;Q.location.reload()}function Ld(b){b=ca.element(b).injector();if(!b)throw Ja("test");return b.get("$$testability")}
function vc(b,a){a=a||"_";return b.replace(Md,function(b,d){return(d?a:"")+b.toLowerCase()})}function Nd(){var b;wc||((ta=Q.jQuery)&&ta.fn.on?(A=ta,w(ta.fn,{scope:Ka.scope,isolateScope:Ka.isolateScope,controller:Ka.controller,injector:Ka.injector,inheritedData:Ka.inheritedData}),b=ta.cleanData,ta.cleanData=function(a){var c;if(Qb)Qb=!1;else for(var d=0,e;null!=(e=a[d]);d++)(c=ta._data(e,"events"))&&c.$destroy&&ta(e).triggerHandler("$destroy");b(a)}):A=T,ca.element=A,wc=!0)}function Rb(b,a,c){if(!b)throw Ja("areq",
a||"?",c||"required");return b}function sb(b,a,c){c&&H(b)&&(b=b[b.length-1]);Rb(G(b),a,"not a function, got "+(b&&"object"===typeof b?b.constructor.name||"Object":typeof b));return b}function La(b,a){if("hasOwnProperty"===b)throw Ja("badname",a);}function xc(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&&G(b)?pc(e,b):b}function tb(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 A(c)}function ia(){return Object.create(null)}
function Od(b){function a(a,b,c){return a[b]||(a[b]=c())}var c=R("$injector"),d=R("ng");b=a(b,"angular",Object);b.$$minErr=b.$$minErr||R;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(c,d,e,f){f||(f=b);return function(){f[e||"push"]([c,d,arguments]);return u}}if(!g)throw c("nomod",f);var b=[],d=[],e=[],q=a("$injector","invoke","push",d),u={_invokeQueue:b,_configBlocks:d,
_runBlocks:e,requires:g,name:f,provider:a("$provide","provider"),factory:a("$provide","factory"),service:a("$provide","service"),value:a("$provide","value"),constant:a("$provide","constant","unshift"),animation:a("$animateProvider","register"),filter:a("$filterProvider","register"),controller:a("$controllerProvider","register"),directive:a("$compileProvider","directive"),config:q,run:function(a){e.push(a);return this}};h&&q(h);return u})}})}function Pd(b){w(b,{bootstrap:uc,copy:Da,extend:w,equals:ha,
element:A,forEach:r,injector:ab,noop:E,bind:pc,toJson:$a,fromJson:qc,identity:ra,isUndefined:x,isDefined:y,isString:C,isFunction:G,isObject:J,isNumber:Y,isElement:oc,isArray:H,version:Qd,isDate:ga,lowercase:z,uppercase:ub,callbacks:{counter:0},getTestability:Ld,$$minErr:R,$$csp:bb,reloadWithDebugInfo:Kd});cb=Od(Q);try{cb("ngLocale")}catch(a){cb("ngLocale",[]).provider("$locale",Rd)}cb("ng",["ngLocale"],["$provide",function(a){a.provider({$$sanitizeUri:Sd});a.provider("$compile",yc).directive({a:Td,
input:zc,textarea:zc,form:Ud,script:Vd,select:Wd,style:Xd,option:Yd,ngBind:Zd,ngBindHtml:$d,ngBindTemplate:ae,ngClass:be,ngClassEven:ce,ngClassOdd:de,ngCloak:ee,ngController:fe,ngForm:ge,ngHide:he,ngIf:ie,ngInclude:je,ngInit:ke,ngNonBindable:le,ngPluralize:me,ngRepeat:ne,ngShow:oe,ngStyle:pe,ngSwitch:qe,ngSwitchWhen:re,ngSwitchDefault:se,ngOptions:te,ngTransclude:ue,ngModel:ve,ngList:we,ngChange:xe,pattern:Ac,ngPattern:Ac,required:Bc,ngRequired:Bc,minlength:Cc,ngMinlength:Cc,maxlength:Dc,ngMaxlength:Dc,
ngValue:ye,ngModelOptions:ze}).directive({ngInclude:Ae}).directive(vb).directive(Ec);a.provider({$anchorScroll:Be,$animate:Ce,$browser:De,$cacheFactory:Ee,$controller:Fe,$document:Ge,$exceptionHandler:He,$filter:Fc,$interpolate:Ie,$interval:Je,$http:Ke,$httpBackend:Le,$location:Me,$log:Ne,$parse:Oe,$rootScope:Pe,$q:Qe,$$q:Re,$sce:Se,$sceDelegate:Te,$sniffer:Ue,$templateCache:Ve,$templateRequest:We,$$testability:Xe,$timeout:Ye,$window:Ze,$$rAF:$e,$$asyncCallback:af,$$jqLite:bf})}])}function db(b){return b.replace(cf,
function(a,b,d,e){return e?d.toUpperCase():d}).replace(df,"Moz$1")}function Gc(b){b=b.nodeType;return b===qa||!b||9===b}function Hc(b,a){var c,d,e=a.createDocumentFragment(),f=[];if(Sb.test(b)){c=c||e.appendChild(a.createElement("div"));d=(ef.exec(b)||["",""])[1].toLowerCase();d=ja[d]||ja._default;c.innerHTML=d[1]+b.replace(ff,"<$1></$2>")+d[2];for(d=d[0];d--;)c=c.lastChild;f=Ya(f,c.childNodes);c=e.firstChild;c.textContent=""}else f.push(a.createTextNode(b));e.textContent="";e.innerHTML="";r(f,function(a){e.appendChild(a)});
return e}function T(b){if(b instanceof T)return b;var a;C(b)&&(b=N(b),a=!0);if(!(this instanceof T)){if(a&&"<"!=b.charAt(0))throw Tb("nosel");return new T(b)}if(a){a=W;var c;b=(c=gf.exec(b))?[a.createElement(c[1])]:(c=Hc(b,a))?c.childNodes:[]}Ic(this,b)}function Ub(b){return b.cloneNode(!0)}function wb(b,a){a||xb(b);if(b.querySelectorAll)for(var c=b.querySelectorAll("*"),d=0,e=c.length;d<e;d++)xb(c[d])}function Jc(b,a,c,d){if(y(d))throw Tb("offargs");var e=(d=yb(b))&&d.events,f=d&&d.handle;if(f)if(a)r(a.split(" "),
function(a){if(y(c)){var d=e[a];Xa(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 xb(b,a){var c=b.ng339,d=c&&zb[c];d&&(a?delete d.data[a]:(d.handle&&(d.events.$destroy&&d.handle({},"$destroy"),Jc(b)),delete zb[c],b.ng339=t))}function yb(b,a){var c=b.ng339,c=c&&zb[c];a&&!c&&(b.ng339=c=++hf,c=zb[c]={events:{},data:{},handle:t});return c}function Vb(b,a,c){if(Gc(b)){var d=y(c),e=!d&&a&&!J(a),
f=!a;b=(b=yb(b,!e))&&b.data;if(d)b[a]=c;else{if(f)return b;if(e)return b&&b[a];w(b,a)}}}function Ab(b,a){return b.getAttribute?-1<(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").indexOf(" "+a+" "):!1}function Bb(b,a){a&&b.setAttribute&&r(a.split(" "),function(a){b.setAttribute("class",N((" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").replace(" "+N(a)+" "," ")))})}function Cb(b,a){if(a&&b.setAttribute){var c=(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ");
r(a.split(" "),function(a){a=N(a);-1===c.indexOf(" "+a+" ")&&(c+=a+" ")});b.setAttribute("class",N(c))}}function Ic(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 Kc(b,a){return Db(b,"$"+(a||"ngController")+"Controller")}function Db(b,a,c){9==b.nodeType&&(b=b.documentElement);for(a=H(a)?a:[a];b;){for(var d=0,e=a.length;d<e;d++)if((c=A.data(b,a[d]))!==t)return c;b=b.parentNode||
11===b.nodeType&&b.host}}function Lc(b){for(wb(b,!0);b.firstChild;)b.removeChild(b.firstChild)}function Mc(b,a){a||wb(b);var c=b.parentNode;c&&c.removeChild(b)}function jf(b,a){a=a||Q;if("complete"===a.document.readyState)a.setTimeout(b);else A(a).on("load",b)}function Nc(b,a){var c=Eb[a.toLowerCase()];return c&&Oc[va(b)]&&c}function kf(b,a){var c=b.nodeName;return("INPUT"===c||"TEXTAREA"===c)&&Pc[a]}function lf(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(x(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=sa(f));for(var l=0;l<g;l++)c.isImmediatePropagationStopped()||f[l].call(b,c)}};c.elem=b;return c}function bf(){this.$get=function(){return w(T,{hasClass:function(b,a){b.attr&&(b=b[0]);
return Ab(b,a)},addClass:function(b,a){b.attr&&(b=b[0]);return Cb(b,a)},removeClass:function(b,a){b.attr&&(b=b[0]);return Bb(b,a)}})}}function Ma(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||Fd)():c+":"+b}function eb(b,a){if(a){var c=0;this.nextUid=function(){return++c}}r(b,this.put,this)}function mf(b){return(b=b.toString().replace(Qc,"").match(Rc))?"function("+(b[1]||"").replace(/[\s\r\n]+/,
" ")+")":"fn"}function ab(b,a){function c(a){return function(b,c){if(J(b))r(b,mc(a));else return a(b,c)}}function d(a,b){La(a,"service");if(G(b)||H(b))b=q.instantiate(b);if(!b.$get)throw Fa("pget",a);return p[a+"Provider"]=b}function e(a,b){return function(){var c=s.invoke(b,this);if(x(c))throw Fa("undef",a);return c}}function f(a,b,c){return d(a,{$get:!1!==c?e(a,b):b})}function g(a){var b=[],c;r(a,function(a){function d(a){var b,c;b=0;for(c=a.length;b<c;b++){var e=a[b],f=q.get(e[0]);f[e[1]].apply(f,
e[2])}}if(!n.get(a)){n.put(a,!0);try{C(a)?(c=cb(a),b=b.concat(g(c.requires)).concat(c._runBlocks),d(c._invokeQueue),d(c._configBlocks)):G(a)?b.push(q.invoke(a)):H(a)?b.push(q.invoke(a)):sb(a,"module")}catch(e){throw H(a)&&(a=a[a.length-1]),e.message&&e.stack&&-1==e.stack.indexOf(e.message)&&(e=e.message+"\n"+e.stack),Fa("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 Fa("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 k=[],h=ab.$$annotate(b,a,g),l,q,p;q=0;for(l=h.length;q<l;q++){p=h[q];if("string"!==typeof p)throw Fa("itkn",p);k.push(f&&f.hasOwnProperty(p)?f[p]:d(p,g))}H(b)&&(b=b[l]);return b.apply(c,k)}return{invoke:e,instantiate:function(a,b,c){var d=Object.create((H(a)?a[a.length-1]:a).prototype||null);a=e(a,d,b,c);return J(a)||G(a)?a:d},get:d,annotate:ab.$$annotate,has:function(a){return p.hasOwnProperty(a+
"Provider")||b.hasOwnProperty(a)}}}a=!0===a;var l={},k=[],n=new eb([],!0),p={$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,ea(b),!1)}),constant:c(function(a,b){La(a,"constant");p[a]=b;u[a]=b}),decorator:function(a,b){var c=q.get(a+"Provider"),d=c.$get;c.$get=function(){var a=s.invoke(d,c);return s.invoke(b,null,{$delegate:a})}}}},q=p.$injector=h(p,function(a,b){ca.isString(b)&&k.push(b);
throw Fa("unpr",k.join(" <- "));}),u={},s=u.$injector=h(u,function(a,b){var c=q.get(a+"Provider",b);return s.invoke(c.$get,c,t,a)});r(g(b),function(a){s.invoke(a||E)});return s}function Be(){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"===va(a))return b=a,!0});return b}function f(b){if(b){b.scrollIntoView();var c;c=g.yOffset;G(c)?c=c():oc(c)?(c=c[0],c="fixed"!==
a.getComputedStyle(c).position?0:c.getBoundingClientRect().bottom):Y(c)||(c=0);c&&(b=b.getBoundingClientRect().top,a.scrollBy(0,b-c))}else a.scrollTo(0,0)}function g(){var a=c.hash(),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||jf(function(){d.$evalAsync(g)})});return g}]}function af(){this.$get=["$$rAF","$timeout",function(b,a){return b.supported?function(a){return b(a)}:
function(b){return a(b,0,!1)}}]}function nf(b,a,c,d){function e(a){try{a.apply(null,Za.call(arguments,1))}finally{if(m--,0===m)for(;F.length;)try{F.pop()()}catch(b){c.error(b)}}}function f(a,b){(function da(){r(Z,function(a){a()});L=b(da,a)})()}function g(){h();l()}function h(){a:{try{B=u.state;break a}catch(a){}B=void 0}B=x(B)?null:B;ha(B,O)&&(B=O);O=B}function l(){if(D!==n.url()||I!==B)D=n.url(),I=B,r(X,function(a){a(n.url(),B)})}function k(a){try{return decodeURIComponent(a)}catch(b){return a}}
var n=this,p=a[0],q=b.location,u=b.history,s=b.setTimeout,M=b.clearTimeout,v={};n.isMock=!1;var m=0,F=[];n.$$completeOutstandingRequest=e;n.$$incOutstandingRequestCount=function(){m++};n.notifyWhenNoOutstandingRequests=function(a){r(Z,function(a){a()});0===m?a():F.push(a)};var Z=[],L;n.addPollFn=function(a){x(L)&&f(100,s);Z.push(a);return a};var B,I,D=q.href,S=a.find("base"),P=null;h();I=B;n.url=function(a,c,e){x(e)&&(e=null);q!==b.location&&(q=b.location);u!==b.history&&(u=b.history);if(a){var f=
I===e;if(D===a&&(!d.history||f))return n;var g=D&&Ga(D)===Ga(a);D=a;I=e;!d.history||g&&f?(g||(P=a),c?q.replace(a):g?(c=q,e=a.indexOf("#"),a=-1===e?"":a.substr(e+1),c.hash=a):q.href=a):(u[c?"replaceState":"pushState"](e,"",a),h(),I=B);return n}return P||q.href.replace(/%27/g,"'")};n.state=function(){return B};var X=[],ba=!1,O=null;n.onUrlChange=function(a){if(!ba){if(d.history)A(b).on("popstate",g);A(b).on("hashchange",g);ba=!0}X.push(a);return a};n.$$checkUrlChange=l;n.baseHref=function(){var a=S.attr("href");
return a?a.replace(/^(https?\:)?\/\/[^\/]*/,""):""};var fa={},y="",ka=n.baseHref();n.cookies=function(a,b){var d,e,f,g;if(a)b===t?p.cookie=encodeURIComponent(a)+"=;path="+ka+";expires=Thu, 01 Jan 1970 00:00:00 GMT":C(b)&&(d=(p.cookie=encodeURIComponent(a)+"="+encodeURIComponent(b)+";path="+ka).length+1,4096<d&&c.warn("Cookie '"+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!"));else{if(p.cookie!==y)for(y=p.cookie,d=y.split("; "),fa={},f=0;f<d.length;f++)e=d[f],g=
e.indexOf("="),0<g&&(a=k(e.substring(0,g)),fa[a]===t&&(fa[a]=k(e.substring(g+1))));return fa}};n.defer=function(a,b){var c;m++;c=s(function(){delete v[c];e(a)},b||0);v[c]=!0;return c};n.defer.cancel=function(a){return v[a]?(delete v[a],M(a),e(E),!0):!1}}function De(){this.$get=["$window","$log","$sniffer","$document",function(b,a,c,d){return new nf(b,d,a,c)}]}function Ee(){this.$get=function(){function b(b,d){function e(a){a!=p&&(q?q==a&&(q=a.n):q=a,f(a.n,a.p),f(a,p),p=a,p.n=null)}function f(a,b){a!=
b&&(a&&(a.p=b),b&&(b.n=a))}if(b in a)throw R("$cacheFactory")("iid",b);var g=0,h=w({},d,{id:b}),l={},k=d&&d.capacity||Number.MAX_VALUE,n={},p=null,q=null;return a[b]={put:function(a,b){if(k<Number.MAX_VALUE){var c=n[a]||(n[a]={key:a});e(c)}if(!x(b))return a in l||g++,l[a]=b,g>k&&this.remove(q.key),b},get:function(a){if(k<Number.MAX_VALUE){var b=n[a];if(!b)return;e(b)}return l[a]},remove:function(a){if(k<Number.MAX_VALUE){var b=n[a];if(!b)return;b==p&&(p=b.p);b==q&&(q=b.n);f(b.n,b.p);delete n[a]}delete l[a];
g--},removeAll:function(){l={};g=0;n={};p=q=null},destroy:function(){n=h=l=null;delete a[b]},info:function(){return w({},h,{size:g})}}}var a={};b.info=function(){var b={};r(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return a[b]};return b}}function Ve(){this.$get=["$cacheFactory",function(b){return b("templates")}]}function yc(b,a){function c(a,b){var c=/^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/,d={};r(a,function(a,e){var f=a.match(c);if(!f)throw la("iscp",b,e,a);d[e]={mode:f[1][0],collection:"*"===
f[2],optional:"?"===f[3],attrName:f[4]||e}});return d}var d={},e=/^\s*directive\:\s*([\w\-]+)\s+(.*)$/,f=/(([\w\-]+)(?:\:([^;]+))?;?)/,g=Gd("ngSrc,ngSrcset,src,srcset"),h=/^(?:(\^\^?)?(\?)?(\^\^?)?)?/,l=/^(on[a-z]+|formaction)$/;this.directive=function p(a,e){La(a,"directive");C(a)?(Rb(e,"directiveFactory"),d.hasOwnProperty(a)||(d[a]=[],b.factory(a+"Directive",["$injector","$exceptionHandler",function(b,e){var f=[];r(d[a],function(d,g){try{var h=b.invoke(d);G(h)?h={compile:ea(h)}:!h.compile&&h.link&&
(h.compile=ea(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";J(h.scope)&&(h.$$isolateBindings=c(h.scope,h.name));f.push(h)}catch(k){e(k)}});return f}])),d[a].push(e)):r(a,mc(p));return this};this.aHrefSanitizationWhitelist=function(b){return y(b)?(a.aHrefSanitizationWhitelist(b),this):a.aHrefSanitizationWhitelist()};this.imgSrcSanitizationWhitelist=function(b){return y(b)?(a.imgSrcSanitizationWhitelist(b),this):a.imgSrcSanitizationWhitelist()};
var k=!0;this.debugInfoEnabled=function(a){return y(a)?(k=a,this):k};this.$get=["$injector","$interpolate","$exceptionHandler","$templateRequest","$parse","$controller","$rootScope","$document","$sce","$animate","$$sanitizeUri",function(a,b,c,s,M,v,m,F,Z,L,B){function I(a,b){try{a.addClass(b)}catch(c){}}function D(a,b,c,d,e){a instanceof A||(a=A(a));r(a,function(b,c){b.nodeType==pb&&b.nodeValue.match(/\S+/)&&(a[c]=A(b).wrap("<span></span>").parent()[0])});var f=S(a,b,a,c,d,e);D.$$addScopeClass(a);
var g=null;return function(b,c,d){Rb(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"!==va(d)&&d.toString().match(/SVG/)?"svg":"html":"html");d="html"!==g?A(Xb(g,A("<div>").append(a).html())):c?Ka.clone.call(a):a;if(h)for(var k in h)d.data("$"+k+"Controller",h[k].instance);D.$$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,q,p,s,M;if(m)for(M=Array(c.length),q=0;q<h.length;q+=3)f=h[q],M[f]=c[f];else M=c;q=0;for(p=h.length;q<p;)k=M[h[q++]],c=h[q++],f=h[q++],c?(c.scope?(l=a.$new(),D.$$addScopeInfo(A(k),l)):l=a,s=c.transcludeOnThisElement?P(a,c.transclude,e,c.elementTranscludeOnThisElement):!c.templateOnThisElement&&e?e:!e&&b?P(a,b):null,c(f,l,k,d,s)):f&&f(a,k.childNodes,t,e)}for(var h=[],k,l,q,p,m,s=0;s<a.length;s++){k=new Yb;l=X(a[s],[],k,0===s?d:t,e);(f=l.length?fa(l,a[s],k,b,c,null,[],[],f):null)&&
f.scope&&D.$$addScopeClass(k.$$element);k=f&&f.terminal||!(q=a[s].childNodes)||!q.length?null:S(q,f?(f.transcludeOnThisElement||!f.templateOnThisElement)&&f.transclude:b);if(f||k)h.push(s,f,k),p=!0,m=m||f;f=null}return p?g:null}function P(a,b,c,d){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 X(a,b,c,d,g){var h=c.$attr,k;switch(a.nodeType){case qa:ka(b,xa(va(a)),"E",d,g);for(var l,
q,p,m=a.attributes,s=0,M=m&&m.length;s<M;s++){var u=!1,L=!1;l=m[s];k=l.name;q=N(l.value);l=xa(k);if(p=U.test(l))k=k.replace(Sc,"").substr(8).replace(/_(.)/g,function(a,b){return b.toUpperCase()});var B=l.replace(/(Start|End)$/,"");x(B)&&l===B+"Start"&&(u=k,L=k.substr(0,k.length-5)+"end",k=k.substr(0,k.length-6));l=xa(k.toLowerCase());h[l]=k;if(p||!c.hasOwnProperty(l))c[l]=q,Nc(a,l)&&(c[l]=!0);Oa(a,b,q,l,p);ka(b,l,"A",d,g,u,L)}a=a.className;J(a)&&(a=a.animVal);if(C(a)&&""!==a)for(;k=f.exec(a);)l=xa(k[2]),
ka(b,l,"C",d,g)&&(c[l]=N(k[3])),a=a.substr(k.index+k[0].length);break;case pb:za(b,a.nodeValue);break;case 8:try{if(k=e.exec(a.nodeValue))l=xa(k[1]),ka(b,l,"M",d,g)&&(c[l]=N(k[2]))}catch(v){}}b.sort(da);return b}function ba(a,b,c){var d=[],e=0;if(b&&a.hasAttribute&&a.hasAttribute(b)){do{if(!a)throw la("uterdir",b,c);a.nodeType==qa&&(a.hasAttribute(b)&&e++,a.hasAttribute(c)&&e--);d.push(a);a=a.nextSibling}while(0<e)}else d.push(a);return A(d)}function O(a,b,c){return function(d,e,f,g,h){e=ba(e[0],
b,c);return a(d,e,f,g,h)}}function fa(a,d,e,f,g,k,l,p,m){function s(a,b,c,d){if(a){c&&(a=O(a,c,d));a.require=K.require;a.directiveName=da;if(P===K||K.$$isolateScope)a=Y(a,{isolateScope:!0});l.push(a)}if(b){c&&(b=O(b,c,d));b.require=K.require;b.directiveName=da;if(P===K||K.$$isolateScope)b=Y(b,{isolateScope:!0});p.push(b)}}function L(a,b,c,d){var e,f="data",g=!1,k=c,l;if(C(b)){l=b.match(h);b=b.substring(l[0].length);l[3]&&(l[1]?l[3]=null:l[1]=l[3]);"^"===l[1]?f="inheritedData":"^^"===l[1]&&(f="inheritedData",
k=c.parent());"?"===l[2]&&(g=!0);e=null;d&&"data"===f&&(e=d[b])&&(e=e.instance);e=e||k[f]("$"+b+"Controller");if(!e&&!g)throw la("ctreq",b,a);return e||null}H(b)&&(e=[],r(b,function(b){e.push(L(a,b,c,d))}));return e}function B(a,c,f,g,h){function k(a,b,c){var d;Va(a)||(c=b,b=a,a=t);E&&(d=F);c||(c=E?X.parent():X);return h(a,b,d,c,Wb)}var m,s,u,I,F,gb,X,O;d===f?(O=e,X=e.$$element):(X=A(f),O=new Yb(X,e));P&&(I=c.$new(!0));h&&(gb=k,gb.$$boundTransclude=h);S&&(Z={},F={},r(S,function(a){var b={$scope:a===
P||a.$$isolateScope?I:c,$element:X,$attrs:O,$transclude:gb};u=a.controller;"@"==u&&(u=O[a.name]);b=v(u,b,!0,a.controllerAs);F[a.name]=b;E||X.data("$"+a.name+"Controller",b.instance);Z[a.name]=b}));if(P){D.$$addScopeInfo(X,I,!0,!(ma&&(ma===P||ma===P.$$originalDirective)));D.$$addScopeClass(X,!0);g=Z&&Z[P.name];var ba=I;g&&g.identifier&&!0===P.bindToController&&(ba=g.instance);r(I.$$isolateBindings=P.$$isolateBindings,function(a,d){var e=a.attrName,f=a.optional,g,h,k,l;switch(a.mode){case "@":O.$observe(e,
function(a){ba[d]=a});O.$$observers[e].$$scope=c;O[e]&&(ba[d]=b(O[e])(c));break;case "=":if(f&&!O[e])break;h=M(O[e]);l=h.literal?ha:function(a,b){return a===b||a!==a&&b!==b};k=h.assign||function(){g=ba[d]=h(c);throw la("nonassign",O[e],P.name);};g=ba[d]=h(c);f=function(a){l(a,ba[d])||(l(a,g)?k(c,a=ba[d]):ba[d]=a);return g=a};f.$stateful=!0;f=a.collection?c.$watchCollection(O[e],f):c.$watch(M(O[e],f),null,h.literal);I.$on("$destroy",f);break;case "&":h=M(O[e]),ba[d]=function(a){return h(c,a)}}})}Z&&
(r(Z,function(a){a()}),Z=null);g=0;for(m=l.length;g<m;g++)s=l[g],$(s,s.isolateScope?I:c,X,O,s.require&&L(s.directiveName,s.require,X,F),gb);var Wb=c;P&&(P.template||null===P.templateUrl)&&(Wb=I);a&&a(Wb,f.childNodes,t,h);for(g=p.length-1;0<=g;g--)s=p[g],$(s,s.isolateScope?I:c,X,O,s.require&&L(s.directiveName,s.require,X,F),gb)}m=m||{};for(var I=-Number.MAX_VALUE,F,S=m.controllerDirectives,Z,P=m.newIsolateScopeDirective,ma=m.templateDirective,fa=m.nonTlbTranscludeDirective,ka=!1,x=!1,E=m.hasElementTranscludeDirective,
w=e.$$element=A(d),K,da,V,fb=f,za,z=0,Q=a.length;z<Q;z++){K=a[z];var Oa=K.$$start,U=K.$$end;Oa&&(w=ba(d,Oa,U));V=t;if(I>K.priority)break;if(V=K.scope)K.templateUrl||(J(V)?(Na("new/isolated scope",P||F,K,w),P=K):Na("new/isolated scope",P,K,w)),F=F||K;da=K.name;!K.templateUrl&&K.controller&&(V=K.controller,S=S||{},Na("'"+da+"' controller",S[da],K,w),S[da]=K);if(V=K.transclude)ka=!0,K.$$tlb||(Na("transclusion",fa,K,w),fa=K),"element"==V?(E=!0,I=K.priority,V=w,w=e.$$element=A(W.createComment(" "+da+": "+
e[da]+" ")),d=w[0],T(g,Za.call(V,0),d),fb=D(V,f,I,k&&k.name,{nonTlbTranscludeDirective:fa})):(V=A(Ub(d)).contents(),w.empty(),fb=D(V,f));if(K.template)if(x=!0,Na("template",ma,K,w),ma=K,V=G(K.template)?K.template(w,e):K.template,V=Tc(V),K.replace){k=K;V=Sb.test(V)?Uc(Xb(K.templateNamespace,N(V))):[];d=V[0];if(1!=V.length||d.nodeType!==qa)throw la("tplrt",da,"");T(g,w,d);Q={$attr:{}};V=X(d,[],Q);var aa=a.splice(z+1,a.length-(z+1));P&&y(V);a=a.concat(V).concat(aa);R(e,Q);Q=a.length}else w.html(V);if(K.templateUrl)x=
!0,Na("template",ma,K,w),ma=K,K.replace&&(k=K),B=of(a.splice(z,a.length-z),w,e,g,ka&&fb,l,p,{controllerDirectives:S,newIsolateScopeDirective:P,templateDirective:ma,nonTlbTranscludeDirective:fa}),Q=a.length;else if(K.compile)try{za=K.compile(w,e,fb),G(za)?s(null,za,Oa,U):za&&s(za.pre,za.post,Oa,U)}catch(pf){c(pf,wa(w))}K.terminal&&(B.terminal=!0,I=Math.max(I,K.priority))}B.scope=F&&!0===F.scope;B.transcludeOnThisElement=ka;B.elementTranscludeOnThisElement=E;B.templateOnThisElement=x;B.transclude=fb;
m.hasElementTranscludeDirective=E;return B}function y(a){for(var b=0,c=a.length;b<c;b++)a[b]=Ob(a[b],{$$isolateScope:!0})}function ka(b,e,f,g,h,k,l){if(e===h)return null;h=null;if(d.hasOwnProperty(e)){var q;e=a.get(e+"Directive");for(var m=0,s=e.length;m<s;m++)try{q=e[m],(g===t||g>q.priority)&&-1!=q.restrict.indexOf(f)&&(k&&(q=Ob(q,{$$start:k,$$end:l})),b.push(q),h=q)}catch(M){c(M)}}return h}function x(b){if(d.hasOwnProperty(b))for(var c=a.get(b+"Directive"),e=0,f=c.length;e<f;e++)if(b=c[e],b.multiElement)return!0;
return!1}function R(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;r(a,function(d,e){"$"!=e.charAt(0)&&(b[e]&&b[e]!==d&&(d+=("style"===e?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});r(b,function(b,f){"class"==f?(I(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 of(a,b,c,d,e,f,g,h){var k=[],l,q,p=b[0],m=a.shift(),M=Ob(m,{templateUrl:null,transclude:null,
replace:null,$$originalDirective:m}),u=G(m.templateUrl)?m.templateUrl(b,c):m.templateUrl,L=m.templateNamespace;b.empty();s(Z.getTrustedResourceUrl(u)).then(function(s){var B,v;s=Tc(s);if(m.replace){s=Sb.test(s)?Uc(Xb(L,N(s))):[];B=s[0];if(1!=s.length||B.nodeType!==qa)throw la("tplrt",m.name,u);s={$attr:{}};T(d,b,B);var D=X(B,[],s);J(m.scope)&&y(D);a=D.concat(a);R(c,s)}else B=p,b.html(s);a.unshift(M);l=fa(a,B,c,e,b,m,f,g,h);r(d,function(a,c){a==B&&(d[c]=b[0])});for(q=S(b[0].childNodes,e);k.length;){s=
k.shift();v=k.shift();var F=k.shift(),O=k.shift(),D=b[0];if(!s.$$destroyed){if(v!==p){var Z=v.className;h.hasElementTranscludeDirective&&m.replace||(D=Ub(B));T(F,A(v),D);I(A(D),Z)}v=l.transcludeOnThisElement?P(s,l.transclude,O):O;l(q,s,D,d,v)}}k=null});return function(a,b,c,d,e){a=e;b.$$destroyed||(k?k.push(b,c,d,a):(l.transcludeOnThisElement&&(a=P(b,l.transclude,e)),l(q,b,c,d,a)))}}function da(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 Na(a,
b,c,d){if(b)throw la("multidir",b.name,c.name,a,wa(d));}function za(a,c){var d=b(c,!0);d&&a.push({priority:0,compile:function(a){a=a.parent();var b=!!a.length;b&&D.$$addBindingClass(a);return function(a,c){var e=c.parent();b||D.$$addBindingClass(e);D.$$addBindingInfo(e,d.expressions);a.$watch(d,function(a){c[0].nodeValue=a})}}})}function Xb(a,b){a=z(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 Q(a,b){if("srcdoc"==b)return Z.HTML;var c=va(a);if("xlinkHref"==b||"form"==c&&"action"==b||"img"!=c&&("src"==b||"ngSrc"==b))return Z.RESOURCE_URL}function Oa(a,c,d,e,f){var h=Q(a,e);f=g[e]||f;var k=b(d,!0,h,f);if(k){if("multiple"===e&&"select"===va(a))throw la("selmulti",wa(a));c.push({priority:100,compile:function(){return{pre:function(a,c,g){c=g.$$observers||(g.$$observers={});if(l.test(e))throw la("nodomevents");var m=g[e];m!==d&&(k=m&&b(m,!0,h,f),d=m);k&&(g[e]=k(a),(c[e]||(c[e]=[])).$$inter=
!0,(g.$$observers&&g.$$observers[e].$$scope||a).$watch(k,function(a,b){"class"===e&&a!=b?g.$updateClass(a,b):g.$set(e,a)}))}}}})}}function T(a,b,c){var d=b[0],e=b.length,f=d.parentNode,g,h;if(a)for(g=0,h=a.length;g<h;g++)if(a[g]==d){a[g++]=c;h=g+e-1;for(var k=a.length;g<k;g++,h++)h<k?a[g]=a[h]:delete a[g];a.length-=e-1;a.context===d&&(a.context=c);break}f&&f.replaceChild(c,d);a=W.createDocumentFragment();a.appendChild(d);A(c).data(A(d).data());ta?(Qb=!0,ta.cleanData([d])):delete A.cache[d[A.expando]];
d=1;for(e=b.length;d<e;d++)f=b[d],A(f).remove(),a.appendChild(f),delete b[d];b[0]=c;b.length=1}function Y(a,b){return w(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,wa(d))}}var Yb=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};Yb.prototype={$normalize:xa,$addClass:function(a){a&&0<a.length&&L.addClass(this.$$element,a)},$removeClass:function(a){a&&0<a.length&&
L.removeClass(this.$$element,a)},$updateClass:function(a,b){var c=Vc(a,b);c&&c.length&&L.addClass(this.$$element,c);(c=Vc(b,a))&&c.length&&L.removeClass(this.$$element,c)},$set:function(a,b,d,e){var f=this.$$element[0],g=Nc(f,a),h=kf(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=vc(a,"-"));g=va(this.$$element);if("a"===g&&"href"===a||"img"===g&&"src"===a)this[a]=b=B(b,"src"===a);else if("img"===g&&"srcset"===a){for(var g=
"",h=N(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 q=2*l,g=g+B(N(h[q]),!0),g=g+(" "+N(h[q+1]));h=N(h[2*l]).split(/\s/);g+=B(N(h[0]),!0);2===h.length&&(g+=" "+N(h[1]));this[a]=b=g}!1!==d&&(null===b||b===t?this.$$element.removeAttr(e):this.$$element.attr(e,b));(a=this.$$observers)&&r(a[f],function(a){try{a(b)}catch(d){c(d)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers=ia()),e=d[a]||(d[a]=[]);e.push(b);
m.$evalAsync(function(){!e.$$inter&&c.hasOwnProperty(a)&&b(c[a])});return function(){Xa(e,b)}}};var V=b.startSymbol(),ma=b.endSymbol(),Tc="{{"==V||"}}"==ma?ra:function(a){return a.replace(/\{\{/g,V).replace(/}}/g,ma)},U=/^ngAttr[A-Z]/;D.$$addBindingInfo=k?function(a,b){var c=a.data("$binding")||[];H(b)?c=c.concat(b):c.push(b);a.data("$binding",c)}:E;D.$$addBindingClass=k?function(a){I(a,"ng-binding")}:E;D.$$addScopeInfo=k?function(a,b,c,d){a.data(c?d?"$isolateScopeNoTemplate":"$isolateScope":"$scope",
b)}:E;D.$$addScopeClass=k?function(a,b){I(a,b?"ng-isolate-scope":"ng-scope")}:E;return D}]}function xa(b){return db(b.replace(Sc,""))}function Vc(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 Uc(b){b=A(b);var a=b.length;if(1>=a)return b;for(;a--;)8===b[a].nodeType&&qf.call(b,a,1);return b}function Fe(){var b={},a=!1,c=/^(\S+)(\s+as\s+(\w+))?$/;this.register=function(a,c){La(a,
"controller");J(a)?w(b,a):b[a]=c};this.allowGlobals=function(){a=!0};this.$get=["$injector","$window",function(d,e){function f(a,b,c,d){if(!a||!J(a.$scope))throw R("$controller")("noscp",d,b);a.$scope[b]=c}return function(g,h,l,k){var n,p,q;l=!0===l;k&&C(k)&&(q=k);if(C(g)){k=g.match(c);if(!k)throw rf("ctrlfmt",g);p=k[1];q=q||k[3];g=b.hasOwnProperty(p)?b[p]:xc(h.$scope,p,!0)||(a?xc(e,p,!0):t);sb(g,p,!0)}if(l)return l=(H(g)?g[g.length-1]:g).prototype,n=Object.create(l||null),q&&f(h,q,n,p||g.name),w(function(){d.invoke(g,
n,h,p);return n},{instance:n,identifier:q});n=d.instantiate(g,h,p);q&&f(h,q,n,p||g.name);return n}}]}function Ge(){this.$get=["$window",function(b){return A(b.document)}]}function He(){this.$get=["$log",function(b){return function(a,c){b.error.apply(b,arguments)}}]}function Zb(b,a){if(C(b)){var c=b.replace(sf,"").trim();if(c){var d=a("Content-Type");(d=d&&0===d.indexOf(Wc))||(d=(d=c.match(tf))&&uf[d[0]].test(c));d&&(b=qc(c))}}return b}function Xc(b){var a=ia(),c,d,e;if(!b)return a;r(b.split("\n"),
function(b){e=b.indexOf(":");c=z(N(b.substr(0,e)));d=N(b.substr(e+1));c&&(a[c]=a[c]?a[c]+", "+d:d)});return a}function Yc(b){var a=J(b)?b:t;return function(c){a||(a=Xc(b));return c?(c=a[z(c)],void 0===c&&(c=null),c):a}}function Zc(b,a,c,d){if(G(d))return d(b,a,c);r(d,function(d){b=d(b,a,c)});return b}function Ke(){var b=this.defaults={transformResponse:[Zb],transformRequest:[function(a){return J(a)&&"[object File]"!==Ca.call(a)&&"[object Blob]"!==Ca.call(a)&&"[object FormData]"!==Ca.call(a)?$a(a):
a}],headers:{common:{Accept:"application/json, text/plain, */*"},post:sa($b),put:sa($b),patch:sa($b)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN"},a=!1;this.useApplyAsync=function(b){return y(b)?(a=!!b,this):a};var c=this.interceptors=[];this.$get=["$httpBackend","$browser","$cacheFactory","$rootScope","$q","$injector",function(d,e,f,g,h,l){function k(a){function c(a){var b=w({},a);b.data=a.data?Zc(a.data,a.headers,a.status,e.transformResponse):a.data;a=a.status;return 200<=a&&300>a?
b:h.reject(b)}function d(a){var b,c={};r(a,function(a,d){G(a)?(b=a(),null!=b&&(c[d]=b)):c[d]=a});return c}if(!ca.isObject(a))throw R("$http")("badreq",a);var e=w({method:"get",transformRequest:b.transformRequest,transformResponse:b.transformResponse},a);e.headers=function(a){var c=b.headers,e=w({},a.headers),f,g,c=w({},c.common,c[z(a.method)]);a:for(f in c){a=z(f);for(g in e)if(z(g)===a)continue a;e[f]=c[f]}return d(e)}(a);e.method=ub(e.method);var f=[function(a){var d=a.headers,e=Zc(a.data,Yc(d),
t,a.transformRequest);x(e)&&r(d,function(a,b){"content-type"===z(b)&&delete d[b]});x(a.withCredentials)&&!x(b.withCredentials)&&(a.withCredentials=b.withCredentials);return n(a,e).then(c,c)},t],g=h.when(e);for(r(u,function(a){(a.request||a.requestError)&&f.unshift(a.request,a.requestError);(a.response||a.responseError)&&f.push(a.response,a.responseError)});f.length;){a=f.shift();var k=f.shift(),g=g.then(a,k)}g.success=function(a){g.then(function(b){a(b.data,b.status,b.headers,e)});return g};g.error=
function(a){g.then(null,function(b){a(b.data,b.status,b.headers,e)});return g};return g}function n(c,f){function l(b,c,d,e){function f(){m(c,b,d,e)}I&&(200<=b&&300>b?I.put(P,[b,c,Xc(d),e]):I.remove(P));a?g.$applyAsync(f):(f(),g.$$phase||g.$apply())}function m(a,b,d,e){b=Math.max(b,0);(200<=b&&300>b?L.resolve:L.reject)({data:a,status:b,headers:Yc(d),config:c,statusText:e})}function n(a){m(a.data,a.status,sa(a.headers()),a.statusText)}function u(){var a=k.pendingRequests.indexOf(c);-1!==a&&k.pendingRequests.splice(a,
1)}var L=h.defer(),B=L.promise,I,D,S=c.headers,P=p(c.url,c.params);k.pendingRequests.push(c);B.then(u,u);!c.cache&&!b.cache||!1===c.cache||"GET"!==c.method&&"JSONP"!==c.method||(I=J(c.cache)?c.cache:J(b.cache)?b.cache:q);I&&(D=I.get(P),y(D)?D&&G(D.then)?D.then(n,n):H(D)?m(D[1],D[0],sa(D[2]),D[3]):m(D,200,{},"OK"):I.put(P,B));x(D)&&((D=$c(c.url)?e.cookies()[c.xsrfCookieName||b.xsrfCookieName]:t)&&(S[c.xsrfHeaderName||b.xsrfHeaderName]=D),d(c.method,P,f,l,S,c.timeout,c.withCredentials,c.responseType));
return B}function p(a,b){if(!b)return a;var c=[];Ed(b,function(a,b){null===a||x(a)||(H(a)||(a=[a]),r(a,function(a){J(a)&&(a=ga(a)?a.toISOString():$a(a));c.push(Ea(b)+"="+Ea(a))}))});0<c.length&&(a+=(-1==a.indexOf("?")?"?":"&")+c.join("&"));return a}var q=f("$http"),u=[];r(c,function(a){u.unshift(C(a)?l.get(a):l.invoke(a))});k.pendingRequests=[];(function(a){r(arguments,function(a){k[a]=function(b,c){return k(w(c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){r(arguments,function(a){k[a]=
function(b,c,d){return k(w(d||{},{method:a,url:b,data:c}))}})})("post","put","patch");k.defaults=b;return k}]}function vf(){return new Q.XMLHttpRequest}function Le(){this.$get=["$browser","$window","$document",function(b,a,c){return wf(b,vf,b.defer,a.angular.callbacks,c[0])}]}function wf(b,a,c,d,e){function f(a,b,c){var f=e.createElement("script"),n=null;f.type="text/javascript";f.src=a;f.async=!0;n=function(a){f.removeEventListener("load",n,!1);f.removeEventListener("error",n,!1);e.body.removeChild(f);
f=null;var g=-1,u="unknown";a&&("load"!==a.type||d[b].called||(a={type:"error"}),u=a.type,g="error"===a.type?404:200);c&&c(g,u)};f.addEventListener("load",n,!1);f.addEventListener("error",n,!1);e.body.appendChild(f);return n}return function(e,h,l,k,n,p,q,u){function s(){m&&m();F&&F.abort()}function M(a,d,e,f,g){L!==t&&c.cancel(L);m=F=null;a(d,e,f,g);b.$$completeOutstandingRequest(E)}b.$$incOutstandingRequestCount();h=h||b.url();if("jsonp"==z(e)){var v="_"+(d.counter++).toString(36);d[v]=function(a){d[v].data=
a;d[v].called=!0};var m=f(h.replace("JSON_CALLBACK","angular.callbacks."+v),v,function(a,b){M(k,a,d[v].data,"",b);d[v]=E})}else{var F=a();F.open(e,h,!0);r(n,function(a,b){y(a)&&F.setRequestHeader(b,a)});F.onload=function(){var a=F.statusText||"",b="response"in F?F.response:F.responseText,c=1223===F.status?204:F.status;0===c&&(c=b?200:"file"==Aa(h).protocol?404:0);M(k,c,b,F.getAllResponseHeaders(),a)};e=function(){M(k,-1,null,null,"")};F.onerror=e;F.onabort=e;q&&(F.withCredentials=!0);if(u)try{F.responseType=
u}catch(Z){if("json"!==u)throw Z;}F.send(l||null)}if(0<p)var L=c(s,p);else p&&G(p.then)&&p.then(s)}}function Ie(){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(f,g,u,s){function M(c){return c.replace(k,b).replace(n,a)}function v(a){try{var b=a;a=u?e.getTrusted(u,b):e.valueOf(b);var c;if(s&&!y(a))c=a;else if(null==a)c="";
else{switch(typeof a){case "string":break;case "number":a=""+a;break;default:a=$a(a)}c=a}return c}catch(g){c=ac("interr",f,g.toString()),d(c)}}s=!!s;for(var m,F,r=0,L=[],B=[],I=f.length,D=[],S=[];r<I;)if(-1!=(m=f.indexOf(b,r))&&-1!=(F=f.indexOf(a,m+h)))r!==m&&D.push(M(f.substring(r,m))),r=f.substring(m+h,F),L.push(r),B.push(c(r,v)),r=F+l,S.push(D.length),D.push("");else{r!==I&&D.push(M(f.substring(r)));break}if(u&&1<D.length)throw ac("noconcat",f);if(!g||L.length){var P=function(a){for(var b=0,c=
L.length;b<c;b++){if(s&&x(a[b]))return;D[S[b]]=a[b]}return D.join("")};return w(function(a){var b=0,c=L.length,e=Array(c);try{for(;b<c;b++)e[b]=B[b](a);return P(e)}catch(g){a=ac("interr",f,g.toString()),d(a)}},{exp:f,expressions:L,$$watchDelegate:function(a,b,c){var d;return a.$watchGroup(B,function(c,e){var f=P(c);G(b)&&b.call(this,f,c!==e?d:f,a);d=f},c)}})}}var h=b.length,l=a.length,k=new RegExp(b.replace(/./g,f),"g"),n=new RegExp(a.replace(/./g,f),"g");g.startSymbol=function(){return b};g.endSymbol=
function(){return a};return g}]}function Je(){this.$get=["$rootScope","$window","$q","$$q",function(b,a,c,d){function e(e,h,l,k){var n=a.setInterval,p=a.clearInterval,q=0,u=y(k)&&!k,s=(u?d:c).defer(),M=s.promise;l=y(l)?l:0;M.then(null,null,e);M.$$intervalId=n(function(){s.notify(q++);0<l&&q>=l&&(s.resolve(q),p(M.$$intervalId),delete f[M.$$intervalId]);u||b.$apply()},h);f[M.$$intervalId]=s;return M}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 Rd(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0,maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",posSuf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},DATETIME_FORMATS:{MONTH:"January February March April May June July August September October November December".split(" "),SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),
DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss a",shortTime:"h:mm a",ERANAMES:["Before Christ","Anno Domini"],ERAS:["BC","AD"]},pluralCat:function(b){return 1===b?"one":"other"}}}}function bc(b){b=b.split("/");for(var a=b.length;a--;)b[a]=qb(b[a]);
return b.join("/")}function ad(b,a){var c=Aa(b);a.$$protocol=c.protocol;a.$$host=c.hostname;a.$$port=aa(c.port)||xf[c.protocol]||null}function bd(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=sc(d.search);a.$$hash=decodeURIComponent(d.hash);a.$$path&&"/"!=a.$$path.charAt(0)&&(a.$$path="/"+a.$$path)}function ya(b,a){if(0===a.indexOf(b))return a.substr(b.length)}function Ga(b){var a=b.indexOf("#");
return-1==a?b:b.substr(0,a)}function Fb(b){return b.replace(/(#.+)|#$/,"$1")}function cc(b){return b.substr(0,Ga(b).lastIndexOf("/")+1)}function dc(b,a){this.$$html5=!0;a=a||"";var c=cc(b);ad(b,this);this.$$parse=function(a){var b=ya(c,a);if(!C(b))throw Gb("ipthprfx",a,c);bd(b,this);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=Pb(this.$$search),b=this.$$hash?"#"+qb(this.$$hash):"";this.$$url=bc(this.$$path)+(a?"?"+a:"")+b;this.$$absUrl=c+this.$$url.substr(1)};this.$$parseLinkUrl=
function(d,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;(f=ya(b,d))!==t?(g=f,g=(f=ya(a,f))!==t?c+(ya("/",f)||f):b+g):(f=ya(c,d))!==t?g=c+f:c==d+"/"&&(g=c);g&&this.$$parse(g);return!!g}}function ec(b,a){var c=cc(b);ad(b,this);this.$$parse=function(d){d=ya(b,d)||ya(c,d);var e;"#"===d.charAt(0)?(e=ya(a,d),x(e)&&(e=d)):e=this.$$html5?d:"";bd(e,this);d=this.$$path;var f=/^\/[A-Z]:(\/.*)/;0===e.indexOf(b)&&(e=e.replace(b,""));f.exec(e)||(d=(e=f.exec(d))?e[1]:d);this.$$path=d;this.$$compose()};
this.$$compose=function(){var c=Pb(this.$$search),e=this.$$hash?"#"+qb(this.$$hash):"";this.$$url=bc(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=b+(this.$$url?a+this.$$url:"")};this.$$parseLinkUrl=function(a,c){return Ga(b)==Ga(a)?(this.$$parse(a),!0):!1}}function cd(b,a){this.$$html5=!0;ec.apply(this,arguments);var c=cc(b);this.$$parseLinkUrl=function(d,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;b==Ga(d)?f=d:(g=ya(c,d))?f=b+a+g:c===d+"/"&&(f=c);f&&this.$$parse(f);return!!f};this.$$compose=
function(){var c=Pb(this.$$search),e=this.$$hash?"#"+qb(this.$$hash):"";this.$$url=bc(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=b+a+this.$$url}}function Hb(b){return function(){return this[b]}}function dd(b,a){return function(c){if(x(c))return this[b];this[b]=a(c);this.$$compose();return this}}function Me(){var b="",a={enabled:!1,requireBase:!0,rewriteLinks:!0};this.hashPrefix=function(a){return y(a)?(b=a,this):b};this.html5Mode=function(b){return Wa(b)?(a.enabled=b,this):J(b)?(Wa(b.enabled)&&(a.enabled=
b.enabled),Wa(b.requireBase)&&(a.requireBase=b.requireBase),Wa(b.rewriteLinks)&&(a.rewriteLinks=b.rewriteLinks),this):a};this.$get=["$rootScope","$browser","$sniffer","$rootElement","$window",function(c,d,e,f,g){function h(a,b,c){var e=k.url(),f=k.$$state;try{d.url(a,b,c),k.$$state=d.state()}catch(g){throw k.url(e),k.$$state=f,g;}}function l(a,b){c.$broadcast("$locationChangeSuccess",k.absUrl(),a,k.$$state,b)}var k,n;n=d.baseHref();var p=d.url(),q;if(a.enabled){if(!n&&a.requireBase)throw Gb("nobase");
q=p.substring(0,p.indexOf("/",p.indexOf("//")+2))+(n||"/");n=e.history?dc:cd}else q=Ga(p),n=ec;k=new n(q,"#"+b);k.$$parseLinkUrl(p,p);k.$$state=d.state();var u=/^\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=A(b.target);"a"!==va(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");J(h)&&"[object SVGAnimatedString]"===h.toString()&&(h=Aa(h.animVal).href);
u.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))}});Fb(k.absUrl())!=Fb(p)&&d.url(k.absUrl(),!0);var s=!0;d.onUrlChange(function(a,b){c.$evalAsync(function(){var d=k.absUrl(),e=k.$$state,f;k.$$parse(a);k.$$state=b;f=c.$broadcast("$locationChangeStart",a,d,b,e).defaultPrevented;k.absUrl()===a&&(f?(k.$$parse(d),k.$$state=e,h(d,!1,e)):(s=!1,l(d,e)))});c.$$phase||c.$digest()});
c.$watch(function(){var a=Fb(d.url()),b=Fb(k.absUrl()),f=d.state(),g=k.$$replace,q=a!==b||k.$$html5&&e.history&&f!==k.$$state;if(s||q)s=!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):(q&&h(b,g,f===k.$$state?null:k.$$state),l(a,f)))});k.$$replace=!1});return k}]}function Ne(){var b=!0,a=this;this.debugEnabled=function(a){return y(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||E;a=!1;try{a=!!e.apply}catch(l){}return a?function(){var a=[];r(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 ua(b,a){if("__defineGetter__"===b||"__defineSetter__"===b||"__lookupGetter__"===b||"__lookupSetter__"===b||"__proto__"===b)throw na("isecfld",a);return b}function oa(b,a){if(b){if(b.constructor===b)throw na("isecfn",a);if(b.window===b)throw na("isecwindow",a);if(b.children&&(b.nodeName||b.prop&&b.attr&&b.find))throw na("isecdom",a);if(b===Object)throw na("isecobj",a);}return b}function fc(b){return b.constant}function hb(b,a,c,d,e){oa(b,e);oa(a,e);c=c.split(".");for(var f,
g=0;1<c.length;g++){f=ua(c.shift(),e);var h=0===g&&a&&a[f]||b[f];h||(h={},b[f]=h);b=oa(h,e)}f=ua(c.shift(),e);oa(b[f],e);return b[f]=d}function Pa(b){return"constructor"==b}function ed(b,a,c,d,e,f,g){ua(b,f);ua(a,f);ua(c,f);ua(d,f);ua(e,f);var h=function(a){return oa(a,f)},l=g||Pa(b)?h:ra,k=g||Pa(a)?h:ra,n=g||Pa(c)?h:ra,p=g||Pa(d)?h:ra,q=g||Pa(e)?h:ra;return function(f,g){var h=g&&g.hasOwnProperty(b)?g:f;if(null==h)return h;h=l(h[b]);if(!a)return h;if(null==h)return t;h=k(h[a]);if(!c)return h;if(null==
h)return t;h=n(h[c]);if(!d)return h;if(null==h)return t;h=p(h[d]);return e?null==h?t:h=q(h[e]):h}}function yf(b,a){return function(c,d){return b(c,d,oa,a)}}function zf(b,a,c){var d=a.expensiveChecks,e=d?Af:Bf,f=e[b];if(f)return f;var g=b.split("."),h=g.length;if(a.csp)f=6>h?ed(g[0],g[1],g[2],g[3],g[4],c,d):function(a,b){var e=0,f;do f=ed(g[e++],g[e++],g[e++],g[e++],g[e++],c,d)(a,b),b=t,a=f;while(e<h);return f};else{var l="";d&&(l+="s = eso(s, fe);\nl = eso(l, fe);\n");var k=d;r(g,function(a,b){ua(a,
c);var e=(b?"s":'((l&&l.hasOwnProperty("'+a+'"))?l:s)')+"."+a;if(d||Pa(a))e="eso("+e+", fe)",k=!0;l+="if(s == null) return undefined;\ns="+e+";\n"});l+="return s;";a=new Function("s","l","eso","fe",l);a.toString=ea(l);k&&(a=yf(a,c));f=a}f.sharedGetter=!0;f.assign=function(a,c,d){return hb(a,d,b,c,b)};return e[b]=f}function gc(b){return G(b.valueOf)?b.valueOf():Cf.call(b)}function Oe(){var b=ia(),a=ia();this.$get=["$filter","$sniffer",function(c,d){function e(a){var b=a;a.sharedGetter&&(b=function(b,
c){return a(b,c)},b.literal=a.literal,b.constant=a.constant,b.assign=a.assign);return b}function f(a,b){for(var c=0,d=a.length;c<d;c++){var e=a[c];e.constant||(e.inputs?f(e.inputs,b):-1===b.indexOf(e)&&b.push(e))}return b}function g(a,b){return null==a||null==b?a===b:"object"===typeof a&&(a=gc(a),"object"===typeof a)?!1:a===b||a!==a&&b!==b}function h(a,b,c,d){var e=d.$$inputs||(d.$$inputs=f(d.inputs,[])),h;if(1===e.length){var k=g,e=e[0];return a.$watch(function(a){var b=e(a);g(b,k)||(h=d(a),k=b&&
gc(b));return h},b,c)}for(var l=[],q=0,p=e.length;q<p;q++)l[q]=g;return a.$watch(function(a){for(var b=!1,c=0,f=e.length;c<f;c++){var k=e[c](a);if(b||(b=!g(k,l[c])))l[c]=k&&gc(k)}b&&(h=d(a));return h},b,c)}function l(a,b,c,d){var e,f;return e=a.$watch(function(a){return d(a)},function(a,c,d){f=a;G(b)&&b.apply(this,arguments);y(a)&&d.$$postDigest(function(){y(f)&&e()})},c)}function k(a,b,c,d){function e(a){var b=!0;r(a,function(a){y(a)||(b=!1)});return b}var f,g;return f=a.$watch(function(a){return d(a)},
function(a,c,d){g=a;G(b)&&b.call(this,a,c,d);e(a)&&d.$$postDigest(function(){e(g)&&f()})},c)}function n(a,b,c,d){var e;return e=a.$watch(function(a){return d(a)},function(a,c,d){G(b)&&b.apply(this,arguments);e()},c)}function p(a,b){if(!b)return a;var c=a.$$watchDelegate,c=c!==k&&c!==l?function(c,d){var e=a(c,d);return b(e,c,d)}:function(c,d){var e=a(c,d),f=b(e,c,d);return y(e)?f:e};a.$$watchDelegate&&a.$$watchDelegate!==h?c.$$watchDelegate=a.$$watchDelegate:b.$stateful||(c.$$watchDelegate=h,c.inputs=
[a]);return c}var q={csp:d.csp,expensiveChecks:!1},u={csp:d.csp,expensiveChecks:!0};return function(d,f,g){var m,r,t;switch(typeof d){case "string":t=d=d.trim();var L=g?a:b;m=L[t];m||(":"===d.charAt(0)&&":"===d.charAt(1)&&(r=!0,d=d.substring(2)),g=g?u:q,m=new hc(g),m=(new ib(m,c,g)).parse(d),m.constant?m.$$watchDelegate=n:r?(m=e(m),m.$$watchDelegate=m.literal?k:l):m.inputs&&(m.$$watchDelegate=h),L[t]=m);return p(m,f);case "function":return p(d,f);default:return p(E,f)}}}]}function Qe(){this.$get=
["$rootScope","$exceptionHandler",function(b,a){return fd(function(a){b.$evalAsync(a)},a)}]}function Re(){this.$get=["$browser","$exceptionHandler",function(b,a){return fd(function(a){b.defer(a)},a)}]}function fd(b,a){function c(a,b,c){function d(b){return function(c){e||(e=!0,b.call(a,c))}}var e=!1;return[d(b),d(c)]}function d(){this.$$state={status:0}}function e(a,b){return function(c){b.call(a,c)}}function f(c){!c.processScheduled&&c.pending&&(c.processScheduled=!0,b(function(){var b,d,e;e=c.pending;
c.processScheduled=!1;c.pending=t;for(var f=0,g=e.length;f<g;++f){d=e[f][0];b=e[f][c.status];try{G(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=R("$q",TypeError);d.prototype={then:function(a,b,c){var d=new g;this.$$state.pending=this.$$state.pending||[];this.$$state.pending.push([d,a,b,c]);0<this.$$state.status&&
f(this.$$state);return d.promise},"catch":function(a){return this.then(null,a)},"finally":function(a,b){return this.then(function(b){return k(b,!0,a)},function(b){return k(b,!1,a)},b)}};g.prototype={resolve:function(a){this.promise.$$state.status||(a===this.promise?this.$$reject(h("qcycle",a)):this.$$resolve(a))},$$resolve:function(b){var d,e;e=c(this,this.$$resolve,this.$$reject);try{if(J(b)||G(b))d=b&&b.then;G(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(G(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{G(c)&&(d=c())}catch(e){return l(e,!1)}return d&&G(d.then)?d.then(function(){return l(a,b)},function(a){return l(a,!1)}):l(a,b)},n=function(a,b,c,d){var e=new g;e.resolve(a);return e.promise.then(b,c,d)},p=function u(a){if(!G(a))throw h("norslvr",a);if(!(this instanceof u))return new u(a);var b=new g;a(function(a){b.resolve(a)},function(a){b.reject(a)});return b.promise};p.defer=function(){return new g};p.reject=function(a){var b=new g;
b.reject(a);return b.promise};p.when=n;p.all=function(a){var b=new g,c=0,d=H(a)?[]:{};r(a,function(a,e){c++;n(a).then(function(a){d.hasOwnProperty(e)||(d[e]=a,--c||b.resolve(d))},function(a){d.hasOwnProperty(e)||b.reject(a)})});0===c&&b.resolve(d);return b.promise};return p}function $e(){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 Pe(){function b(a){function b(){this.$$watchers=this.$$nextSibling=this.$$childHead=this.$$childTail=null;this.$$listeners={};this.$$listenerCount={};this.$$watchersCount=0;this.$id=++ob;this.$$ChildScope=null}b.prototype=a;return b}var a=10,c=R("$rootScope"),d=null,e=null;this.digestTtl=function(b){arguments.length&&(a=b);return a};this.$get=["$injector","$exceptionHandler",
"$parse","$browser",function(f,g,h,l){function k(a){a.currentScope.$$destroyed=!0}function n(){this.$id=++ob;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.$$isolateBindings=null}function p(a){if(v.$$phase)throw c("inprog",v.$$phase);v.$$phase=a}function q(a,b,c){do a.$$listenerCount[c]-=b,0===a.$$listenerCount[c]&&delete a.$$listenerCount[c];
while(a=a.$parent)}function u(){}function s(){for(;t.length;)try{t.shift()()}catch(a){g(a)}e=null}function M(){null===e&&(e=l.defer(function(){v.$apply(s)}))}n.prototype={constructor:n,$new:function(a,c){var d;c=c||this;a?(d=new n,d.$root=this.$root):(this.$$ChildScope||(this.$$ChildScope=b(this)),d=new this.$$ChildScope);d.$parent=c;d.$$prevSibling=c.$$childTail;c.$$childHead?(c.$$childTail.$$nextSibling=d,c.$$childTail=d):c.$$childHead=c.$$childTail=d;(a||c!=this)&&d.$on("$destroy",k);return d},
$watch:function(a,b,c){var e=h(a);if(e.$$watchDelegate)return e.$$watchDelegate(this,b,c,e);var f=this.$$watchers,g={fn:b,last:u,get:e,exp:a,eq:!!c};d=null;G(b)||(g.fn=E);f||(f=this.$$watchers=[]);f.unshift(g);return function(){Xa(f,g);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)});r(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(!x(e)){if(J(e))if(Sa(e))for(f!==p&&(f=p,u=f.length=0,l++),a=e.length,u!==a&&(l++,f.length=u=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!==n&&(f=n={},u=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)):(u++,f[b]=g,l++));if(u>a)for(b in l++,f)e.hasOwnProperty(b)||(u--,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,q=h(a,c),p=[],n={},m=!0,u=0;return this.$watch(q,function(){m?(m=!1,b(e,e,d)):b(e,g,d);if(k)if(J(e))if(Sa(e)){g=Array(e.length);for(var a=0;a<e.length;a++)g[a]=e[a]}else for(a in g={},e)tc.call(e,a)&&(g[a]=e[a]);else g=e})},$digest:function(){var b,f,h,k,q,n,r=a,t,O=[],M,y;p("$digest");l.$$checkUrlChange();
this===v&&null!==e&&(l.defer.cancel(e),s());d=null;do{n=!1;for(t=this;m.length;){try{y=m.shift(),y.scope.$eval(y.expression,y.locals)}catch(w){g(w)}d=null}a:do{if(k=t.$$watchers)for(q=k.length;q--;)try{if(b=k[q])if((f=b.get(t))!==(h=b.last)&&!(b.eq?ha(f,h):"number"===typeof f&&"number"===typeof h&&isNaN(f)&&isNaN(h)))n=!0,d=b,b.last=b.eq?Da(f,null):f,b.fn(f,h===u?f:h,t),5>r&&(M=4-r,O[M]||(O[M]=[]),O[M].push({msg:G(b.exp)?"fn: "+(b.exp.name||b.exp.toString()):b.exp,newVal:f,oldVal:h}));else if(b===
d){n=!1;break a}}catch(A){g(A)}if(!(k=t.$$childHead||t!==this&&t.$$nextSibling))for(;t!==this&&!(k=t.$$nextSibling);)t=t.$parent}while(t=k);if((n||m.length)&&!r--)throw v.$$phase=null,c("infdig",a,O);}while(n||m.length);for(v.$$phase=null;F.length;)try{F.shift()()}catch(x){g(x)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;if(this!==v){for(var b in this.$$listenerCount)q(this,this.$$listenerCount[b],b);a.$$childHead==this&&(a.$$childHead=
this.$$nextSibling);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=E;this.$on=this.$watch=this.$watchGroup=function(){return E};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){v.$$phase||m.length||l.defer(function(){m.length&&v.$digest()});m.push({scope:this,expression:a,locals:b})},$$postDigest:function(a){F.push(a)},$apply:function(a){try{return p("$apply"),this.$eval(a)}catch(b){g(b)}finally{v.$$phase=null;try{v.$digest()}catch(c){throw g(c),c;}}},$applyAsync:function(a){function b(){c.$eval(a)}var c=this;a&&t.push(b);M()},$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,q(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=Ya([h],arguments,1),l,q;do{d=e.$$listeners[a]||c;h.currentScope=e;l=0;for(q=d.length;l<q;l++)if(d[l])try{d[l].apply(null,k)}catch(p){g(p)}else d.splice(l,1),l--,q--;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=Ya([e],arguments,1),h,l;c=d;){e.currentScope=c;d=c.$$listeners[a]||[];h=0;for(l=d.length;h<l;h++)if(d[h])try{d[h].apply(null,f)}catch(k){g(k)}else d.splice(h,1),h--,l--;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 v=new n,m=v.$$asyncQueue=[],F=v.$$postDigestQueue=[],t=v.$$applyAsyncQueue=[];return v}]}function Sd(){var b=/^\s*(https?|ftp|mailto|tel|file):/,a=/^\s*((https?|ftp|file|blob):|data:image\/)/;this.aHrefSanitizationWhitelist=function(a){return y(a)?(b=a,this):b};this.imgSrcSanitizationWhitelist=function(b){return y(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 Df(b){if("self"===b)return b;if(C(b)){if(-1<b.indexOf("***"))throw Ba("iwcard",b);b=gd(b).replace("\\*\\*",".*").replace("\\*","[^:/.?&;]*");return new RegExp("^"+b+"$")}if(Ua(b))return new RegExp("^"+b.source+"$");throw Ba("imatcher");}function hd(b){var a=[];y(b)&&r(b,function(b){a.push(Df(b))});return a}function Te(){this.SCE_CONTEXTS=pa;var b=["self"],a=[];this.resourceUrlWhitelist=function(a){arguments.length&&(b=hd(a));return b};this.resourceUrlBlacklist=function(b){arguments.length&&
(a=hd(b));return a};this.$get=["$injector",function(c){function d(a,b){return"self"===a?$c(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 Ba("unsafe");};c.has("$sanitize")&&(f=c.get("$sanitize"));var g=e(),h={};h[pa.HTML]=e(g);h[pa.CSS]=e(g);h[pa.URL]=
e(g);h[pa.JS]=e(g);h[pa.RESOURCE_URL]=e(h[pa.URL]);return{trustAs:function(a,b){var c=h.hasOwnProperty(a)?h[a]:null;if(!c)throw Ba("icontext",a,b);if(null===b||b===t||""===b)return b;if("string"!==typeof b)throw Ba("itype",a);return new c(b)},getTrusted:function(c,e){if(null===e||e===t||""===e)return e;var g=h.hasOwnProperty(c)?h[c]:null;if(g&&e instanceof g)return e.$$unwrapTrustedValue();if(c===pa.RESOURCE_URL){var g=Aa(e.toString()),p,q,u=!1;p=0;for(q=b.length;p<q;p++)if(d(b[p],g)){u=!0;break}if(u)for(p=
0,q=a.length;p<q;p++)if(d(a[p],g)){u=!1;break}if(u)return e;throw Ba("insecurl",e.toString());}if(c===pa.HTML)return f(e);throw Ba("unsafe");},valueOf:function(a){return a instanceof g?a.$$unwrapTrustedValue():a}}}]}function Se(){var b=!0;this.enabled=function(a){arguments.length&&(b=!!a);return b};this.$get=["$parse","$sceDelegate",function(a,c){if(b&&8>Qa)throw Ba("iequirks");var d=sa(pa);d.isEnabled=function(){return b};d.trustAs=c.trustAs;d.getTrusted=c.getTrusted;d.valueOf=c.valueOf;b||(d.trustAs=
d.getTrusted=function(a,b){return b},d.valueOf=ra);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;r(pa,function(a,b){var c=z(b);d[db("parse_as_"+c)]=function(b){return e(a,b)};d[db("get_trusted_"+c)]=function(b){return f(a,b)};d[db("trust_as_"+c)]=function(b){return g(a,b)}});return d}]}function Ue(){this.$get=["$window","$document",function(b,a){var c={},d=aa((/android (\d+)/.exec(z((b.navigator||
{}).userAgent))||[])[1]),e=/Boxee/i.test((b.navigator||{}).userAgent),f=a[0]||{},g,h=/^(Moz|webkit|ms)(?=[A-Z])/,l=f.body&&f.body.style,k=!1,n=!1;if(l){for(var p in l)if(k=h.exec(p)){g=k[0];g=g.substr(0,1).toUpperCase()+g.substr(1);break}g||(g="WebkitOpacity"in l&&"webkit");k=!!("transition"in l||g+"Transition"in l);n=!!("animation"in l||g+"Animation"in l);!d||k&&n||(k=C(f.body.style.webkitTransition),n=C(f.body.style.webkitAnimation))}return{history:!(!b.history||!b.history.pushState||4>d||e),hasEvent:function(a){if("input"===
a&&11>=Qa)return!1;if(x(c[a])){var b=f.createElement("div");c[a]="on"+a in b}return c[a]},csp:bb(),vendorPrefix:g,transitions:k,animations:n,android:d}}]}function We(){this.$get=["$templateCache","$http","$q",function(b,a,c){function d(e,f){d.totalPendingRequests++;var g=a.defaults&&a.defaults.transformResponse;H(g)?g=g.filter(function(a){return a!==Zb}):g===Zb&&(g=null);return a.get(e,{cache:b,transformResponse:g})["finally"](function(){d.totalPendingRequests--}).then(function(a){return a.data},
function(a){if(!f)throw la("tpload",e);return c.reject(a)})}d.totalPendingRequests=0;return d}]}function Xe(){this.$get=["$rootScope","$browser","$location",function(b,a,c){return{findBindings:function(a,b,c){a=a.getElementsByClassName("ng-binding");var g=[];r(a,function(a){var d=ca.element(a).data("$binding");d&&r(d,function(d){c?(new RegExp("(^|\\s)"+gd(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 Ye(){this.$get=["$rootScope","$browser","$q","$$q","$exceptionHandler",function(b,a,c,d,e){function f(f,l,k){var n=y(k)&&!k,p=(n?d:c).defer(),q=p.promise;l=a.defer(function(){try{p.resolve(f())}catch(a){p.reject(a),e(a)}finally{delete g[q.$$timeoutId]}n||
b.$apply()},l);q.$$timeoutId=l;g[l]=p;return q}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){Qa&&($.setAttribute("href",b),b=$.href);$.setAttribute("href",b);return{href:$.href,protocol:$.protocol?$.protocol.replace(/:$/,""):"",host:$.host,search:$.search?$.search.replace(/^\?/,""):"",hash:$.hash?$.hash.replace(/^#/,""):"",hostname:$.hostname,port:$.port,pathname:"/"===
$.pathname.charAt(0)?$.pathname:"/"+$.pathname}}function $c(b){b=C(b)?Aa(b):b;return b.protocol===id.protocol&&b.host===id.host}function Ze(){this.$get=ea(Q)}function Fc(b){function a(c,d){if(J(c)){var e={};r(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",jd);a("date",kd);a("filter",Ef);a("json",Ff);a("limitTo",Gf);a("lowercase",Hf);a("number",ld);a("orderBy",md);a("uppercase",
If)}function Ef(){return function(b,a,c){if(!H(b))return b;var d;switch(typeof a){case "function":break;case "boolean":case "number":case "string":d=!0;case "object":a=Jf(a,c,d);break;default:return b}return b.filter(a)}}function Jf(b,a,c){var d=J(b)&&"$"in b;!0===a?a=ha:G(a)||(a=function(a,b){if(J(a)||J(b))return!1;a=z(""+a);b=z(""+b);return-1!==a.indexOf(b)});return function(e){return d&&!J(e)?Ha(e,b.$,a,!1):Ha(e,b,a,c)}}function Ha(b,a,c,d,e){var f=null!==b?typeof b:"null",g=null!==a?typeof a:
"null";if("string"===g&&"!"===a.charAt(0))return!Ha(b,a.substring(1),c,d);if(H(b))return b.some(function(b){return Ha(b,a,c,d)});switch(f){case "object":var h;if(d){for(h in b)if("$"!==h.charAt(0)&&Ha(b[h],a,c,!0))return!0;return e?!1:Ha(b,a,c,!1)}if("object"===g){for(h in a)if(e=a[h],!G(e)&&!x(e)&&(f="$"===h,!Ha(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 jd(b){var a=b.NUMBER_FORMATS;return function(b,d,e){x(d)&&(d=a.CURRENCY_SYM);x(e)&&
(e=a.PATTERNS[1].maxFrac);return null==b?b:nd(b,a.PATTERNS[1],a.GROUP_SEP,a.DECIMAL_SEP,e).replace(/\u00A4/g,d)}}function ld(b){var a=b.NUMBER_FORMATS;return function(b,d){return null==b?b:nd(b,a.PATTERNS[0],a.GROUP_SEP,a.DECIMAL_SEP,d)}}function nd(b,a,c,d,e){if(!isFinite(b)||J(b))return"";var f=0>b;b=Math.abs(b);var g=b+"",h="",l=[],k=!1;if(-1!==g.indexOf("e")){var n=g.match(/([\d\.]+)e(-?)(\d+)/);n&&"-"==n[2]&&n[3]>e+1?b=0:(h=g,k=!0)}if(k)0<e&&1>b&&(h=b.toFixed(e),b=parseFloat(h));else{g=(g.split(od)[1]||
"").length;x(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(od),k=g[0],g=g[1]||"",p=0,q=a.lgSize,u=a.gSize;if(k.length>=q+u)for(p=k.length-q,n=0;n<p;n++)0===(p-n)%u&&0!==n&&(h+=c),h+=k.charAt(n);for(n=p;n<k.length;n++)0===(k.length-n)%q&&0!==n&&(h+=c),h+=k.charAt(n);for(;g.length<e;)g+="0";e&&"0"!==e&&(h+=d+g.substr(0,e))}0===b&&(f=!1);l.push(f?a.negPre:a.posPre,h,f?a.negSuf:a.posSuf);return l.join("")}function Ib(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 U(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 Ib(e,a,d)}}function Jb(b,a){return function(c,d){var e=c["get"+b](),f=ub(a?"SHORT"+b:b);return d[f][e]}}function pd(b){var a=(new Date(b,0,1)).getDay();return new Date(b,0,(4>=a?5:12)-a)}function qd(b){return function(a){var c=pd(a.getFullYear());a=+new Date(a.getFullYear(),a.getMonth(),a.getDate()+
(4-a.getDay()))-+c;a=1+Math.round(a/6048E5);return Ib(a,b)}}function ic(b,a){return 0>=b.getFullYear()?a.ERAS[0]:a.ERAS[1]}function kd(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=aa(b[9]+b[10]),g=aa(b[9]+b[11]));h.call(a,aa(b[1]),aa(b[2])-1,aa(b[3]));f=aa(b[4]||0)-f;g=aa(b[5]||0)-g;h=aa(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;C(c)&&(c=Kf.test(c)?aa(c):a(c));Y(c)&&(c=new Date(c));if(!ga(c))return c;for(;e;)(k=Lf.exec(e))?(h=Ya(h,k,1),e=h.pop()):(h.push(e),e=null);f&&"UTC"===f&&(c=new Date(c.getTime()),c.setMinutes(c.getMinutes()+c.getTimezoneOffset()));r(h,function(a){l=Mf[a];g+=l?l(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function Ff(){return function(b,a){x(a)&&(a=2);return $a(b,a)}}function Gf(){return function(b,
a){Y(b)&&(b=b.toString());return H(b)||C(b)?(a=Infinity===Math.abs(Number(a))?Number(a):aa(a))?0<a?b.slice(0,a):b.slice(a):C(b)?"":[]:b}}function md(b){return function(a,c,d){function e(a,b){return b?function(b,c){return a(c,b)}:a}function f(a){switch(typeof a){case "number":case "boolean":case "string":return!0;default:return!1}}function g(a){return null===a?"null":"function"===typeof a.valueOf&&(a=a.valueOf(),f(a))||"function"===typeof a.toString&&(a=a.toString(),f(a))?a:""}function h(a,b){var c=
typeof a,d=typeof b;c===d&&"object"===c&&(a=g(a),b=g(b));return c===d?("string"===c&&(a=a.toLowerCase(),b=b.toLowerCase()),a===b?0:a<b?-1:1):c<d?-1:1}if(!Sa(a))return a;c=H(c)?c:[c];0===c.length&&(c=["+"]);c=c.map(function(a){var c=!1,d=a||ra;if(C(a)){if("+"==a.charAt(0)||"-"==a.charAt(0))c="-"==a.charAt(0),a=a.substring(1);if(""===a)return e(h,c);d=b(a);if(d.constant){var f=d();return e(function(a,b){return h(a[f],b[f])},c)}}return e(function(a,b){return h(d(a),d(b))},c)});return Za.call(a).sort(e(function(a,
b){for(var d=0;d<c.length;d++){var e=c[d](a,b);if(0!==e)return e}return 0},d))}}function Ia(b){G(b)&&(b={link:b});b.restrict=b.restrict||"AC";return ea(b)}function rd(b,a,c,d,e){var f=this,g=[],h=f.$$parentForm=b.parent().controller("form")||Kb;f.$error={};f.$$success={};f.$pending=t;f.$name=e(a.name||a.ngForm||"")(c);f.$dirty=!1;f.$pristine=!0;f.$valid=!0;f.$invalid=!1;f.$submitted=!1;h.$addControl(f);f.$rollbackViewValue=function(){r(g,function(a){a.$rollbackViewValue()})};f.$commitViewValue=function(){r(g,
function(a){a.$commitViewValue()})};f.$addControl=function(a){La(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];r(f.$pending,function(b,c){f.$setValidity(c,null,a)});r(f.$error,function(b,c){f.$setValidity(c,null,a)});r(f.$$success,function(b,c){f.$setValidity(c,null,a)});Xa(g,a)};sd({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&&(Xa(d,c),0===d.length&&delete a[b])},parentForm:h,$animate:d});f.$setDirty=function(){d.removeClass(b,Ra);d.addClass(b,Lb);f.$dirty=!0;f.$pristine=!1;h.$setDirty()};f.$setPristine=function(){d.setClass(b,Ra,Lb+" ng-submitted");f.$dirty=!1;f.$pristine=!0;f.$submitted=!1;r(g,function(a){a.$setPristine()})};f.$setUntouched=function(){r(g,function(a){a.$setUntouched()})};f.$setSubmitted=function(){d.addClass(b,"ng-submitted");
f.$submitted=!0;h.$setSubmitted()}}function jc(b){b.$formatters.push(function(a){return b.$isEmpty(a)?a:a.toString()})}function jb(b,a,c,d,e,f){var g=z(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=N(e));(d.$viewValue!==e||""===e&&d.$$hasNativeValidators)&&d.$setViewValue(e,b)}};if(e.hasEvent("input"))a.on("input",
l);else{var k,n=function(a,b,c){k||(k=f.defer(function(){k=null;b&&b.value===c||l(a)}))};a.on("keydown",function(a){var b=a.keyCode;91===b||15<b&&19>b||37<=b&&40>=b||n(a,this,this.value)});if(e.hasEvent("paste"))a.on("paste cut",n)}a.on("change",l);d.$render=function(){a.val(d.$isEmpty(d.$viewValue)?"":d.$viewValue)}}function Mb(b,a){return function(c,d){var e,f;if(ga(c))return c;if(C(c)){'"'==c.charAt(0)&&'"'==c.charAt(c.length-1)&&(c=c.substring(1,c.length-1));if(Nf.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},r(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 kb(b,a,c,d){return function(e,f,g,h,l,k,n){function p(a){return a&&!(a.getTime&&a.getTime()!==a.getTime())}function q(a){return y(a)?ga(a)?a:c(a):t}td(e,f,g,h);
jb(e,f,g,h,l,k);var u=h&&h.$options&&h.$options.timezone,s;h.$$parserName=b;h.$parsers.push(function(b){return h.$isEmpty(b)?null:a.test(b)?(b=c(b,s),"UTC"===u&&b.setMinutes(b.getMinutes()-b.getTimezoneOffset()),b):t});h.$formatters.push(function(a){if(a&&!ga(a))throw Nb("datefmt",a);if(p(a)){if((s=a)&&"UTC"===u){var b=6E4*s.getTimezoneOffset();s=new Date(s.getTime()+b)}return n("date")(a,d,u)}s=null;return""});if(y(g.min)||g.ngMin){var r;h.$validators.min=function(a){return!p(a)||x(r)||c(a)>=r};
g.$observe("min",function(a){r=q(a);h.$validate()})}if(y(g.max)||g.ngMax){var v;h.$validators.max=function(a){return!p(a)||x(v)||c(a)<=v};g.$observe("max",function(a){v=q(a);h.$validate()})}}}function td(b,a,c,d){(d.$$hasNativeValidators=J(a[0].validity))&&d.$parsers.push(function(b){var c=a.prop("validity")||{};return c.badInput&&!c.typeMismatch?t:b})}function ud(b,a,c,d,e){if(y(d)){b=b(d);if(!b.constant)throw R("ngModel")("constexpr",c,d);return b(a)}return e}function kc(b,a){b="ngClass"+b;return["$animate",
function(c){function d(a,b){var c=[],d=0;a:for(;d<a.length;d++){for(var e=a[d],n=0;n<b.length;n++)if(e==b[n])continue a;c.push(e)}return c}function e(a){if(!H(a)){if(C(a))return a.split(" ");if(J(a)){var b=[];r(a,function(a,c){a&&(b=b.concat(c.split(" ")))});return b}}return a}return{restrict:"AC",link:function(f,g,h){function l(a,b){var c=g.data("$classCounts")||{},d=[];r(a,function(a){if(0<b||c[a])c[a]=(c[a]||0)+b,c[a]===+(0<b)&&d.push(a)});g.data("$classCounts",c);return d.join(" ")}function k(b){if(!0===
a||f.$index%2===a){var k=e(b||[]);if(!n){var u=l(k,1);h.$addClass(u)}else if(!ha(b,n)){var s=e(n),u=d(k,s),k=d(s,k),u=l(u,1),k=l(k,-1);u&&u.length&&c.addClass(g,u);k&&k.length&&c.removeClass(g,k)}}n=sa(b)}var n;f.$watch(h[b],k,!0);h.$observe("class",function(a){k(f.$eval(h[b]))});"ngClass"!==b&&f.$watch("$index",function(c,d){var g=c&1;if(g!==(d&1)){var k=e(f.$eval(h[b]));g===a?(g=l(k,1),h.$addClass(g)):(g=l(k,-1),h.$removeClass(g))}})}}}]}function sd(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?"-"+vc(b,"-"):"";a(lb+b,!0===c);a(vd+b,!1===c)}var d=b.ctrl,e=b.$element,f={},g=b.set,h=b.unset,l=b.parentForm,k=b.$animate;f[vd]=!(f[lb]=e.hasClass(lb));d.$setValidity=function(b,e,f){e===t?(d.$pending||(d.$pending={}),g(d.$pending,b,f)):(d.$pending&&h(d.$pending,b,f),wd(d.$pending)&&(d.$pending=t));Wa(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(xd,
!0),d.$valid=d.$invalid=t,c("",null)):(a(xd,!1),d.$valid=wd(d.$error),d.$invalid=!d.$valid,c("",d.$valid));e=d.$pending&&d.$pending[b]?t:d.$error[b]?!1:d.$$success[b]?!0:null;c(b,e);l.$setValidity(b,e,d)}}function wd(b){if(b)for(var a in b)return!1;return!0}var Of=/^\/(.+)\/([a-z]*)$/,z=function(b){return C(b)?b.toLowerCase():b},tc=Object.prototype.hasOwnProperty,ub=function(b){return C(b)?b.toUpperCase():b},Qa,A,ta,Za=[].slice,qf=[].splice,Pf=[].push,Ca=Object.prototype.toString,Ja=R("ng"),ca=Q.angular||
(Q.angular={}),cb,ob=0;Qa=W.documentMode;E.$inject=[];ra.$inject=[];var H=Array.isArray,N=function(b){return C(b)?b.trim():b},gd=function(b){return b.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08")},bb=function(){if(y(bb.isActive_))return bb.isActive_;var b=!(!W.querySelector("[ng-csp]")&&!W.querySelector("[data-ng-csp]"));if(!b)try{new Function("")}catch(a){b=!0}return bb.isActive_=b},rb=["ng-","data-ng-","ng:","x-ng-"],Md=/[A-Z]/g,wc=!1,Qb,qa=1,pb=3,Qd={full:"1.3.15",major:1,
minor:3,dot:15,codeName:"locality-filtration"};T.expando="ng339";var zb=T.cache={},hf=1;T._data=function(b){return this.cache[b[this.expando]]||{}};var cf=/([\:\-\_]+(.))/g,df=/^moz([A-Z])/,Qf={mouseleave:"mouseout",mouseenter:"mouseover"},Tb=R("jqLite"),gf=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,Sb=/<|&#?\w+;/,ef=/<([\w:]+)/,ff=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ja={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,"",""]};ja.optgroup=ja.option;ja.tbody=ja.tfoot=ja.colgroup=ja.caption=ja.thead;ja.th=ja.td;var Ka=T.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=!1;"complete"===W.readyState?setTimeout(a):(this.on("DOMContentLoaded",a),T(Q).on("load",a))},toString:function(){var b=[];r(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return 0<=
b?A(this[b]):A(this[this.length+b])},length:0,push:Pf,sort:[].sort,splice:[].splice},Eb={};r("multiple selected checked disabled readOnly required open".split(" "),function(b){Eb[z(b)]=b});var Oc={};r("input select option textarea button form details".split(" "),function(b){Oc[b]=!0});var Pc={ngMinlength:"minlength",ngMaxlength:"maxlength",ngMin:"min",ngMax:"max",ngPattern:"pattern"};r({data:Vb,removeData:xb},function(b,a){T[a]=b});r({data:Vb,inheritedData:Db,scope:function(b){return A.data(b,"$scope")||
Db(b.parentNode||b,["$isolateScope","$scope"])},isolateScope:function(b){return A.data(b,"$isolateScope")||A.data(b,"$isolateScopeNoTemplate")},controller:Kc,injector:function(b){return Db(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:Ab,css:function(b,a,c){a=db(a);if(y(c))b.style[a]=c;else return b.style[a]},attr:function(b,a,c){var d=z(a);if(Eb[d])if(y(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||E).specified?
d:t;else if(y(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),null===b?t:b},prop:function(b,a,c){if(y(c))b[a]=c;else return b[a]},text:function(){function b(a,b){if(x(b)){var d=a.nodeType;return d===qa||d===pb?a.textContent:""}a.textContent=b}b.$dv="";return b}(),val:function(b,a){if(x(a)){if(b.multiple&&"select"===va(b)){var c=[];r(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(x(a))return b.innerHTML;
wb(b,!0);b.innerHTML=a},empty:Lc},function(b,a){T.prototype[a]=function(a,d){var e,f,g=this.length;if(b!==Lc&&(2==b.length&&b!==Ab&&b!==Kc?a:d)===t){if(J(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===t?Math.min(g,1):g;for(f=0;f<g;f++){var h=b(this[f],a,d);e=e?e+h:h}return e}for(e=0;e<g;e++)b(this[e],a,d);return this}});r({removeData:xb,on:function a(c,d,e,f){if(y(f))throw Tb("onargs");if(Gc(c)){var g=yb(c,!0);f=g.events;var h=g.handle;h||(h=
g.handle=lf(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,Qf[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:Jc,one:function(a,c,d){a=A(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;wb(a);r(new T(c),function(c){d?e.insertBefore(c,d.nextSibling):e.replaceChild(c,
a);d=c})},children:function(a){var c=[];r(a.childNodes,function(a){a.nodeType===qa&&c.push(a)});return c},contents:function(a){return a.contentDocument||a.childNodes||[]},append:function(a,c){var d=a.nodeType;if(d===qa||11===d){c=new T(c);for(var d=0,e=c.length;d<e;d++)a.appendChild(c[d])}},prepend:function(a,c){if(a.nodeType===qa){var d=a.firstChild;r(new T(c),function(c){a.insertBefore(c,d)})}},wrap:function(a,c){c=A(c).eq(0).clone()[0];var d=a.parentNode;d&&d.replaceChild(c,a);c.appendChild(a)},
remove:Mc,detach:function(a){Mc(a,!0)},after:function(a,c){var d=a,e=a.parentNode;c=new T(c);for(var f=0,g=c.length;f<g;f++){var h=c[f];e.insertBefore(h,d.nextSibling);d=h}},addClass:Cb,removeClass:Bb,toggleClass:function(a,c,d){c&&r(c.split(" "),function(c){var f=d;x(f)&&(f=!Ab(a,c));(f?Cb:Bb)(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=yb(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:E,type:g,target:a},c.type&&(e=w(e,c)),c=sa(h),f=d?[e].concat(d):[e],r(c,function(c){e.isImmediatePropagationStopped()||c.apply(a,
f)})}},function(a,c){T.prototype[c]=function(c,e,f){for(var g,h=0,l=this.length;h<l;h++)x(g)?(g=a(this[h],c,e,f),y(g)&&(g=A(g))):Ic(g,a(this[h],c,e,f));return y(g)?g:this};T.prototype.bind=T.prototype.on;T.prototype.unbind=T.prototype.off});eb.prototype={put:function(a,c){this[Ma(a,this.nextUid)]=c},get:function(a){return this[Ma(a,this.nextUid)]},remove:function(a){var c=this[a=Ma(a,this.nextUid)];delete this[a];return c}};var Rc=/^function\s*[^\(]*\(\s*([^\)]*)\)/m,Rf=/,/,Sf=/^\s*(_?)(\S+?)\1\s*$/,
Qc=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Fa=R("$injector");ab.$$annotate=function(a,c,d){var e;if("function"===typeof a){if(!(e=a.$inject)){e=[];if(a.length){if(c)throw C(d)&&d||(d=a.name||mf(a)),Fa("strictdi",d);c=a.toString().replace(Qc,"");c=c.match(Rc);r(c[1].split(Rf),function(a){a.replace(Sf,function(a,c,d){e.push(d)})})}a.$inject=e}}else H(a)?(c=a.length-1,sb(a[c],"fn"),e=a.slice(0,c)):sb(a,"fn",!0);return e};var Tf=R("$animate"),Ce=["$provide",function(a){this.$$selectors={};this.register=function(c,
d){var e=c+"-animation";if(c&&"."!=c.charAt(0))throw Tf("notcsel",c);this.$$selectors[c.substr(1)]=e;a.factory(e,d)};this.classNameFilter=function(a){1===arguments.length&&(this.$$classNameFilter=a instanceof RegExp?a:null);return this.$$classNameFilter};this.$get=["$$q","$$asyncCallback","$rootScope",function(a,d,e){function f(d){var f,g=a.defer();g.promise.$$cancelFn=function(){f&&f()};e.$$postDigest(function(){f=d(function(){g.resolve()})});return g.promise}function g(a,c){var d=[],e=[],f=ia();
r((a.attr("class")||"").split(/\s+/),function(a){f[a]=!0});r(c,function(a,c){var g=f[c];!1===a&&g?e.push(c):!0!==a||g||d.push(c)});return 0<d.length+e.length&&[d.length?d:null,e.length?e:null]}function h(a,c,d){for(var e=0,f=c.length;e<f;++e)a[c[e]]=d}function l(){n||(n=a.defer(),d(function(){n.resolve();n=null}));return n.promise}function k(a,c){if(ca.isObject(c)){var d=w(c.from||{},c.to||{});a.css(d)}}var n;return{animate:function(a,c,d){k(a,{from:c,to:d});return l()},enter:function(a,c,d,e){k(a,
e);d?d.after(a):c.prepend(a);return l()},leave:function(a,c){k(a,c);a.remove();return l()},move:function(a,c,d,e){return this.enter(a,c,d,e)},addClass:function(a,c,d){return this.setClass(a,c,[],d)},$$addClassImmediately:function(a,c,d){a=A(a);c=C(c)?c:H(c)?c.join(" "):"";r(a,function(a){Cb(a,c)});k(a,d);return l()},removeClass:function(a,c,d){return this.setClass(a,[],c,d)},$$removeClassImmediately:function(a,c,d){a=A(a);c=C(c)?c:H(c)?c.join(" "):"";r(a,function(a){Bb(a,c)});k(a,d);return l()},setClass:function(a,
c,d,e){var k=this,l=!1;a=A(a);var m=a.data("$$animateClasses");m?e&&m.options&&(m.options=ca.extend(m.options||{},e)):(m={classes:{},options:e},l=!0);e=m.classes;c=H(c)?c:c.split(" ");d=H(d)?d:d.split(" ");h(e,c,!0);h(e,d,!1);l&&(m.promise=f(function(c){var d=a.data("$$animateClasses");a.removeData("$$animateClasses");if(d){var e=g(a,d.classes);e&&k.$$setClassImmediately(a,e[0],e[1],d.options)}c()}),a.data("$$animateClasses",m));return m.promise},$$setClassImmediately:function(a,c,d,e){c&&this.$$addClassImmediately(a,
c);d&&this.$$removeClassImmediately(a,d);k(a,e);return l()},enabled:E,cancel:E}}]}],la=R("$compile");yc.$inject=["$provide","$$sanitizeUriProvider"];var Sc=/^((?:x|data)[\:\-_])/i,rf=R("$controller"),Wc="application/json",$b={"Content-Type":Wc+";charset=utf-8"},tf=/^\[|^\{(?!\{)/,uf={"[":/]$/,"{":/}$/},sf=/^\)\]\}',?\n/,ac=R("$interpolate"),Uf=/^([^\?#]*)(\?([^#]*))?(#(.*))?$/,xf={http:80,https:443,ftp:21},Gb=R("$location"),Vf={$$html5:!1,$$replace:!1,absUrl:Hb("$$absUrl"),url:function(a){if(x(a))return this.$$url;
var c=Uf.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:Hb("$$protocol"),host:Hb("$$host"),port:Hb("$$port"),path:dd("$$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(C(a)||Y(a))a=a.toString(),this.$$search=sc(a);else if(J(a))a=Da(a,{}),r(a,function(c,e){null==c&&delete a[e]}),this.$$search=
a;else throw Gb("isrcharg");break;default:x(c)||null===c?delete this.$$search[a]:this.$$search[a]=c}this.$$compose();return this},hash:dd("$$hash",function(a){return null!==a?a.toString():""}),replace:function(){this.$$replace=!0;return this}};r([cd,ec,dc],function(a){a.prototype=Object.create(Vf);a.prototype.state=function(c){if(!arguments.length)return this.$$state;if(a!==dc||!this.$$html5)throw Gb("nostate");this.$$state=x(c)?null:c;return this}});var na=R("$parse"),Wf=Function.prototype.call,
Xf=Function.prototype.apply,Yf=Function.prototype.bind,mb=ia();r({"null":function(){return null},"true":function(){return!0},"false":function(){return!1},undefined:function(){}},function(a,c){a.constant=a.literal=a.sharedGetter=!0;mb[c]=a});mb["this"]=function(a){return a};mb["this"].sharedGetter=!0;var nb=w(ia(),{"+":function(a,c,d,e){d=d(a,c);e=e(a,c);return y(d)?y(e)?d+e:d:y(e)?e:t},"-":function(a,c,d,e){d=d(a,c);e=e(a,c);return(y(d)?d:0)-(y(e)?e:0)},"*":function(a,c,d,e){return d(a,c)*e(a,c)},
"/":function(a,c,d,e){return d(a,c)/e(a,c)},"%":function(a,c,d,e){return d(a,c)%e(a,c)},"===":function(a,c,d,e){return d(a,c)===e(a,c)},"!==":function(a,c,d,e){return d(a,c)!==e(a,c)},"==":function(a,c,d,e){return d(a,c)==e(a,c)},"!=":function(a,c,d,e){return d(a,c)!=e(a,c)},"<":function(a,c,d,e){return d(a,c)<e(a,c)},">":function(a,c,d,e){return d(a,c)>e(a,c)},"<=":function(a,c,d,e){return d(a,c)<=e(a,c)},">=":function(a,c,d,e){return d(a,c)>=e(a,c)},"&&":function(a,c,d,e){return d(a,c)&&e(a,c)},
"||":function(a,c,d,e){return d(a,c)||e(a,c)},"!":function(a,c,d){return!d(a,c)},"=":!0,"|":!0}),Zf={n:"\n",f:"\f",r:"\r",t:"\t",v:"\v","'":"'",'"':'"'},hc=function(a){this.options=a};hc.prototype={constructor:hc,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=nb[c],f=nb[d];nb[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=y(c)?"s "+c+"-"+this.index+" ["+this.text.substring(c,d)+"]":" "+d;throw na("lexerr",a,c,this.text);},readNumber:function(){for(var a="",c=this.index;this.index<
this.text.length;){var d=z(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+=Zf[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 ib=function(a,c,d){this.lexer=a;this.$filter=c;this.options=d};ib.ZERO=w(function(){return 0},{sharedGetter:!0,constant:!0});ib.prototype={constructor:ib,parse:function(a){this.text=a;this.tokens=this.lexer.lex(a);a=this.statements();0!==this.tokens.length&&this.throwError("is an unexpected token",this.tokens[0]);
a.literal=!!a.literal;a.constant=!!a.constant;return a},primary:function(){var a;this.expect("(")?(a=this.filterChain(),this.consume(")")):this.expect("[")?a=this.arrayDeclaration():this.expect("{")?a=this.object():this.peek().identifier&&this.peek().text in mb?a=mb[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,d;c=this.expect("(","[",".");)"("===c.text?(a=this.functionCall(a,
d),d=null):"["===c.text?(d=a,a=this.objectIndex(a)):"."===c.text?(d=a,a=this.fieldAccess(a)):this.throwError("IMPOSSIBLE");return a},throwError:function(a,c){throw na("syntax",c.text,a,c.index+1,this.text,this.text.substring(c.index));},peekToken:function(){if(0===this.tokens.length)throw na("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},consume:function(a){if(0===this.tokens.length)throw na("ueoe",this.text);var c=this.expect(a);c||this.throwError("is unexpected, expecting ["+a+"]",this.peek());return c},unaryFn:function(a,c){var d=nb[a];return w(function(a,f){return d(a,f,c)},{constant:c.constant,inputs:[c]})},binaryFn:function(a,c,d,e){var f=nb[c];return w(function(c,e){return f(c,e,a,d)},{constant:a.constant&&
d.constant,inputs:!e&&[a,d]})},identifier:function(){for(var a=this.consume().text;this.peek(".")&&this.peekAhead(1).identifier&&!this.peekAhead(2,"(");)a+=this.consume().text+this.consume().text;return zf(a,this.options,this.text)},constant:function(){var a=this.consume().value;return w(function(){return a},{constant:!0,literal:!0})},statements:function(){for(var a=[];;)if(0<this.tokens.length&&!this.peek("}",")",";","]")&&a.push(this.filterChain()),!this.expect(";"))return 1===a.length?a[0]:function(c,
d){for(var e,f=0,g=a.length;f<g;f++)e=a[f](c,d);return e}},filterChain:function(){for(var a=this.expression();this.expect("|");)a=this.filter(a);return a},filter:function(a){var c=this.$filter(this.consume().text),d,e;if(this.peek(":"))for(d=[],e=[];this.expect(":");)d.push(this.expression());var f=[a].concat(d||[]);return w(function(f,h){var l=a(f,h);if(e){e[0]=l;for(l=d.length;l--;)e[l+1]=d[l](f,h);return c.apply(t,e)}return c(l)},{constant:!c.$stateful&&f.every(fc),inputs:!c.$stateful&&f})},expression:function(){return this.assignment()},
assignment:function(){var a=this.ternary(),c,d;return(d=this.expect("="))?(a.assign||this.throwError("implies assignment but ["+this.text.substring(0,d.index)+"] can not be assigned to",d),c=this.ternary(),w(function(d,f){return a.assign(d,c(d,f),f)},{inputs:[a,c]})):a},ternary:function(){var a=this.logicalOR(),c;if(this.expect("?")&&(c=this.assignment(),this.consume(":"))){var d=this.assignment();return w(function(e,f){return a(e,f)?c(e,f):d(e,f)},{constant:a.constant&&c.constant&&d.constant})}return a},
logicalOR:function(){for(var a=this.logicalAND(),c;c=this.expect("||");)a=this.binaryFn(a,c.text,this.logicalAND(),!0);return a},logicalAND:function(){for(var a=this.equality(),c;c=this.expect("&&");)a=this.binaryFn(a,c.text,this.equality(),!0);return a},equality:function(){for(var a=this.relational(),c;c=this.expect("==","!=","===","!==");)a=this.binaryFn(a,c.text,this.relational());return a},relational:function(){for(var a=this.additive(),c;c=this.expect("<",">","<=",">=");)a=this.binaryFn(a,c.text,
this.additive());return a},additive:function(){for(var a=this.multiplicative(),c;c=this.expect("+","-");)a=this.binaryFn(a,c.text,this.multiplicative());return a},multiplicative:function(){for(var a=this.unary(),c;c=this.expect("*","/","%");)a=this.binaryFn(a,c.text,this.unary());return a},unary:function(){var a;return this.expect("+")?this.primary():(a=this.expect("-"))?this.binaryFn(ib.ZERO,a.text,this.unary()):(a=this.expect("!"))?this.unaryFn(a.text,this.unary()):this.primary()},fieldAccess:function(a){var c=
this.identifier();return w(function(d,e,f){d=f||a(d,e);return null==d?t:c(d)},{assign:function(d,e,f){var g=a(d,f);g||a.assign(d,g={},f);return c.assign(g,e)}})},objectIndex:function(a){var c=this.text,d=this.expression();this.consume("]");return w(function(e,f){var g=a(e,f),h=d(e,f);ua(h,c);return g?oa(g[h],c):t},{assign:function(e,f,g){var h=ua(d(e,g),c),l=oa(a(e,g),c);l||a.assign(e,l={},g);return l[h]=f}})},functionCall:function(a,c){var d=[];if(")"!==this.peekToken().text){do d.push(this.expression());
while(this.expect(","))}this.consume(")");var e=this.text,f=d.length?[]:null;return function(g,h){var l=c?c(g,h):y(c)?t:g,k=a(g,h,l)||E;if(f)for(var n=d.length;n--;)f[n]=oa(d[n](g,h),e);oa(l,e);if(k){if(k.constructor===k)throw na("isecfn",e);if(k===Wf||k===Xf||k===Yf)throw na("isecff",e);}l=k.apply?k.apply(l,f):k(f[0],f[1],f[2],f[3],f[4]);f&&(f.length=0);return oa(l,e)}},arrayDeclaration:function(){var a=[];if("]"!==this.peekToken().text){do{if(this.peek("]"))break;a.push(this.expression())}while(this.expect(","))
}this.consume("]");return w(function(c,d){for(var e=[],f=0,g=a.length;f<g;f++)e.push(a[f](c,d));return e},{literal:!0,constant:a.every(fc),inputs:a})},object:function(){var a=[],c=[];if("}"!==this.peekToken().text){do{if(this.peek("}"))break;var d=this.consume();d.constant?a.push(d.value):d.identifier?a.push(d.text):this.throwError("invalid key",d);this.consume(":");c.push(this.expression())}while(this.expect(","))}this.consume("}");return w(function(d,f){for(var g={},h=0,l=c.length;h<l;h++)g[a[h]]=
c[h](d,f);return g},{literal:!0,constant:c.every(fc),inputs:c})}};var Bf=ia(),Af=ia(),Cf=Object.prototype.valueOf,Ba=R("$sce"),pa={HTML:"html",CSS:"css",URL:"url",RESOURCE_URL:"resourceUrl",JS:"js"},la=R("$compile"),$=W.createElement("a"),id=Aa(Q.location.href);Fc.$inject=["$provide"];jd.$inject=["$locale"];ld.$inject=["$locale"];var od=".",Mf={yyyy:U("FullYear",4),yy:U("FullYear",2,0,!0),y:U("FullYear",1),MMMM:Jb("Month"),MMM:Jb("Month",!0),MM:U("Month",2,1),M:U("Month",1,1),dd:U("Date",2),d:U("Date",
1),HH:U("Hours",2),H:U("Hours",1),hh:U("Hours",2,-12),h:U("Hours",1,-12),mm:U("Minutes",2),m:U("Minutes",1),ss:U("Seconds",2),s:U("Seconds",1),sss:U("Milliseconds",3),EEEE:Jb("Day"),EEE:Jb("Day",!0),a:function(a,c){return 12>a.getHours()?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){a=-1*a.getTimezoneOffset();return a=(0<=a?"+":"")+(Ib(Math[0<a?"floor":"ceil"](a/60),2)+Ib(Math.abs(a%60),2))},ww:qd(2),w:qd(1),G:ic,GG:ic,GGG:ic,GGGG:function(a,c){return 0>=a.getFullYear()?c.ERANAMES[0]:c.ERANAMES[1]}},Lf=/((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
Kf=/^\-?\d+$/;kd.$inject=["$locale"];var Hf=ea(z),If=ea(ub);md.$inject=["$parse"];var Td=ea({restrict:"E",compile:function(a,c){if(!c.href&&!c.xlinkHref&&!c.name)return function(a,c){if("a"===c[0].nodeName.toLowerCase()){var f="[object SVGAnimatedString]"===Ca.call(c.prop("href"))?"xlink:href":"href";c.on("click",function(a){c.attr(f)||a.preventDefault()})}}}}),vb={};r(Eb,function(a,c){if("multiple"!=a){var d=xa("ng-"+c);vb[d]=function(){return{restrict:"A",priority:100,link:function(a,f,g){a.$watch(g[d],
function(a){g.$set(c,!!a)})}}}}});r(Pc,function(a,c){vb[c]=function(){return{priority:100,link:function(a,e,f){if("ngPattern"===c&&"/"==f.ngPattern.charAt(0)&&(e=f.ngPattern.match(Of))){f.$set("ngPattern",new RegExp(e[1],e[2]));return}a.$watch(f[c],function(a){f.$set(c,a)})}}}});r(["src","srcset","href"],function(a){var c=xa("ng-"+a);vb[c]=function(){return{priority:99,link:function(d,e,f){var g=a,h=a;"href"===a&&"[object SVGAnimatedString]"===Ca.call(e.prop("href"))&&(h="xlinkHref",f.$attr[h]="xlink:href",
g=null);f.$observe(c,function(c){c?(f.$set(h,c),Qa&&g&&e.prop(g,f[h])):"href"===a&&f.$set(h,null)})}}}});var Kb={$addControl:E,$$renameControl:function(a,c){a.$name=c},$removeControl:E,$setValidity:E,$setDirty:E,$setPristine:E,$setSubmitted:E};rd.$inject=["$element","$attrs","$scope","$animate","$interpolate"];var yd=function(a){return["$timeout",function(c){return{name:"form",restrict:a?"EAC":"E",controller:rd,compile:function(d,e){d.addClass(Ra).addClass(lb);var f=e.name?"name":a&&e.ngForm?"ngForm":
!1;return{pre:function(a,d,e,k){if(!("action"in e)){var n=function(c){a.$apply(function(){k.$commitViewValue();k.$setSubmitted()});c.preventDefault()};d[0].addEventListener("submit",n,!1);d.on("$destroy",function(){c(function(){d[0].removeEventListener("submit",n,!1)},0,!1)})}var p=k.$$parentForm;f&&(hb(a,null,k.$name,k,k.$name),e.$observe(f,function(c){k.$name!==c&&(hb(a,null,k.$name,t,k.$name),p.$$renameControl(k,c),hb(a,null,k.$name,k,k.$name))}));d.on("$destroy",function(){p.$removeControl(k);
f&&hb(a,null,e[f],t,k.$name);w(k,Kb)})}}}}}]},Ud=yd(),ge=yd(!0),Nf=/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/,$f=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,ag=/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,bg=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,zd=/^(\d{4})-(\d{2})-(\d{2})$/,Ad=/^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,lc=/^(\d{4})-W(\d\d)$/,Bd=/^(\d{4})-(\d\d)$/,
Cd=/^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,Dd={text:function(a,c,d,e,f,g){jb(a,c,d,e,f,g);jc(e)},date:kb("date",zd,Mb(zd,["yyyy","MM","dd"]),"yyyy-MM-dd"),"datetime-local":kb("datetimelocal",Ad,Mb(Ad,"yyyy MM dd HH mm ss sss".split(" ")),"yyyy-MM-ddTHH:mm:ss.sss"),time:kb("time",Cd,Mb(Cd,["HH","mm","ss","sss"]),"HH:mm:ss.sss"),week:kb("week",lc,function(a,c){if(ga(a))return a;if(C(a)){lc.lastIndex=0;var d=lc.exec(a);if(d){var e=+d[1],f=+d[2],g=d=0,h=0,l=0,k=pd(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:kb("month",Bd,Mb(Bd,["yyyy","MM"]),"yyyy-MM"),number:function(a,c,d,e,f,g){td(a,c,d,e);jb(a,c,d,e,f,g);e.$$parserName="number";e.$parsers.push(function(a){return e.$isEmpty(a)?null:bg.test(a)?parseFloat(a):t});e.$formatters.push(function(a){if(!e.$isEmpty(a)){if(!Y(a))throw Nb("numfmt",a);a=a.toString()}return a});if(y(d.min)||d.ngMin){var h;e.$validators.min=function(a){return e.$isEmpty(a)||
x(h)||a>=h};d.$observe("min",function(a){y(a)&&!Y(a)&&(a=parseFloat(a,10));h=Y(a)&&!isNaN(a)?a:t;e.$validate()})}if(y(d.max)||d.ngMax){var l;e.$validators.max=function(a){return e.$isEmpty(a)||x(l)||a<=l};d.$observe("max",function(a){y(a)&&!Y(a)&&(a=parseFloat(a,10));l=Y(a)&&!isNaN(a)?a:t;e.$validate()})}},url:function(a,c,d,e,f,g){jb(a,c,d,e,f,g);jc(e);e.$$parserName="url";e.$validators.url=function(a,c){var d=a||c;return e.$isEmpty(d)||$f.test(d)}},email:function(a,c,d,e,f,g){jb(a,c,d,e,f,g);jc(e);
e.$$parserName="email";e.$validators.email=function(a,c){var d=a||c;return e.$isEmpty(d)||ag.test(d)}},radio:function(a,c,d,e){x(d.name)&&c.attr("name",++ob);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=ud(l,a,"ngTrueValue",d.ngTrueValue,!0),n=ud(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 ha(a,k)});e.$parsers.push(function(a){return a?k:n})},hidden:E,button:E,submit:E,reset:E,file:E},zc=["$browser","$sniffer","$filter","$parse",function(a,c,d,e){return{restrict:"E",require:["?ngModel"],link:{pre:function(f,g,h,l){l[0]&&(Dd[z(h.type)]||Dd.text)(f,g,h,l[0],c,a,d,e)}}}}],cg=/^(true|false|\d+)$/,ye=function(){return{restrict:"A",priority:100,compile:function(a,
c){return cg.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)})}}}},Zd=["$compile",function(a){return{restrict:"AC",compile:function(c){a.$$addBindingClass(c);return function(c,e,f){a.$$addBindingInfo(e,f.ngBind);e=e[0];c.$watch(f.ngBind,function(a){e.textContent=a===t?"":a})}}}}],ae=["$interpolate","$compile",function(a,c){return{compile:function(d){c.$$addBindingClass(d);return function(d,f,g){d=a(f.attr(g.$attr.ngBindTemplate));
c.$$addBindingInfo(f,d.expressions);f=f[0];g.$observe("ngBindTemplate",function(a){f.textContent=a===t?"":a})}}}}],$d=["$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))||"")})}}}}],xe=ea({restrict:"A",require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),
be=kc("",!0),de=kc("Odd",0),ce=kc("Even",1),ee=Ia({compile:function(a,c){c.$set("ngCloak",t);a.removeClass("ng-cloak")}}),fe=[function(){return{restrict:"A",scope:!0,controller:"@",priority:500}}],Ec={},dg={blur:!0,focus:!0};r("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),function(a){var c=xa("ng-"+a);Ec[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})};dg[a]&&e.$$phase?c.$evalAsync(f):c.$apply(f)})}}}}]});var ie=["$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=
tb(h.clone),a.leave(k).then(function(){k=null}),h=null))})}}}],je=["$templateRequest","$anchorScroll","$animate","$sce",function(a,c,d,e){return{restrict:"ECA",priority:400,terminal:!0,transclude:"element",controller:ca.noop,compile:function(f,g){var h=g.ngInclude||g.src,l=g.onload||"",k=g.autoscroll;return function(f,g,q,r,s){var t=0,v,m,F,w=function(){m&&(m.remove(),m=null);v&&(v.$destroy(),v=null);F&&(d.leave(F).then(function(){m=null}),m=F,F=null)};f.$watch(e.parseAsResourceUrl(h),function(e){var h=
function(){!y(k)||k&&!f.$eval(k)||c()},m=++t;e?(a(e,!0).then(function(a){if(m===t){var c=f.$new();r.template=a;a=s(c,function(a){w();d.enter(a,null,g).then(h)});v=c;F=a;v.$emit("$includeContentLoaded",e);f.$eval(l)}},function(){m===t&&(w(),f.$emit("$includeContentError",e))}),f.$emit("$includeContentRequested",e)):(w(),r.template=null)})}}}}],Ae=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(c,d,e,f){/SVG/.test(d[0].toString())?(d.empty(),a(Hc(f.template,
W).childNodes)(c,function(a){d.append(a)},{futureParentElement:d})):(d.html(f.template),a(d.contents())(c))}}}],ke=Ia({priority:450,compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),we=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?N(f):f;e.$parsers.push(function(a){if(!x(a)){var c=[];a&&r(a.split(h),function(a){a&&c.push(g?N(a):a)});return c}});e.$formatters.push(function(a){return H(a)?
a.join(f):t});e.$isEmpty=function(a){return!a||!a.length}}}},lb="ng-valid",vd="ng-invalid",Ra="ng-pristine",Lb="ng-dirty",xd="ng-pending",Nb=new R("ngModel"),eg=["$scope","$exceptionHandler","$attrs","$element","$parse","$animate","$timeout","$rootScope","$q","$interpolate",function(a,c,d,e,f,g,h,l,k,n){this.$modelValue=this.$viewValue=Number.NaN;this.$$rawModelValue=t;this.$validators={};this.$asyncValidators={};this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$untouched=!0;
this.$touched=!1;this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$error={};this.$$success={};this.$pending=t;this.$name=n(d.name||"",!1)(a);var p=f(d.ngModel),q=p.assign,u=p,s=q,M=null,v,m=this;this.$$setOptions=function(a){if((m.$options=a)&&a.getterSetter){var c=f(d.ngModel+"()"),g=f(d.ngModel+"($$$p)");u=function(a){var d=p(a);G(d)&&(d=c(a));return d};s=function(a,c){G(p(a))?g(a,{$$$p:m.$modelValue}):q(a,m.$modelValue)}}else if(!p.assign)throw Nb("nonassign",d.ngModel,wa(e));
};this.$render=E;this.$isEmpty=function(a){return x(a)||""===a||null===a||a!==a};var F=e.inheritedData("$formController")||Kb,w=0;sd({ctrl:this,$element:e,set:function(a,c){a[c]=!0},unset:function(a,c){delete a[c]},parentForm:F,$animate:g});this.$setPristine=function(){m.$dirty=!1;m.$pristine=!0;g.removeClass(e,Lb);g.addClass(e,Ra)};this.$setDirty=function(){m.$dirty=!0;m.$pristine=!1;g.removeClass(e,Ra);g.addClass(e,Lb);F.$setDirty()};this.$setUntouched=function(){m.$touched=!1;m.$untouched=!0;g.setClass(e,
"ng-untouched","ng-touched")};this.$setTouched=function(){m.$touched=!0;m.$untouched=!1;g.setClass(e,"ng-touched","ng-untouched")};this.$rollbackViewValue=function(){h.cancel(M);m.$viewValue=m.$$lastCommittedViewValue;m.$render()};this.$validate=function(){if(!Y(m.$modelValue)||!isNaN(m.$modelValue)){var a=m.$$rawModelValue,c=m.$valid,d=m.$modelValue,e=m.$options&&m.$options.allowInvalid;m.$$runValidators(a,m.$$lastCommittedViewValue,function(f){e||c===f||(m.$modelValue=f?a:t,m.$modelValue!==d&&m.$$writeModelToScope())})}};
this.$$runValidators=function(a,c,d){function e(){var d=!0;r(m.$validators,function(e,f){var h=e(a,c);d=d&&h;g(f,h)});return d?!0:(r(m.$asyncValidators,function(a,c){g(c,null)}),!1)}function f(){var d=[],e=!0;r(m.$asyncValidators,function(f,h){var k=f(a,c);if(!k||!G(k.then))throw Nb("$asyncValidators",k);g(h,t);d.push(k.then(function(){g(h,!0)},function(a){e=!1;g(h,!1)}))});d.length?k.all(d).then(function(){h(e)},E):h(!0)}function g(a,c){l===w&&m.$setValidity(a,c)}function h(a){l===w&&d(a)}w++;var l=
w;(function(){var a=m.$$parserName||"parse";if(v===t)g(a,null);else return v||(r(m.$validators,function(a,c){g(c,null)}),r(m.$asyncValidators,function(a,c){g(c,null)})),g(a,v),v;return!0})()?e()?f():h(!1):h(!1)};this.$commitViewValue=function(){var a=m.$viewValue;h.cancel(M);if(m.$$lastCommittedViewValue!==a||""===a&&m.$$hasNativeValidators)m.$$lastCommittedViewValue=a,m.$pristine&&this.$setDirty(),this.$$parseAndValidate()};this.$$parseAndValidate=function(){var c=m.$$lastCommittedViewValue;if(v=
x(c)?t:!0)for(var d=0;d<m.$parsers.length;d++)if(c=m.$parsers[d](c),x(c)){v=!1;break}Y(m.$modelValue)&&isNaN(m.$modelValue)&&(m.$modelValue=u(a));var e=m.$modelValue,f=m.$options&&m.$options.allowInvalid;m.$$rawModelValue=c;f&&(m.$modelValue=c,m.$modelValue!==e&&m.$$writeModelToScope());m.$$runValidators(c,m.$$lastCommittedViewValue,function(a){f||(m.$modelValue=a?c:t,m.$modelValue!==e&&m.$$writeModelToScope())})};this.$$writeModelToScope=function(){s(a,m.$modelValue);r(m.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}})};
this.$setViewValue=function(a,c){m.$viewValue=a;m.$options&&!m.$options.updateOnDefault||m.$$debounceViewValueCommit(c)};this.$$debounceViewValueCommit=function(c){var d=0,e=m.$options;e&&y(e.debounce)&&(e=e.debounce,Y(e)?d=e:Y(e[c])?d=e[c]:Y(e["default"])&&(d=e["default"]));h.cancel(M);d?M=h(function(){m.$commitViewValue()},d):l.$$phase?m.$commitViewValue():a.$apply(function(){m.$commitViewValue()})};a.$watch(function(){var c=u(a);if(c!==m.$modelValue){m.$modelValue=m.$$rawModelValue=c;v=t;for(var d=
m.$formatters,e=d.length,f=c;e--;)f=d[e](f);m.$viewValue!==f&&(m.$viewValue=m.$$lastCommittedViewValue=f,m.$render(),m.$$runValidators(c,f,E))}return c})}],ve=["$rootScope",function(a){return{restrict:"A",require:["ngModel","^?form","^?ngModelOptions"],controller:eg,priority:1,compile:function(c){c.addClass(Ra).addClass("ng-untouched").addClass(lb);return{pre:function(a,c,f,g){var h=g[0],l=g[1]||Kb;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))})}}}}}],fg=/(\s+|^)default(\s+|$)/,ze=function(){return{restrict:"A",controller:["$scope","$attrs",function(a,c){var d=this;this.$options=a.$eval(c.ngModelOptions);this.$options.updateOn!==t?(this.$options.updateOnDefault=
!1,this.$options.updateOn=N(this.$options.updateOn.replace(fg,function(){d.$options.updateOnDefault=!0;return" "}))):this.$options.updateOnDefault=!0}]}},le=Ia({terminal:!0,priority:1E3}),me=["$locale","$interpolate",function(a,c){var d=/{}/g,e=/^when(Minus)?(.+)$/;return{restrict:"EA",link:function(f,g,h){function l(a){g.text(a||"")}var k=h.count,n=h.$attr.when&&g.attr(h.$attr.when),p=h.offset||0,q=f.$eval(n)||{},u={},n=c.startSymbol(),s=c.endSymbol(),t=n+k+"-"+p+s,v=ca.noop,m;r(h,function(a,c){var d=
e.exec(c);d&&(d=(d[1]?"-":"")+z(d[2]),q[d]=g.attr(h.$attr[c]))});r(q,function(a,e){u[e]=c(a.replace(d,t))});f.$watch(k,function(c){c=parseFloat(c);var d=isNaN(c);d||c in q||(c=a.pluralCat(c-p));c===m||d&&isNaN(m)||(v(),v=f.$watch(u[c],l),m=c)})}}}],ne=["$parse","$animate",function(a,c){var d=R("ngRepeat"),e=function(a,c,d,e,k,n,p){a[d]=e;k&&(a[k]=n);a.$index=c;a.$first=0===c;a.$last=c===p-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 n=k[1],p=k[2],q=k[3],u=k[4],k=n.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);if(!k)throw d("iidexp",n);var s=k[3]||k[1],y=k[2];if(q&&(!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(q)||/^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(q)))throw d("badident",
q);var v,m,w,x,E={$id:Ma};u?v=a(u):(w=function(a,c){return Ma(c)},x=function(a){return a});return function(a,f,g,k,n){v&&(m=function(c,d,e){y&&(E[y]=c);E[s]=d;E.$index=e;return v(a,E)});var u=ia();a.$watchCollection(p,function(g){var k,p,v=f[0],D,E=ia(),G,H,L,S,J,C,z;q&&(a[q]=g);if(Sa(g))J=g,p=m||w;else{p=m||x;J=[];for(z in g)g.hasOwnProperty(z)&&"$"!=z.charAt(0)&&J.push(z);J.sort()}G=J.length;z=Array(G);for(k=0;k<G;k++)if(H=g===J?k:J[k],L=g[H],S=p(H,L,k),u[S])C=u[S],delete u[S],E[S]=C,z[k]=C;else{if(E[S])throw r(z,
function(a){a&&a.scope&&(u[a.id]=a)}),d("dupes",h,S,L);z[k]={id:S,scope:t,clone:t};E[S]=!0}for(D in u){C=u[D];S=tb(C.clone);c.leave(S);if(S[0].parentNode)for(k=0,p=S.length;k<p;k++)S[k].$$NG_REMOVED=!0;C.scope.$destroy()}for(k=0;k<G;k++)if(H=g===J?k:J[k],L=g[H],C=z[k],C.scope){D=v;do D=D.nextSibling;while(D&&D.$$NG_REMOVED);C.clone[0]!=D&&c.move(tb(C.clone),null,A(v));v=C.clone[C.clone.length-1];e(C.scope,k,s,L,y,H,G)}else n(function(a,d){C.scope=d;var f=l.cloneNode(!1);a[a.length++]=f;c.enter(a,
null,A(v));v=f;C.clone=a;E[C.id]=C;e(C.scope,k,s,L,y,H,G)});u=E})}}}}],oe=["$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"})})}}}],he=["$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"})})}}}],pe=Ia(function(a,c,d){a.$watchCollection(d.ngStyle,
function(a,d){d&&a!==d&&r(d,function(a,d){c.css(d,"")});a&&c.css(a)})}),qe=["$animate",function(a){return{restrict:"EA",require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(c,d,e,f){var g=[],h=[],l=[],k=[],n=function(a,c){return function(){a.splice(c,1)}};c.$watch(e.ngSwitch||e.on,function(c){var d,e;d=0;for(e=l.length;d<e;++d)a.cancel(l[d]);d=l.length=0;for(e=k.length;d<e;++d){var s=tb(h[d].clone);k[d].$destroy();(l[d]=a.leave(s)).then(n(l,d))}h.length=0;k.length=0;(g=
f.cases["!"+c]||f.cases["?"])&&r(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)})})})}}}],re=Ia({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})}}),se=Ia({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})}}),ue=Ia({restrict:"EAC",link:function(a,c,d,e,f){if(!f)throw R("ngTransclude")("orphan",wa(c));f(function(a){c.empty();c.append(a)})}}),Vd=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(c,d){"text/ng-template"==d.type&&a.put(d.id,c[0].text)}}}],gg=R("ngOptions"),te=ea({restrict:"A",terminal:!0}),Wd=["$compile","$parse",function(a,c){var d=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\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]+?))?$/,
e={$setViewValue:E};return{restrict:"E",require:["select","?ngModel"],controller:["$element","$scope","$attrs",function(a,c,d){var l=this,k={},n=e,p;l.databound=d.ngModel;l.init=function(a,c,d){n=a;p=d};l.addOption=function(c,d){La(c,'"option value"');k[c]=!0;n.$viewValue==c&&(a.val(c),p.parent()&&p.remove());d&&d[0].hasAttribute("selected")&&(d[0].selected=!0)};l.removeOption=function(a){this.hasOption(a)&&(delete k[a],n.$viewValue===a&&this.renderUnknownOption(a))};l.renderUnknownOption=function(c){c=
"? "+Ma(c)+" ?";p.val(c);a.prepend(p);a.val(c);p.prop("selected",!0)};l.hasOption=function(a){return k.hasOwnProperty(a)};c.$on("$destroy",function(){l.renderUnknownOption=E})}],link:function(e,g,h,l){function k(a,c,d,e){d.$render=function(){var a=d.$viewValue;e.hasOption(a)?(C.parent()&&C.remove(),c.val(a),""===a&&v.prop("selected",!0)):x(a)&&v?c.val(""):e.renderUnknownOption(a)};c.on("change",function(){a.$apply(function(){C.parent()&&C.remove();d.$setViewValue(c.val())})})}function n(a,c,d){var e;
d.$render=function(){var a=new eb(d.$viewValue);r(c.find("option"),function(c){c.selected=y(a.get(c.value))})};a.$watch(function(){ha(e,d.$viewValue)||(e=sa(d.$viewValue),d.$render())});c.on("change",function(){a.$apply(function(){var a=[];r(c.find("option"),function(c){c.selected&&a.push(c.value)});d.$setViewValue(a)})})}function p(e,f,g){function h(a,c,d){T[x]=d;G&&(T[G]=c);return a(e,T)}function k(a){var c;if(u)if(I&&H(a)){c=new eb([]);for(var d=0;d<a.length;d++)c.put(h(I,null,a[d]),!0)}else c=
new eb(a);else I&&(a=h(I,null,a));return function(d,e){var f;f=I?I:B?B:z;return u?y(c.remove(h(f,d,e))):a===h(f,d,e)}}function l(){m||(e.$$postDigest(p),m=!0)}function n(a,c,d){a[c]=a[c]||0;a[c]+=d?1:-1}function p(){m=!1;var a={"":[]},c=[""],d,l,s,t,v;s=g.$viewValue;t=L(e)||[];var B=G?Object.keys(t).sort():t,x,A,H,z,O={};v=k(s);var N=!1,U,W;Q={};for(z=0;H=B.length,z<H;z++){x=z;if(G&&(x=B[z],"$"===x.charAt(0)))continue;A=t[x];d=h(J,x,A)||"";(l=a[d])||(l=a[d]=[],c.push(d));d=v(x,A);N=N||d;A=h(C,x,A);
A=y(A)?A:"";W=I?I(e,T):G?B[z]:z;I&&(Q[W]=x);l.push({id:W,label:A,selected:d})}u||(w||null===s?a[""].unshift({id:"",label:"",selected:!N}):N||a[""].unshift({id:"?",label:"",selected:!0}));x=0;for(B=c.length;x<B;x++){d=c[x];l=a[d];R.length<=x?(s={element:E.clone().attr("label",d),label:l.label},t=[s],R.push(t),f.append(s.element)):(t=R[x],s=t[0],s.label!=d&&s.element.attr("label",s.label=d));N=null;z=0;for(H=l.length;z<H;z++)d=l[z],(v=t[z+1])?(N=v.element,v.label!==d.label&&(n(O,v.label,!1),n(O,d.label,
!0),N.text(v.label=d.label),N.prop("label",v.label)),v.id!==d.id&&N.val(v.id=d.id),N[0].selected!==d.selected&&(N.prop("selected",v.selected=d.selected),Qa&&N.prop("selected",v.selected))):(""===d.id&&w?U=w:(U=F.clone()).val(d.id).prop("selected",d.selected).attr("selected",d.selected).prop("label",d.label).text(d.label),t.push(v={element:U,label:d.label,id:d.id,selected:d.selected}),n(O,d.label,!0),N?N.after(U):s.element.append(U),N=U);for(z++;t.length>z;)d=t.pop(),n(O,d.label,!1),d.element.remove()}for(;R.length>
x;){l=R.pop();for(z=1;z<l.length;++z)n(O,l[z].label,!1);l[0].element.remove()}r(O,function(a,c){0<a?q.addOption(c):0>a&&q.removeOption(c)})}var v;if(!(v=s.match(d)))throw gg("iexp",s,wa(f));var C=c(v[2]||v[1]),x=v[4]||v[6],A=/ as /.test(v[0])&&v[1],B=A?c(A):null,G=v[5],J=c(v[3]||""),z=c(v[2]?v[1]:x),L=c(v[7]),I=v[8]?c(v[8]):null,Q={},R=[[{element:f,label:""}]],T={};w&&(a(w)(e),w.removeClass("ng-scope"),w.remove());f.empty();f.on("change",function(){e.$apply(function(){var a=L(e)||[],c;if(u)c=[],r(f.val(),
function(d){d=I?Q[d]:d;c.push("?"===d?t:""===d?null:h(B?B:z,d,a[d]))});else{var d=I?Q[f.val()]:f.val();c="?"===d?t:""===d?null:h(B?B:z,d,a[d])}g.$setViewValue(c);p()})});g.$render=p;e.$watchCollection(L,l);e.$watchCollection(function(){var a=L(e),c;if(a&&H(a)){c=Array(a.length);for(var d=0,f=a.length;d<f;d++)c[d]=h(C,d,a[d])}else if(a)for(d in c={},a)a.hasOwnProperty(d)&&(c[d]=h(C,d,a[d]));return c},l);u&&e.$watchCollection(function(){return g.$modelValue},l)}if(l[1]){var q=l[0];l=l[1];var u=h.multiple,
s=h.ngOptions,w=!1,v,m=!1,F=A(W.createElement("option")),E=A(W.createElement("optgroup")),C=F.clone();h=0;for(var B=g.children(),G=B.length;h<G;h++)if(""===B[h].value){v=w=B.eq(h);break}q.init(l,w,C);u&&(l.$isEmpty=function(a){return!a||0===a.length});s?p(e,g,l):u?n(e,g,l):k(e,g,l,q)}}}}],Yd=["$interpolate",function(a){var c={addOption:E,removeOption:E};return{restrict:"E",priority:100,compile:function(d,e){if(x(e.value)){var f=a(d.text(),!0);f||e.$set("value",d.text())}return function(a,d,e){var k=
d.parent(),n=k.data("$selectController")||k.parent().data("$selectController");n&&n.databound||(n=c);f?a.$watch(f,function(a,c){e.$set("value",a);c!==a&&n.removeOption(c);n.addOption(a,d)}):n.addOption(e.value,d);d.on("$destroy",function(){n.removeOption(e.value)})}}}}],Xd=ea({restrict:"E",terminal:!1}),Bc=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()}))}}},
Ac=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){C(a)&&0<a.length&&(a=new RegExp("^"+a+"$"));if(a&&!a.test)throw R("ngPattern")("noregexp",g,a,wa(c));f=a||t;e.$validate()});e.$validators.pattern=function(a){return e.$isEmpty(a)||x(f)||f.test(a)}}}}},Dc=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){if(e){var f=-1;d.$observe("maxlength",function(a){a=aa(a);f=isNaN(a)?-1:a;e.$validate()});
e.$validators.maxlength=function(a,c){return 0>f||e.$isEmpty(c)||c.length<=f}}}}},Cc=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){if(e){var f=0;d.$observe("minlength",function(a){f=aa(a)||0;e.$validate()});e.$validators.minlength=function(a,c){return e.$isEmpty(c)||c.length>=f}}}}};Q.angular.bootstrap?console.log("WARNING: Tried to load angular more than once."):(Nd(),Pd(ca),A(W).ready(function(){Jd(W,uc)}))})(window,document);!window.angular.$$csp()&&window.angular.element(document).find("head").prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}</style>');
//# sourceMappingURL=angular.min.js.map

11
lib/bootstrap.min.js vendored

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More