Version 2.0.0
parent
b0f40cc909
commit
0d384c7019
|
@ -1,49 +1,11 @@
|
|||
secrets.txt
|
||||
*.pstore
|
||||
*.gem
|
||||
*.rbc
|
||||
*.swp
|
||||
.bundle/*
|
||||
.DS_Store
|
||||
.idea/*
|
||||
.loadpath
|
||||
.project
|
||||
.ruby-gemset
|
||||
.ruby-version
|
||||
/.bundle
|
||||
/.bundle/
|
||||
/.yardoc/
|
||||
/_yardoc/
|
||||
/coverage/
|
||||
/coverage/
|
||||
*.sqlite3
|
||||
*.sqlite3-journal
|
||||
/doc/
|
||||
/InstalledFiles
|
||||
/lib/.build
|
||||
/lib/bundler/man/
|
||||
/log/*.log
|
||||
*.log
|
||||
/pkg/
|
||||
/rdoc/
|
||||
/spec/reports/
|
||||
/test/tmp/
|
||||
/test/version_tmp/
|
||||
/tmp
|
||||
/tmp/
|
||||
config.yml
|
||||
coverage/**
|
||||
credentials.yml
|
||||
db/settings.yml
|
||||
doc/*
|
||||
doc/**/*
|
||||
log/*
|
||||
notes.rb
|
||||
/lib/.build
|
||||
write_db_settings.rb
|
||||
snippet.coffee
|
||||
snippets.rb
|
||||
tmp/**/*
|
||||
*.rb~
|
||||
serial_port.txt
|
||||
*credentials*
|
||||
# App artifacts
|
||||
/_build
|
||||
/db
|
||||
/deps
|
||||
/*.ez
|
||||
|
||||
# Nerves images
|
||||
/_images
|
||||
|
||||
# Generate on crash by the VM
|
||||
erl_crash.dump
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
To get started, <a href="https://www.clahub.com/agreements/FarmBot/farmbot-raspberry-pi-controller">sign the Contributor License Agreement</a>.
|
19
Gemfile
19
Gemfile
|
@ -1,19 +0,0 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
gem 'eventmachine', '1.2.0.1'
|
||||
gem 'active_record_migrations'
|
||||
gem 'farmbot-resource', '0.6.0' # REST API client for farmbot.
|
||||
gem 'farmbot-serial', '1.1.2' # Arduino communication.
|
||||
gem 'god' # For autorestarting on crashes and stuff.
|
||||
gem 'ice_cube' # For scheduling of pre-programmed schedule
|
||||
gem 'liquid' # For custom programming of schedules.
|
||||
gem 'mutations', '0.7.2' # for e-v-e-r-y-t-h-i-n-g
|
||||
gem 'settingslogic' # For settings.yml file
|
||||
gem 'highline' # For setup.rb.
|
||||
gem 'sqlite3' # For storing schedule data locally.
|
||||
gem 'em-mqtt', '~> 0.0.5' # For browser <--> device communication.
|
||||
|
||||
group :test, :development do
|
||||
gem 'rspec'
|
||||
gem 'simplecov'
|
||||
end
|
139
Gemfile.lock
139
Gemfile.lock
|
@ -1,139 +0,0 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actionpack (4.2.3)
|
||||
actionview (= 4.2.3)
|
||||
activesupport (= 4.2.3)
|
||||
rack (~> 1.6)
|
||||
rack-test (~> 0.6.2)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
actionview (4.2.3)
|
||||
activesupport (= 4.2.3)
|
||||
builder (~> 3.1)
|
||||
erubis (~> 2.7.0)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
active_record_migrations (4.2.3.1)
|
||||
activerecord (= 4.2.3)
|
||||
railties (= 4.2.3)
|
||||
rake
|
||||
activemodel (4.2.3)
|
||||
activesupport (= 4.2.3)
|
||||
builder (~> 3.1)
|
||||
activerecord (4.2.3)
|
||||
activemodel (= 4.2.3)
|
||||
activesupport (= 4.2.3)
|
||||
arel (~> 6.0)
|
||||
activesupport (4.2.3)
|
||||
i18n (~> 0.7)
|
||||
json (~> 1.7, >= 1.7.7)
|
||||
minitest (~> 5.1)
|
||||
thread_safe (~> 0.3, >= 0.3.4)
|
||||
tzinfo (~> 1.1)
|
||||
arel (6.0.3)
|
||||
builder (3.2.2)
|
||||
diff-lcs (1.2.5)
|
||||
docile (1.1.5)
|
||||
domain_name (0.5.20160310)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
em-mqtt (0.0.5)
|
||||
eventmachine
|
||||
mqtt (>= 0.3.0)
|
||||
erubis (2.7.0)
|
||||
eventmachine (1.2.0.1)
|
||||
farmbot-resource (0.6.0)
|
||||
rest-client (~> 1.8)
|
||||
farmbot-serial (1.1.2)
|
||||
eventmachine
|
||||
serialport
|
||||
god (0.13.6)
|
||||
highline (1.7.8)
|
||||
http-cookie (1.0.2)
|
||||
domain_name (~> 0.5)
|
||||
i18n (0.7.0)
|
||||
ice_cube (0.13.0)
|
||||
json (1.8.3)
|
||||
liquid (3.0.6)
|
||||
loofah (2.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
mime-types (2.99.1)
|
||||
mini_portile (0.6.2)
|
||||
minitest (5.8.0)
|
||||
mqtt (0.3.1)
|
||||
mutations (0.7.2)
|
||||
activesupport
|
||||
netrc (0.11.0)
|
||||
nokogiri (1.6.6.2)
|
||||
mini_portile (~> 0.6.0)
|
||||
rack (1.6.4)
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
rails-deprecated_sanitizer (1.0.3)
|
||||
activesupport (>= 4.2.0.alpha)
|
||||
rails-dom-testing (1.0.6)
|
||||
activesupport (>= 4.2.0.beta, < 5.0)
|
||||
nokogiri (~> 1.6.0)
|
||||
rails-deprecated_sanitizer (>= 1.0.1)
|
||||
rails-html-sanitizer (1.0.2)
|
||||
loofah (~> 2.0)
|
||||
railties (4.2.3)
|
||||
actionpack (= 4.2.3)
|
||||
activesupport (= 4.2.3)
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
rake (10.4.2)
|
||||
rest-client (1.8.0)
|
||||
http-cookie (>= 1.0.2, < 2.0)
|
||||
mime-types (>= 1.16, < 3.0)
|
||||
netrc (~> 0.7)
|
||||
rspec (3.3.0)
|
||||
rspec-core (~> 3.3.0)
|
||||
rspec-expectations (~> 3.3.0)
|
||||
rspec-mocks (~> 3.3.0)
|
||||
rspec-core (3.3.2)
|
||||
rspec-support (~> 3.3.0)
|
||||
rspec-expectations (3.3.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.3.0)
|
||||
rspec-mocks (3.3.2)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.3.0)
|
||||
rspec-support (3.3.0)
|
||||
serialport (1.3.1)
|
||||
settingslogic (2.0.9)
|
||||
simplecov (0.10.0)
|
||||
docile (~> 1.1.0)
|
||||
json (~> 1.8)
|
||||
simplecov-html (~> 0.10.0)
|
||||
simplecov-html (0.10.0)
|
||||
sqlite3 (1.3.10)
|
||||
thor (0.19.1)
|
||||
thread_safe (0.3.5)
|
||||
tzinfo (1.2.2)
|
||||
thread_safe (~> 0.1)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.2)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
active_record_migrations
|
||||
em-mqtt (~> 0.0.5)
|
||||
eventmachine (= 1.2.0.1)
|
||||
farmbot-resource (= 0.6.0)
|
||||
farmbot-serial (= 1.1.2)
|
||||
god
|
||||
highline
|
||||
ice_cube
|
||||
liquid
|
||||
mutations (= 0.7.2)
|
||||
rspec
|
||||
settingslogic
|
||||
simplecov
|
||||
sqlite3
|
||||
|
||||
BUNDLED WITH
|
||||
1.12.5
|
|
@ -0,0 +1,9 @@
|
|||
The MIT License
|
||||
|
||||
Copyright (c) 2016 Farmbot Project
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
181
README.md
181
README.md
|
@ -1,137 +1,68 @@
|
|||
# FarmBot Software for the Raspberry Pi
|
||||
# FarmBot Software for the RaspBerry Pi 3
|
||||
The "brains" of Farmbot. Responsible for receiving the commands from users or the farmbot-web-app. It executes them and reports back the results to any subscribed user(s).
|
||||
|
||||
The "brains" of Farmbot. Responsible for receiving the commands from users or the farmbot-web-app. It executes them and report back the results to any subscribed user(s).
|
||||
## Technical Stuff
|
||||
* Written in Elixir with the Nerves Framework
|
||||
* Operation scheduling is not working yet
|
||||
* Device status infor, such as X,Y,Z and calibration data is stored on the Data partition Nerves allows.
|
||||
* Backups to the cloud provided by ["Farmbot Web API"]("https://github.com/farmbot/farmbot-web-api")
|
||||
* Messaging happens vie ["MQTT"]("https://github.com/farmbot/mqtt-gateway")
|
||||
|
||||
Technical Stuff
|
||||
---------------
|
||||
# Running in production
|
||||
You can download the latest release from ["Here"]("https://github.com/FarmBot/farmbot-raspberry-pi-controller/releases")
|
||||
Make sure you download the `.img` file.
|
||||
## Windows users
|
||||
* You can use ["Win32 Disk Imager"]("https://sourceforge.net/projects/win32diskimager/")
|
||||
* Select the `.img` file you downloaded
|
||||
* Select your sdcard's drive letter
|
||||
* Click `write`
|
||||
|
||||
* Written in Ruby.
|
||||
* Operation scheduling data is stored in SQLite 3.
|
||||
* Device status info, such as X, Y, Z and calibration data is stored via [PStore](http://ruby-doc.org/stdlib-1.9.2/libdoc/pstore/rdoc/PStore.html)
|
||||
* Backups to the cloud provided by [Farmbot Web API](https://github.com/farmbot/farmbot-web-api).
|
||||
* Messaging happens via [MQTT](http://mqtt.org/).
|
||||
* Communicates with Arduino hardware using the [farmbot-serial gem](https://github.com/FarmBot/farmbot-serial)
|
||||
## Linux / OSX / UNIX
|
||||
* `dd if=img_file of=/dev/sdX`
|
||||
* where img_file is the path to you `.img` file, and X is your device's drive letter.
|
||||
|
||||
Running in production
|
||||
---------------------
|
||||
## Running
|
||||
* Plug your SD Card into your RPi3
|
||||
* Plug your Arduino into your RPi3
|
||||
* Plug your power into your RPi3
|
||||
* From a WiFi enabled device, search for the SSID `FarmbotConfigurator`
|
||||
* Connect to that and open a web browser to ["http://192.168.24.1/"]("http://192.168.24.1")
|
||||
* Follow the on screen instruction
|
||||
* Profit???
|
||||
|
||||
```
|
||||
bundle install
|
||||
ruby farmbot.rb
|
||||
# Building
|
||||
Ok its actually really easy once your environment is set up. Let me prefix this with this simple phrase. ["YOU NEED LINUX"]("http://www.whylinuxisbetter.net/") I am sorry. It is just the bottom line. A VM works fine, a dual boot environment works. even OpenSuse works. But `bash for windows` does not work. `Cygwin` does not work. and for the love of all things development ready, `osx` does not work. You need Linux to build Linux. So with that rant out of the way, and ready for revision here are the steps to build:
|
||||
* Install Elixir and Erlang. (Shameless plug for ASDF) ([HEY TRY ME?]("https://gist.github.com/ConnorRigby/8a8bffff935d1a43cd74c4b8cf28a845"))
|
||||
* install [`Nerves`]("https://hexdocs.pm/nerves/installation.html") (and all the things it tells you to install there)
|
||||
* clone and cd into this directory.
|
||||
* plug an sdcard into your machine.
|
||||
``` bash
|
||||
MIX_ENV=prod mix deps.get
|
||||
MIX_ENV=prod mix firmware
|
||||
MIX_ENV=prod mix firmware.burn
|
||||
```
|
||||
|
||||
|
||||
Running on Local
|
||||
----------------
|
||||
|
||||
If you're running your own local [farmbot web app](https://github.com/farmbot/farmbot-web-app)
|
||||
|
||||
`FBENV=development ruby farmbot.rb`
|
||||
|
||||
|
||||
Installation on Raspberry Pi
|
||||
----------------------------
|
||||
|
||||
Installation on a Raspberry Pi 3 will take about 30 minutes.
|
||||
|
||||
### Update the RPi
|
||||
|
||||
```
|
||||
sudo apt-get update
|
||||
You can also run locally (not on an RPI3) (This works on windows)
|
||||
``` bash
|
||||
export MIX_ENV=dev
|
||||
rm -rf _deps build _images
|
||||
mix deps.get
|
||||
iex -S mix
|
||||
```
|
||||
You should only need to do the first two commands once.
|
||||
|
||||
### Install Ruby 2.2
|
||||
## Debugging on hardware
|
||||
The Rpi will boot up with an Iex console on the hardware UART. If you need to debug something this is the easiest to get too.
|
||||
If you do not happen to have a 3.3v FTDI cable, you can build the firmware with the console on HDMI. In the `erlinit.config` file you can change `-c ttyS0` to `-c ttyS1`. This requires you to plug a usb mouse, keyboard and monitor into your pi.
|
||||
|
||||
This gem requires Ruby 2.2 minimum. Later versions will work fine as well. As of this writing, a Pi is loaded with 1.9.3 by default.
|
||||
## Other stuff
|
||||
You can connect IEx to the running pi by doing
|
||||
`iex -name console@localhost --remsh farmbot@<FARMBOT IP ADDRESS> --cookie democookie`
|
||||
Debug message still only will print to UART or HDMI (whichever you have configured)
|
||||
|
||||
To remove the old version of Ruby that comes with most RPi distributions:
|
||||
If you are frequently building firmware, removing the sdcard and writing the build every time gets pretty old. You can upload firmware to an already running farmbot one of two ways after you run a successful `mix firmware`
|
||||
0. You can upload the image to the pi using CURL or similar. `curl -T _images/rpi3/fw.fw "http://$RPI_IP_ADDRESS:8988/firmware" -H "Content-Type: application/x-firmware" -H "X-Reboot: true"`
|
||||
0. Or you can host the .fw file on your pc using a webserver ie `npm install serve` and download it from the pi.This should be ran ON THE PI ITSELF `Downloader.download_and_install_update("http://<DEV_PC_IP_ADDRESS>/WHEREVER YOUR FILE IS")`
|
||||
|
||||
```
|
||||
sudo apt-get remove ruby* --purge
|
||||
```
|
||||
|
||||
To upgrade your ruby version, try this:
|
||||
|
||||
```
|
||||
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
|
||||
curl -L https://get.rvm.io | bash -s stable --ruby
|
||||
```
|
||||
|
||||
This will take about 2 hours a standard Raspberry Pi 2, or about 20 minutes on a Raspberry Pi 3.
|
||||
|
||||
### Clone, install and run
|
||||
|
||||
**NOTE:** We tag releases when they are stable. The latest version (on master) is not guaranteed to be stable.
|
||||
|
||||
See [releases](https://github.com/FarmBot/farmbot-raspberry-pi-controller/releases) to find a stable release.
|
||||
|
||||
**OPTION A:** For less stable "edge" version:
|
||||
|
||||
```
|
||||
git clone https://github.com/FarmBot/farmbot-raspberry-pi-controller
|
||||
```
|
||||
|
||||
**OPTION B:** For stable release 1.1:
|
||||
|
||||
```
|
||||
git clone -b 'alpha-1.1' --single-branch https://github.com/FarmBot/farmbot-raspberry-pi-controller
|
||||
```
|
||||
|
||||
**REQUIRED FOR EITHER:** (takes about 10 minutes on an RPi 3)
|
||||
|
||||
```
|
||||
cd farmbot-raspberry-pi-controller
|
||||
gem install bundler
|
||||
bundle install
|
||||
rake db:setup
|
||||
```
|
||||
|
||||
#### Set up the device:
|
||||
|
||||
Go to the [My Farmbot Website](http://my.farmbot.io) (or your private server) and sign up for a Farmbot account.
|
||||
|
||||
Then from within the `farmbot-raspberry-pi-controller` project directory run:
|
||||
|
||||
```bash
|
||||
ruby setup.rb
|
||||
```
|
||||
|
||||
#### Report problems:
|
||||
|
||||
We can't fix issues we don't know about. If you are having issues with setup, please [raise an issue with us](https://github.com/FarmBot/farmbot-raspberry-pi-controller/issues/new). This helps us identify confusing steps, common setup issues and other problems.
|
||||
|
||||
Arduino
|
||||
-------
|
||||
|
||||
You will need to flash your Arduino with custom firmware. For instructions on how to do this, see [the FarmBot-Arduino github page](https://github.com/FarmBot/farmbot-arduino-firmware)
|
||||
|
||||
Contributors
|
||||
-------
|
||||
|
||||
See full list [here](https://github.com/FarmBot/farmbot-raspberry-pi-controller/graphs/contributors).
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2016 Farmbot Project
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
# Tests
|
||||
Test coverage is not very good right now but you should be able to run `mix test --no-start` and see tests run.
|
||||
|
|
10
Rakefile
10
Rakefile
|
@ -1,10 +0,0 @@
|
|||
require 'active_record_migrations'
|
||||
|
||||
ActiveRecordMigrations.configure do |c|
|
||||
c.database_configuration = {
|
||||
'development' => {'adapter' => 'sqlite3',
|
||||
'database' => 'db/db.sqlite3'},
|
||||
}
|
||||
end
|
||||
|
||||
ActiveRecordMigrations.load_tasks
|
|
@ -0,0 +1,2 @@
|
|||
use Mix.Config
|
||||
import_config "#{Mix.env}.exs"
|
|
@ -0,0 +1,9 @@
|
|||
use Mix.Config
|
||||
config :uart,
|
||||
baud: 115200
|
||||
|
||||
config :fb,
|
||||
ro_path: "/tmp"
|
||||
|
||||
config :json_rpc,
|
||||
transport: MqttHandler
|
|
@ -0,0 +1,14 @@
|
|||
use Mix.Config
|
||||
config :nerves, :firmware,
|
||||
rootfs_additions: "config/rootfs-additions-#{Mix.Project.config[:target]}",
|
||||
hardware: "config/rootfs-additions-#{Mix.Project.config[:target]}"
|
||||
|
||||
config :uart,
|
||||
baud: 115200
|
||||
|
||||
config :fb,
|
||||
ro_path: "/root",
|
||||
update_server: "http://192.168.29.154:4040"
|
||||
|
||||
config :json_rpc,
|
||||
transport: MqttHandler
|
|
@ -0,0 +1,41 @@
|
|||
|
||||
# Additional configuration for erlinit
|
||||
|
||||
# Turn on the debug prints
|
||||
#-v
|
||||
|
||||
# Specify the UART port that the shell should use.
|
||||
-c ttyS0
|
||||
|
||||
# If more than one tty are available, always warn if the user is looking at
|
||||
# the wrong one.
|
||||
--warn-unused-tty
|
||||
|
||||
# Use dtach to capture the iex session so that it can be redirected
|
||||
# to the app's GUI
|
||||
#-s "/usr/bin/dtach -N /tmp/iex_prompt"
|
||||
|
||||
# Specify the user and group IDs for the Erlang VM
|
||||
#--uid 100
|
||||
#--gid 200
|
||||
|
||||
# Uncomment to hang the board rather than rebooting when Erlang exits
|
||||
--hang-on-exit
|
||||
|
||||
# Optionally run a program if the Erlang VM exits
|
||||
--run-on-exit /bin/sh
|
||||
|
||||
# Enable UTF-8 filename handling in Erlang and custom inet configuration
|
||||
-e LANG=en_US.UTF-8;LANGUAGE=en;ERL_INETRC=/etc/erl_inetrc
|
||||
|
||||
# Mount the application partition
|
||||
# See http://www.linuxfromscratch.org/lfs/view/6.3/chapter08/fstab.html about
|
||||
# ignoring warning the Linux kernel warning about using UTF8 with vfat.
|
||||
-m /dev/mmcblk0p3:/root:vfat::
|
||||
|
||||
# Erlang release search path
|
||||
-r /srv/erlang
|
||||
|
||||
# Assign a unique hostname based on the board id
|
||||
-d "/usr/bin/boardid -b rpi -n 4"
|
||||
-n nerves-%.4s
|
|
@ -0,0 +1,6 @@
|
|||
interface=wlan0
|
||||
ssid=FarmbotConfigurator
|
||||
hw_mode=g
|
||||
channel=6
|
||||
auth_algs=1
|
||||
wmm_enabled=0
|
|
@ -0,0 +1,2 @@
|
|||
127.0.0.1 localhost
|
||||
192.168.29.154 mqtt
|
|
@ -0,0 +1,318 @@
|
|||
/*
|
||||
AngularJS v1.5.8
|
||||
(c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(C){'use strict';function N(a){return function(){var b=arguments[0],d;d="["+(a?a+":":"")+b+"] http://errors.angularjs.org/1.5.8/"+(a?a+"/":"")+b;for(b=1;b<arguments.length;b++){d=d+(1==b?"?":"&")+"p"+(b-1)+"=";var c=encodeURIComponent,e;e=arguments[b];e="function"==typeof e?e.toString().replace(/ \{[\s\S]*$/,""):"undefined"==typeof e?"undefined":"string"!=typeof e?JSON.stringify(e):e;d+=c(e)}return Error(d)}}function ta(a){if(null==a||Va(a))return!1;if(L(a)||G(a)||F&&a instanceof F)return!0;
|
||||
var b="length"in Object(a)&&a.length;return T(b)&&(0<=b&&(b-1 in a||a instanceof Array)||"function"==typeof a.item)}function q(a,b,d){var c,e;if(a)if(z(a))for(c in a)"prototype"==c||"length"==c||"name"==c||a.hasOwnProperty&&!a.hasOwnProperty(c)||b.call(d,a[c],c,a);else if(L(a)||ta(a)){var f="object"!==typeof a;c=0;for(e=a.length;c<e;c++)(f||c in a)&&b.call(d,a[c],c,a)}else if(a.forEach&&a.forEach!==q)a.forEach(b,d,a);else if(sc(a))for(c in a)b.call(d,a[c],c,a);else if("function"===typeof a.hasOwnProperty)for(c in a)a.hasOwnProperty(c)&&
|
||||
b.call(d,a[c],c,a);else for(c in a)ua.call(a,c)&&b.call(d,a[c],c,a);return a}function tc(a,b,d){for(var c=Object.keys(a).sort(),e=0;e<c.length;e++)b.call(d,a[c[e]],c[e]);return c}function uc(a){return function(b,d){a(d,b)}}function Yd(){return++pb}function Pb(a,b,d){for(var c=a.$$hashKey,e=0,f=b.length;e<f;++e){var g=b[e];if(D(g)||z(g))for(var h=Object.keys(g),k=0,l=h.length;k<l;k++){var m=h[k],n=g[m];d&&D(n)?da(n)?a[m]=new Date(n.valueOf()):Wa(n)?a[m]=new RegExp(n):n.nodeName?a[m]=n.cloneNode(!0):
|
||||
Qb(n)?a[m]=n.clone():(D(a[m])||(a[m]=L(n)?[]:{}),Pb(a[m],[n],!0)):a[m]=n}}c?a.$$hashKey=c:delete a.$$hashKey;return a}function S(a){return Pb(a,va.call(arguments,1),!1)}function Zd(a){return Pb(a,va.call(arguments,1),!0)}function Z(a){return parseInt(a,10)}function Rb(a,b){return S(Object.create(a),b)}function A(){}function Xa(a){return a}function ha(a){return function(){return a}}function vc(a){return z(a.toString)&&a.toString!==ma}function y(a){return"undefined"===typeof a}function w(a){return"undefined"!==
|
||||
typeof a}function D(a){return null!==a&&"object"===typeof a}function sc(a){return null!==a&&"object"===typeof a&&!wc(a)}function G(a){return"string"===typeof a}function T(a){return"number"===typeof a}function da(a){return"[object Date]"===ma.call(a)}function z(a){return"function"===typeof a}function Wa(a){return"[object RegExp]"===ma.call(a)}function Va(a){return a&&a.window===a}function Ya(a){return a&&a.$evalAsync&&a.$watch}function Ga(a){return"boolean"===typeof a}function $d(a){return a&&T(a.length)&&
|
||||
ae.test(ma.call(a))}function Qb(a){return!(!a||!(a.nodeName||a.prop&&a.attr&&a.find))}function be(a){var b={};a=a.split(",");var d;for(d=0;d<a.length;d++)b[a[d]]=!0;return b}function wa(a){return Q(a.nodeName||a[0]&&a[0].nodeName)}function Za(a,b){var d=a.indexOf(b);0<=d&&a.splice(d,1);return d}function pa(a,b){function d(a,b){var d=b.$$hashKey,e;if(L(a)){e=0;for(var f=a.length;e<f;e++)b.push(c(a[e]))}else if(sc(a))for(e in a)b[e]=c(a[e]);else if(a&&"function"===typeof a.hasOwnProperty)for(e in a)a.hasOwnProperty(e)&&
|
||||
(b[e]=c(a[e]));else for(e in a)ua.call(a,e)&&(b[e]=c(a[e]));d?b.$$hashKey=d:delete b.$$hashKey;return b}function c(a){if(!D(a))return a;var b=f.indexOf(a);if(-1!==b)return g[b];if(Va(a)||Ya(a))throw xa("cpws");var b=!1,c=e(a);void 0===c&&(c=L(a)?[]:Object.create(wc(a)),b=!0);f.push(a);g.push(c);return b?d(a,c):c}function e(a){switch(ma.call(a)){case "[object Int8Array]":case "[object Int16Array]":case "[object Int32Array]":case "[object Float32Array]":case "[object Float64Array]":case "[object Uint8Array]":case "[object Uint8ClampedArray]":case "[object Uint16Array]":case "[object Uint32Array]":return new a.constructor(c(a.buffer),
|
||||
a.byteOffset,a.length);case "[object ArrayBuffer]":if(!a.slice){var b=new ArrayBuffer(a.byteLength);(new Uint8Array(b)).set(new Uint8Array(a));return b}return a.slice(0);case "[object Boolean]":case "[object Number]":case "[object String]":case "[object Date]":return new a.constructor(a.valueOf());case "[object RegExp]":return b=new RegExp(a.source,a.toString().match(/[^\/]*$/)[0]),b.lastIndex=a.lastIndex,b;case "[object Blob]":return new a.constructor([a],{type:a.type})}if(z(a.cloneNode))return a.cloneNode(!0)}
|
||||
var f=[],g=[];if(b){if($d(b)||"[object ArrayBuffer]"===ma.call(b))throw xa("cpta");if(a===b)throw xa("cpi");L(b)?b.length=0:q(b,function(a,d){"$$hashKey"!==d&&delete b[d]});f.push(a);g.push(b);return d(a,b)}return c(a)}function na(a,b){if(a===b)return!0;if(null===a||null===b)return!1;if(a!==a&&b!==b)return!0;var d=typeof a,c;if(d==typeof b&&"object"==d)if(L(a)){if(!L(b))return!1;if((d=a.length)==b.length){for(c=0;c<d;c++)if(!na(a[c],b[c]))return!1;return!0}}else{if(da(a))return da(b)?na(a.getTime(),
|
||||
b.getTime()):!1;if(Wa(a))return Wa(b)?a.toString()==b.toString():!1;if(Ya(a)||Ya(b)||Va(a)||Va(b)||L(b)||da(b)||Wa(b))return!1;d=U();for(c in a)if("$"!==c.charAt(0)&&!z(a[c])){if(!na(a[c],b[c]))return!1;d[c]=!0}for(c in b)if(!(c in d)&&"$"!==c.charAt(0)&&w(b[c])&&!z(b[c]))return!1;return!0}return!1}function $a(a,b,d){return a.concat(va.call(b,d))}function ab(a,b){var d=2<arguments.length?va.call(arguments,2):[];return!z(b)||b instanceof RegExp?b:d.length?function(){return arguments.length?b.apply(a,
|
||||
$a(d,arguments,0)):b.apply(a,d)}:function(){return arguments.length?b.apply(a,arguments):b.call(a)}}function ce(a,b){var d=b;"string"===typeof a&&"$"===a.charAt(0)&&"$"===a.charAt(1)?d=void 0:Va(b)?d="$WINDOW":b&&C.document===b?d="$DOCUMENT":Ya(b)&&(d="$SCOPE");return d}function bb(a,b){if(!y(a))return T(b)||(b=b?2:null),JSON.stringify(a,ce,b)}function xc(a){return G(a)?JSON.parse(a):a}function yc(a,b){a=a.replace(de,"");var d=Date.parse("Jan 01, 1970 00:00:00 "+a)/6E4;return isNaN(d)?b:d}function Sb(a,
|
||||
b,d){d=d?-1:1;var c=a.getTimezoneOffset();b=yc(b,c);d*=b-c;a=new Date(a.getTime());a.setMinutes(a.getMinutes()+d);return a}function ya(a){a=F(a).clone();try{a.empty()}catch(b){}var d=F("<div>").append(a).html();try{return a[0].nodeType===Ma?Q(d):d.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+Q(b)})}catch(c){return Q(d)}}function zc(a){try{return decodeURIComponent(a)}catch(b){}}function Ac(a){var b={};q((a||"").split("&"),function(a){var c,e,f;a&&(e=a=a.replace(/\+/g,"%20"),
|
||||
c=a.indexOf("="),-1!==c&&(e=a.substring(0,c),f=a.substring(c+1)),e=zc(e),w(e)&&(f=w(f)?zc(f):!0,ua.call(b,e)?L(b[e])?b[e].push(f):b[e]=[b[e],f]:b[e]=f))});return b}function Tb(a){var b=[];q(a,function(a,c){L(a)?q(a,function(a){b.push(ea(c,!0)+(!0===a?"":"="+ea(a,!0)))}):b.push(ea(c,!0)+(!0===a?"":"="+ea(a,!0)))});return b.length?b.join("&"):""}function qb(a){return ea(a,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function ea(a,b){return encodeURIComponent(a).replace(/%40/gi,
|
||||
"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%3B/gi,";").replace(/%20/g,b?"%20":"+")}function ee(a,b){var d,c,e=Na.length;for(c=0;c<e;++c)if(d=Na[c]+b,G(d=a.getAttribute(d)))return d;return null}function fe(a,b){var d,c,e={};q(Na,function(b){b+="app";!d&&a.hasAttribute&&a.hasAttribute(b)&&(d=a,c=a.getAttribute(b))});q(Na,function(b){b+="app";var e;!d&&(e=a.querySelector("["+b.replace(":","\\:")+"]"))&&(d=e,c=e.getAttribute(b))});d&&(e.strictDi=null!==ee(d,"strict-di"),
|
||||
b(d,c?[c]:[],e))}function Bc(a,b,d){D(d)||(d={});d=S({strictDi:!1},d);var c=function(){a=F(a);if(a.injector()){var c=a[0]===C.document?"document":ya(a);throw xa("btstrpd",c.replace(/</,"<").replace(/>/,">"));}b=b||[];b.unshift(["$provide",function(b){b.value("$rootElement",a)}]);d.debugInfoEnabled&&b.push(["$compileProvider",function(a){a.debugInfoEnabled(!0)}]);b.unshift("ng");c=cb(b,d.strictDi);c.invoke(["$rootScope","$rootElement","$compile","$injector",function(a,b,c,d){a.$apply(function(){b.data("$injector",
|
||||
d);c(b)(a)})}]);return c},e=/^NG_ENABLE_DEBUG_INFO!/,f=/^NG_DEFER_BOOTSTRAP!/;C&&e.test(C.name)&&(d.debugInfoEnabled=!0,C.name=C.name.replace(e,""));if(C&&!f.test(C.name))return c();C.name=C.name.replace(f,"");ca.resumeBootstrap=function(a){q(a,function(a){b.push(a)});return c()};z(ca.resumeDeferredBootstrap)&&ca.resumeDeferredBootstrap()}function ge(){C.name="NG_ENABLE_DEBUG_INFO!"+C.name;C.location.reload()}function he(a){a=ca.element(a).injector();if(!a)throw xa("test");return a.get("$$testability")}
|
||||
function Cc(a,b){b=b||"_";return a.replace(ie,function(a,c){return(c?b:"")+a.toLowerCase()})}function je(){var a;if(!Dc){var b=rb();(qa=y(b)?C.jQuery:b?C[b]:void 0)&&qa.fn.on?(F=qa,S(qa.fn,{scope:Oa.scope,isolateScope:Oa.isolateScope,controller:Oa.controller,injector:Oa.injector,inheritedData:Oa.inheritedData}),a=qa.cleanData,qa.cleanData=function(b){for(var c,e=0,f;null!=(f=b[e]);e++)(c=qa._data(f,"events"))&&c.$destroy&&qa(f).triggerHandler("$destroy");a(b)}):F=O;ca.element=F;Dc=!0}}function sb(a,
|
||||
b,d){if(!a)throw xa("areq",b||"?",d||"required");return a}function Pa(a,b,d){d&&L(a)&&(a=a[a.length-1]);sb(z(a),b,"not a function, got "+(a&&"object"===typeof a?a.constructor.name||"Object":typeof a));return a}function Qa(a,b){if("hasOwnProperty"===a)throw xa("badname",b);}function Ec(a,b,d){if(!b)return a;b=b.split(".");for(var c,e=a,f=b.length,g=0;g<f;g++)c=b[g],a&&(a=(e=a)[c]);return!d&&z(a)?ab(e,a):a}function tb(a){for(var b=a[0],d=a[a.length-1],c,e=1;b!==d&&(b=b.nextSibling);e++)if(c||a[e]!==
|
||||
b)c||(c=F(va.call(a,0,e))),c.push(b);return c||a}function U(){return Object.create(null)}function ke(a){function b(a,b,c){return a[b]||(a[b]=c())}var d=N("$injector"),c=N("ng");a=b(a,"angular",Object);a.$$minErr=a.$$minErr||N;return b(a,"module",function(){var a={};return function(f,g,h){if("hasOwnProperty"===f)throw c("badname","module");g&&a.hasOwnProperty(f)&&(a[f]=null);return b(a,f,function(){function a(b,d,e,f){f||(f=c);return function(){f[e||"push"]([b,d,arguments]);return R}}function b(a,
|
||||
d){return function(b,e){e&&z(e)&&(e.$$moduleName=f);c.push([a,d,arguments]);return R}}if(!g)throw d("nomod",f);var c=[],e=[],p=[],u=a("$injector","invoke","push",e),R={_invokeQueue:c,_configBlocks:e,_runBlocks:p,requires:g,name:f,provider:b("$provide","provider"),factory:b("$provide","factory"),service:b("$provide","service"),value:a("$provide","value"),constant:a("$provide","constant","unshift"),decorator:b("$provide","decorator"),animation:b("$animateProvider","register"),filter:b("$filterProvider",
|
||||
"register"),controller:b("$controllerProvider","register"),directive:b("$compileProvider","directive"),component:b("$compileProvider","component"),config:u,run:function(a){p.push(a);return this}};h&&u(h);return R})}})}function ia(a,b){if(L(a)){b=b||[];for(var d=0,c=a.length;d<c;d++)b[d]=a[d]}else if(D(a))for(d in b=b||{},a)if("$"!==d.charAt(0)||"$"!==d.charAt(1))b[d]=a[d];return b||a}function le(a){S(a,{bootstrap:Bc,copy:pa,extend:S,merge:Zd,equals:na,element:F,forEach:q,injector:cb,noop:A,bind:ab,
|
||||
toJson:bb,fromJson:xc,identity:Xa,isUndefined:y,isDefined:w,isString:G,isFunction:z,isObject:D,isNumber:T,isElement:Qb,isArray:L,version:me,isDate:da,lowercase:Q,uppercase:ub,callbacks:{$$counter:0},getTestability:he,$$minErr:N,$$csp:Ba,reloadWithDebugInfo:ge});Ub=ke(C);Ub("ng",["ngLocale"],["$provide",function(a){a.provider({$$sanitizeUri:ne});a.provider("$compile",Fc).directive({a:oe,input:Gc,textarea:Gc,form:pe,script:qe,select:re,style:se,option:te,ngBind:ue,ngBindHtml:ve,ngBindTemplate:we,ngClass:xe,
|
||||
ngClassEven:ye,ngClassOdd:ze,ngCloak:Ae,ngController:Be,ngForm:Ce,ngHide:De,ngIf:Ee,ngInclude:Fe,ngInit:Ge,ngNonBindable:He,ngPluralize:Ie,ngRepeat:Je,ngShow:Ke,ngStyle:Le,ngSwitch:Me,ngSwitchWhen:Ne,ngSwitchDefault:Oe,ngOptions:Pe,ngTransclude:Qe,ngModel:Re,ngList:Se,ngChange:Te,pattern:Hc,ngPattern:Hc,required:Ic,ngRequired:Ic,minlength:Jc,ngMinlength:Jc,maxlength:Kc,ngMaxlength:Kc,ngValue:Ue,ngModelOptions:Ve}).directive({ngInclude:We}).directive(vb).directive(Lc);a.provider({$anchorScroll:Xe,
|
||||
$animate:Ye,$animateCss:Ze,$$animateJs:$e,$$animateQueue:af,$$AnimateRunner:bf,$$animateAsyncRun:cf,$browser:df,$cacheFactory:ef,$controller:ff,$document:gf,$exceptionHandler:hf,$filter:Mc,$$forceReflow:jf,$interpolate:kf,$interval:lf,$http:mf,$httpParamSerializer:nf,$httpParamSerializerJQLike:of,$httpBackend:pf,$xhrFactory:qf,$jsonpCallbacks:rf,$location:sf,$log:tf,$parse:uf,$rootScope:vf,$q:wf,$$q:xf,$sce:yf,$sceDelegate:zf,$sniffer:Af,$templateCache:Bf,$templateRequest:Cf,$$testability:Df,$timeout:Ef,
|
||||
$window:Ff,$$rAF:Gf,$$jqLite:Hf,$$HashMap:If,$$cookieReader:Jf})}])}function db(a){return a.replace(Kf,function(a,d,c,e){return e?c.toUpperCase():c}).replace(Lf,"Moz$1")}function Nc(a){a=a.nodeType;return 1===a||!a||9===a}function Oc(a,b){var d,c,e=b.createDocumentFragment(),f=[];if(Vb.test(a)){d=e.appendChild(b.createElement("div"));c=(Mf.exec(a)||["",""])[1].toLowerCase();c=ja[c]||ja._default;d.innerHTML=c[1]+a.replace(Nf,"<$1></$2>")+c[2];for(c=c[0];c--;)d=d.lastChild;f=$a(f,d.childNodes);d=e.firstChild;
|
||||
d.textContent=""}else f.push(b.createTextNode(a));e.textContent="";e.innerHTML="";q(f,function(a){e.appendChild(a)});return e}function Pc(a,b){var d=a.parentNode;d&&d.replaceChild(b,a);b.appendChild(a)}function O(a){if(a instanceof O)return a;var b;G(a)&&(a=W(a),b=!0);if(!(this instanceof O)){if(b&&"<"!=a.charAt(0))throw Wb("nosel");return new O(a)}if(b){b=C.document;var d;a=(d=Of.exec(a))?[b.createElement(d[1])]:(d=Oc(a,b))?d.childNodes:[]}Qc(this,a)}function Xb(a){return a.cloneNode(!0)}function wb(a,
|
||||
b){b||eb(a);if(a.querySelectorAll)for(var d=a.querySelectorAll("*"),c=0,e=d.length;c<e;c++)eb(d[c])}function Rc(a,b,d,c){if(w(c))throw Wb("offargs");var e=(c=xb(a))&&c.events,f=c&&c.handle;if(f)if(b){var g=function(b){var c=e[b];w(d)&&Za(c||[],d);w(d)&&c&&0<c.length||(a.removeEventListener(b,f,!1),delete e[b])};q(b.split(" "),function(a){g(a);yb[a]&&g(yb[a])})}else for(b in e)"$destroy"!==b&&a.removeEventListener(b,f,!1),delete e[b]}function eb(a,b){var d=a.ng339,c=d&&fb[d];c&&(b?delete c.data[b]:
|
||||
(c.handle&&(c.events.$destroy&&c.handle({},"$destroy"),Rc(a)),delete fb[d],a.ng339=void 0))}function xb(a,b){var d=a.ng339,d=d&&fb[d];b&&!d&&(a.ng339=d=++Pf,d=fb[d]={events:{},data:{},handle:void 0});return d}function Yb(a,b,d){if(Nc(a)){var c=w(d),e=!c&&b&&!D(b),f=!b;a=(a=xb(a,!e))&&a.data;if(c)a[b]=d;else{if(f)return a;if(e)return a&&a[b];S(a,b)}}}function zb(a,b){return a.getAttribute?-1<(" "+(a.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").indexOf(" "+b+" "):!1}function Ab(a,b){b&&a.setAttribute&&
|
||||
q(b.split(" "),function(b){a.setAttribute("class",W((" "+(a.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").replace(" "+W(b)+" "," ")))})}function Bb(a,b){if(b&&a.setAttribute){var d=(" "+(a.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ");q(b.split(" "),function(a){a=W(a);-1===d.indexOf(" "+a+" ")&&(d+=a+" ")});a.setAttribute("class",W(d))}}function Qc(a,b){if(b)if(b.nodeType)a[a.length++]=b;else{var d=b.length;if("number"===typeof d&&b.window!==b){if(d)for(var c=0;c<d;c++)a[a.length++]=
|
||||
b[c]}else a[a.length++]=b}}function Sc(a,b){return Cb(a,"$"+(b||"ngController")+"Controller")}function Cb(a,b,d){9==a.nodeType&&(a=a.documentElement);for(b=L(b)?b:[b];a;){for(var c=0,e=b.length;c<e;c++)if(w(d=F.data(a,b[c])))return d;a=a.parentNode||11===a.nodeType&&a.host}}function Tc(a){for(wb(a,!0);a.firstChild;)a.removeChild(a.firstChild)}function Db(a,b){b||wb(a);var d=a.parentNode;d&&d.removeChild(a)}function Qf(a,b){b=b||C;if("complete"===b.document.readyState)b.setTimeout(a);else F(b).on("load",
|
||||
a)}function Uc(a,b){var d=Eb[b.toLowerCase()];return d&&Vc[wa(a)]&&d}function Rf(a,b){var d=function(c,d){c.isDefaultPrevented=function(){return c.defaultPrevented};var f=b[d||c.type],g=f?f.length:0;if(g){if(y(c.immediatePropagationStopped)){var h=c.stopImmediatePropagation;c.stopImmediatePropagation=function(){c.immediatePropagationStopped=!0;c.stopPropagation&&c.stopPropagation();h&&h.call(c)}}c.isImmediatePropagationStopped=function(){return!0===c.immediatePropagationStopped};var k=f.specialHandlerWrapper||
|
||||
Sf;1<g&&(f=ia(f));for(var l=0;l<g;l++)c.isImmediatePropagationStopped()||k(a,c,f[l])}};d.elem=a;return d}function Sf(a,b,d){d.call(a,b)}function Tf(a,b,d){var c=b.relatedTarget;c&&(c===a||Uf.call(a,c))||d.call(a,b)}function Hf(){this.$get=function(){return S(O,{hasClass:function(a,b){a.attr&&(a=a[0]);return zb(a,b)},addClass:function(a,b){a.attr&&(a=a[0]);return Bb(a,b)},removeClass:function(a,b){a.attr&&(a=a[0]);return Ab(a,b)}})}}function Ca(a,b){var d=a&&a.$$hashKey;if(d)return"function"===typeof d&&
|
||||
(d=a.$$hashKey()),d;d=typeof a;return d="function"==d||"object"==d&&null!==a?a.$$hashKey=d+":"+(b||Yd)():d+":"+a}function Ra(a,b){if(b){var d=0;this.nextUid=function(){return++d}}q(a,this.put,this)}function Wc(a){a=(Function.prototype.toString.call(a)+" ").replace(Vf,"");return a.match(Wf)||a.match(Xf)}function Yf(a){return(a=Wc(a))?"function("+(a[1]||"").replace(/[\s\r\n]+/," ")+")":"fn"}function cb(a,b){function d(a){return function(b,c){if(D(b))q(b,uc(a));else return a(b,c)}}function c(a,b){Qa(a,
|
||||
"service");if(z(b)||L(b))b=p.instantiate(b);if(!b.$get)throw Ha("pget",a);return n[a+"Provider"]=b}function e(a,b){return function(){var c=B.invoke(b,this);if(y(c))throw Ha("undef",a);return c}}function f(a,b,d){return c(a,{$get:!1!==d?e(a,b):b})}function g(a){sb(y(a)||L(a),"modulesToLoad","not an array");var b=[],c;q(a,function(a){function d(a){var b,c;b=0;for(c=a.length;b<c;b++){var e=a[b],f=p.get(e[0]);f[e[1]].apply(f,e[2])}}if(!m.get(a)){m.put(a,!0);try{G(a)?(c=Ub(a),b=b.concat(g(c.requires)).concat(c._runBlocks),
|
||||
d(c._invokeQueue),d(c._configBlocks)):z(a)?b.push(p.invoke(a)):L(a)?b.push(p.invoke(a)):Pa(a,"module")}catch(e){throw L(a)&&(a=a[a.length-1]),e.message&&e.stack&&-1==e.stack.indexOf(e.message)&&(e=e.message+"\n"+e.stack),Ha("modulerr",a,e.stack||e.message||e);}}});return b}function h(a,c){function d(b,e){if(a.hasOwnProperty(b)){if(a[b]===k)throw Ha("cdep",b+" <- "+l.join(" <- "));return a[b]}try{return l.unshift(b),a[b]=k,a[b]=c(b,e)}catch(f){throw a[b]===k&&delete a[b],f;}finally{l.shift()}}function e(a,
|
||||
c,f){var g=[];a=cb.$$annotate(a,b,f);for(var h=0,k=a.length;h<k;h++){var l=a[h];if("string"!==typeof l)throw Ha("itkn",l);g.push(c&&c.hasOwnProperty(l)?c[l]:d(l,f))}return g}return{invoke:function(a,b,c,d){"string"===typeof c&&(d=c,c=null);c=e(a,c,d);L(a)&&(a=a[a.length-1]);d=11>=Ea?!1:"function"===typeof a&&/^(?:class\b|constructor\()/.test(Function.prototype.toString.call(a)+" ");return d?(c.unshift(null),new (Function.prototype.bind.apply(a,c))):a.apply(b,c)},instantiate:function(a,b,c){var d=
|
||||
L(a)?a[a.length-1]:a;a=e(a,b,c);a.unshift(null);return new (Function.prototype.bind.apply(d,a))},get:d,annotate:cb.$$annotate,has:function(b){return n.hasOwnProperty(b+"Provider")||a.hasOwnProperty(b)}}}b=!0===b;var k={},l=[],m=new Ra([],!0),n={$provide:{provider:d(c),factory:d(f),service:d(function(a,b){return f(a,["$injector",function(a){return a.instantiate(b)}])}),value:d(function(a,b){return f(a,ha(b),!1)}),constant:d(function(a,b){Qa(a,"constant");n[a]=b;u[a]=b}),decorator:function(a,b){var c=
|
||||
p.get(a+"Provider"),d=c.$get;c.$get=function(){var a=B.invoke(d,c);return B.invoke(b,null,{$delegate:a})}}}},p=n.$injector=h(n,function(a,b){ca.isString(b)&&l.push(b);throw Ha("unpr",l.join(" <- "));}),u={},R=h(u,function(a,b){var c=p.get(a+"Provider",b);return B.invoke(c.$get,c,void 0,a)}),B=R;n.$injectorProvider={$get:ha(R)};var r=g(a),B=R.get("$injector");B.strictDi=b;q(r,function(a){a&&B.invoke(a)});return B}function Xe(){var a=!0;this.disableAutoScrolling=function(){a=!1};this.$get=["$window",
|
||||
"$location","$rootScope",function(b,d,c){function e(a){var b=null;Array.prototype.some.call(a,function(a){if("a"===wa(a))return b=a,!0});return b}function f(a){if(a){a.scrollIntoView();var c;c=g.yOffset;z(c)?c=c():Qb(c)?(c=c[0],c="fixed"!==b.getComputedStyle(c).position?0:c.getBoundingClientRect().bottom):T(c)||(c=0);c&&(a=a.getBoundingClientRect().top,b.scrollBy(0,a-c))}else b.scrollTo(0,0)}function g(a){a=G(a)?a:d.hash();var b;a?(b=h.getElementById(a))?f(b):(b=e(h.getElementsByName(a)))?f(b):"top"===
|
||||
a&&f(null):f(null)}var h=b.document;a&&c.$watch(function(){return d.hash()},function(a,b){a===b&&""===a||Qf(function(){c.$evalAsync(g)})});return g}]}function gb(a,b){if(!a&&!b)return"";if(!a)return b;if(!b)return a;L(a)&&(a=a.join(" "));L(b)&&(b=b.join(" "));return a+" "+b}function Zf(a){G(a)&&(a=a.split(" "));var b=U();q(a,function(a){a.length&&(b[a]=!0)});return b}function Ia(a){return D(a)?a:{}}function $f(a,b,d,c){function e(a){try{a.apply(null,va.call(arguments,1))}finally{if(R--,0===R)for(;B.length;)try{B.pop()()}catch(b){d.error(b)}}}
|
||||
function f(){t=null;g();h()}function g(){r=K();r=y(r)?null:r;na(r,E)&&(r=E);E=r}function h(){if(v!==k.url()||J!==r)v=k.url(),J=r,q(M,function(a){a(k.url(),r)})}var k=this,l=a.location,m=a.history,n=a.setTimeout,p=a.clearTimeout,u={};k.isMock=!1;var R=0,B=[];k.$$completeOutstandingRequest=e;k.$$incOutstandingRequestCount=function(){R++};k.notifyWhenNoOutstandingRequests=function(a){0===R?a():B.push(a)};var r,J,v=l.href,fa=b.find("base"),t=null,K=c.history?function(){try{return m.state}catch(a){}}:
|
||||
A;g();J=r;k.url=function(b,d,e){y(e)&&(e=null);l!==a.location&&(l=a.location);m!==a.history&&(m=a.history);if(b){var f=J===e;if(v===b&&(!c.history||f))return k;var h=v&&Ja(v)===Ja(b);v=b;J=e;!c.history||h&&f?(h||(t=b),d?l.replace(b):h?(d=l,e=b.indexOf("#"),e=-1===e?"":b.substr(e),d.hash=e):l.href=b,l.href!==b&&(t=b)):(m[d?"replaceState":"pushState"](e,"",b),g(),J=r);t&&(t=b);return k}return t||l.href.replace(/%27/g,"'")};k.state=function(){return r};var M=[],H=!1,E=null;k.onUrlChange=function(b){if(!H){if(c.history)F(a).on("popstate",
|
||||
f);F(a).on("hashchange",f);H=!0}M.push(b);return b};k.$$applicationDestroyed=function(){F(a).off("hashchange popstate",f)};k.$$checkUrlChange=h;k.baseHref=function(){var a=fa.attr("href");return a?a.replace(/^(https?\:)?\/\/[^\/]*/,""):""};k.defer=function(a,b){var c;R++;c=n(function(){delete u[c];e(a)},b||0);u[c]=!0;return c};k.defer.cancel=function(a){return u[a]?(delete u[a],p(a),e(A),!0):!1}}function df(){this.$get=["$window","$log","$sniffer","$document",function(a,b,d,c){return new $f(a,c,b,
|
||||
d)}]}function ef(){this.$get=function(){function a(a,c){function e(a){a!=n&&(p?p==a&&(p=a.n):p=a,f(a.n,a.p),f(a,n),n=a,n.n=null)}function f(a,b){a!=b&&(a&&(a.p=b),b&&(b.n=a))}if(a in b)throw N("$cacheFactory")("iid",a);var g=0,h=S({},c,{id:a}),k=U(),l=c&&c.capacity||Number.MAX_VALUE,m=U(),n=null,p=null;return b[a]={put:function(a,b){if(!y(b)){if(l<Number.MAX_VALUE){var c=m[a]||(m[a]={key:a});e(c)}a in k||g++;k[a]=b;g>l&&this.remove(p.key);return b}},get:function(a){if(l<Number.MAX_VALUE){var b=m[a];
|
||||
if(!b)return;e(b)}return k[a]},remove:function(a){if(l<Number.MAX_VALUE){var b=m[a];if(!b)return;b==n&&(n=b.p);b==p&&(p=b.n);f(b.n,b.p);delete m[a]}a in k&&(delete k[a],g--)},removeAll:function(){k=U();g=0;m=U();n=p=null},destroy:function(){m=h=k=null;delete b[a]},info:function(){return S({},h,{size:g})}}}var b={};a.info=function(){var a={};q(b,function(b,e){a[e]=b.info()});return a};a.get=function(a){return b[a]};return a}}function Bf(){this.$get=["$cacheFactory",function(a){return a("templates")}]}
|
||||
function Fc(a,b){function d(a,b,c){var d=/^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/,e=U();q(a,function(a,f){if(a in n)e[f]=n[a];else{var g=a.match(d);if(!g)throw ga("iscp",b,f,a,c?"controller bindings definition":"isolate scope definition");e[f]={mode:g[1][0],collection:"*"===g[2],optional:"?"===g[3],attrName:g[4]||f};g[4]&&(n[a]=e[f])}});return e}function c(a){var b=a.charAt(0);if(!b||b!==Q(b))throw ga("baddir",a);if(a!==a.trim())throw ga("baddir",a);}function e(a){var b=a.require||a.controller&&a.name;
|
||||
!L(b)&&D(b)&&q(b,function(a,c){var d=a.match(l);a.substring(d[0].length)||(b[c]=d[0]+c)});return b}var f={},g=/^\s*directive\:\s*([\w\-]+)\s+(.*)$/,h=/(([\w\-]+)(?:\:([^;]+))?;?)/,k=be("ngSrc,ngSrcset,src,srcset"),l=/^(?:(\^\^?)?(\?)?(\^\^?)?)?/,m=/^(on[a-z]+|formaction)$/,n=U();this.directive=function B(b,d){Qa(b,"directive");G(b)?(c(b),sb(d,"directiveFactory"),f.hasOwnProperty(b)||(f[b]=[],a.factory(b+"Directive",["$injector","$exceptionHandler",function(a,c){var d=[];q(f[b],function(f,g){try{var h=
|
||||
a.invoke(f);z(h)?h={compile:ha(h)}:!h.compile&&h.link&&(h.compile=ha(h.link));h.priority=h.priority||0;h.index=g;h.name=h.name||b;h.require=e(h);h.restrict=h.restrict||"EA";h.$$moduleName=f.$$moduleName;d.push(h)}catch(k){c(k)}});return d}])),f[b].push(d)):q(b,uc(B));return this};this.component=function(a,b){function c(a){function e(b){return z(b)||L(b)?function(c,d){return a.invoke(b,this,{$element:c,$attrs:d})}:b}var f=b.template||b.templateUrl?b.template:"",g={controller:d,controllerAs:Xc(b.controller)||
|
||||
b.controllerAs||"$ctrl",template:e(f),templateUrl:e(b.templateUrl),transclude:b.transclude,scope:{},bindToController:b.bindings||{},restrict:"E",require:b.require};q(b,function(a,b){"$"===b.charAt(0)&&(g[b]=a)});return g}var d=b.controller||function(){};q(b,function(a,b){"$"===b.charAt(0)&&(c[b]=a,z(d)&&(d[b]=a))});c.$inject=["$injector"];return this.directive(a,c)};this.aHrefSanitizationWhitelist=function(a){return w(a)?(b.aHrefSanitizationWhitelist(a),this):b.aHrefSanitizationWhitelist()};this.imgSrcSanitizationWhitelist=
|
||||
function(a){return w(a)?(b.imgSrcSanitizationWhitelist(a),this):b.imgSrcSanitizationWhitelist()};var p=!0;this.debugInfoEnabled=function(a){return w(a)?(p=a,this):p};var u=10;this.onChangesTtl=function(a){return arguments.length?(u=a,this):u};this.$get=["$injector","$interpolate","$exceptionHandler","$templateRequest","$parse","$controller","$rootScope","$sce","$animate","$$sanitizeUri",function(a,b,c,e,n,t,K,M,H,E){function I(){try{if(!--qa)throw Y=void 0,ga("infchng",u);K.$apply(function(){for(var a=
|
||||
[],b=0,c=Y.length;b<c;++b)try{Y[b]()}catch(d){a.push(d)}Y=void 0;if(a.length)throw a;})}finally{qa++}}function Da(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}function P(a,b,c){pa.innerHTML="<span "+b+">";b=pa.firstChild.attributes;var d=b[0];b.removeNamedItem(d.name);d.value=c;a.attributes.setNamedItem(d)}function x(a,b){try{a.addClass(b)}catch(c){}}function aa(a,b,c,d,e){a instanceof F||(a=F(a));for(var f=/\S+/,g=0,h=a.length;g<
|
||||
h;g++){var k=a[g];k.nodeType===Ma&&k.nodeValue.match(f)&&Pc(k,a[g]=C.document.createElement("span"))}var l=s(a,b,a,c,d,e);aa.$$addScopeClass(a);var m=null;return function(b,c,d){sb(b,"scope");e&&e.needsNewScope&&(b=b.$parent.$new());d=d||{};var f=d.parentBoundTranscludeFn,g=d.transcludeControllers;d=d.futureParentElement;f&&f.$$boundTransclude&&(f=f.$$boundTransclude);m||(m=(d=d&&d[0])?"foreignobject"!==wa(d)&&ma.call(d).match(/SVG/)?"svg":"html":"html");d="html"!==m?F(da(m,F("<div>").append(a).html())):
|
||||
c?Oa.clone.call(a):a;if(g)for(var h in g)d.data("$"+h+"Controller",g[h].instance);aa.$$addScopeInfo(d,b);c&&c(d,b);l&&l(b,d,d,f);return d}}function s(a,b,c,d,e,f){function g(a,c,d,e){var f,k,l,m,p,r,v;if(n)for(v=Array(c.length),m=0;m<h.length;m+=3)f=h[m],v[f]=c[f];else v=c;m=0;for(p=h.length;m<p;)k=v[h[m++]],c=h[m++],f=h[m++],c?(c.scope?(l=a.$new(),aa.$$addScopeInfo(F(k),l)):l=a,r=c.transcludeOnThisElement?za(a,c.transclude,e):!c.templateOnThisElement&&e?e:!e&&b?za(a,b):null,c(f,l,k,d,r)):f&&f(a,
|
||||
k.childNodes,void 0,e)}for(var h=[],k,l,m,p,n,r=0;r<a.length;r++){k=new Da;l=$b(a[r],[],k,0===r?d:void 0,e);(f=l.length?oa(l,a[r],k,b,c,null,[],[],f):null)&&f.scope&&aa.$$addScopeClass(k.$$element);k=f&&f.terminal||!(m=a[r].childNodes)||!m.length?null:s(m,f?(f.transcludeOnThisElement||!f.templateOnThisElement)&&f.transclude:b);if(f||k)h.push(r,f,k),p=!0,n=n||f;f=null}return p?g:null}function za(a,b,c){function d(e,f,g,h,k){e||(e=a.$new(!1,k),e.$$transcluded=!0);return b(e,f,{parentBoundTranscludeFn:c,
|
||||
transcludeControllers:g,futureParentElement:h})}var e=d.$$slots=U(),f;for(f in b.$$slots)e[f]=b.$$slots[f]?za(a,b.$$slots[f],c):null;return d}function $b(a,b,c,d,e){var f=c.$attr;switch(a.nodeType){case 1:O(b,Aa(wa(a)),"E",d,e);for(var g,k,l,m,p=a.attributes,n=0,r=p&&p.length;n<r;n++){var v=!1,u=!1;g=p[n];k=g.name;l=W(g.value);g=Aa(k);if(m=Ba.test(g))k=k.replace(Yc,"").substr(8).replace(/_(.)/g,function(a,b){return b.toUpperCase()});(g=g.match(Ca))&&V(g[1])&&(v=k,u=k.substr(0,k.length-5)+"end",k=
|
||||
k.substr(0,k.length-6));g=Aa(k.toLowerCase());f[g]=k;if(m||!c.hasOwnProperty(g))c[g]=l,Uc(a,g)&&(c[g]=!0);ia(a,b,l,g,m);O(b,g,"A",d,e,v,u)}f=a.className;D(f)&&(f=f.animVal);if(G(f)&&""!==f)for(;a=h.exec(f);)g=Aa(a[2]),O(b,g,"C",d,e)&&(c[g]=W(a[3])),f=f.substr(a.index+a[0].length);break;case Ma:if(11===Ea)for(;a.parentNode&&a.nextSibling&&a.nextSibling.nodeType===Ma;)a.nodeValue+=a.nextSibling.nodeValue,a.parentNode.removeChild(a.nextSibling);ca(b,a.nodeValue);break;case 8:hb(a,b,c,d,e)}b.sort(Z);
|
||||
return b}function hb(a,b,c,d,e){try{var f=g.exec(a.nodeValue);if(f){var h=Aa(f[1]);O(b,h,"M",d,e)&&(c[h]=W(f[2]))}}catch(k){}}function N(a,b,c){var d=[],e=0;if(b&&a.hasAttribute&&a.hasAttribute(b)){do{if(!a)throw ga("uterdir",b,c);1==a.nodeType&&(a.hasAttribute(b)&&e++,a.hasAttribute(c)&&e--);d.push(a);a=a.nextSibling}while(0<e)}else d.push(a);return F(d)}function Zc(a,b,c){return function(d,e,f,g,h){e=N(e[0],b,c);return a(d,e,f,g,h)}}function ac(a,b,c,d,e,f){var g;return a?aa(b,c,d,e,f):function(){g||
|
||||
(g=aa(b,c,d,e,f),b=c=f=null);return g.apply(this,arguments)}}function oa(a,b,d,e,f,g,h,k,l){function m(a,b,c,d){if(a){c&&(a=Zc(a,c,d));a.require=x.require;a.directiveName=I;if(u===x||x.$$isolateScope)a=ja(a,{isolateScope:!0});h.push(a)}if(b){c&&(b=Zc(b,c,d));b.require=x.require;b.directiveName=I;if(u===x||x.$$isolateScope)b=ja(b,{isolateScope:!0});k.push(b)}}function p(a,e,f,g,l){function m(a,b,c,d){var e;Ya(a)||(d=c,c=b,b=a,a=void 0);fa&&(e=t);c||(c=fa?I.parent():I);if(d){var f=l.$$slots[d];if(f)return f(a,
|
||||
b,e,c,s);if(y(f))throw ga("noslot",d,ya(I));}else return l(a,b,e,c,s)}var n,E,x,M,B,t,P,I;b===f?(g=d,I=d.$$element):(I=F(f),g=new Da(I,d));B=e;u?M=e.$new(!0):r&&(B=e.$parent);l&&(P=m,P.$$boundTransclude=l,P.isSlotFilled=function(a){return!!l.$$slots[a]});v&&(t=ag(I,g,P,v,M,e,u));u&&(aa.$$addScopeInfo(I,M,!0,!(H&&(H===u||H===u.$$originalDirective))),aa.$$addScopeClass(I,!0),M.$$isolateBindings=u.$$isolateBindings,E=ka(e,g,M,M.$$isolateBindings,u),E.removeWatches&&M.$on("$destroy",E.removeWatches));
|
||||
for(n in t){E=v[n];x=t[n];var Zb=E.$$bindings.bindToController;x.bindingInfo=x.identifier&&Zb?ka(B,g,x.instance,Zb,E):{};var K=x();K!==x.instance&&(x.instance=K,I.data("$"+E.name+"Controller",K),x.bindingInfo.removeWatches&&x.bindingInfo.removeWatches(),x.bindingInfo=ka(B,g,x.instance,Zb,E))}q(v,function(a,b){var c=a.require;a.bindToController&&!L(c)&&D(c)&&S(t[b].instance,ib(b,c,I,t))});q(t,function(a){var b=a.instance;if(z(b.$onChanges))try{b.$onChanges(a.bindingInfo.initialChanges)}catch(d){c(d)}if(z(b.$onInit))try{b.$onInit()}catch(e){c(e)}z(b.$doCheck)&&
|
||||
(B.$watch(function(){b.$doCheck()}),b.$doCheck());z(b.$onDestroy)&&B.$on("$destroy",function(){b.$onDestroy()})});n=0;for(E=h.length;n<E;n++)x=h[n],la(x,x.isolateScope?M:e,I,g,x.require&&ib(x.directiveName,x.require,I,t),P);var s=e;u&&(u.template||null===u.templateUrl)&&(s=M);a&&a(s,f.childNodes,void 0,l);for(n=k.length-1;0<=n;n--)x=k[n],la(x,x.isolateScope?M:e,I,g,x.require&&ib(x.directiveName,x.require,I,t),P);q(t,function(a){a=a.instance;z(a.$postLink)&&a.$postLink()})}l=l||{};for(var n=-Number.MAX_VALUE,
|
||||
r=l.newScopeDirective,v=l.controllerDirectives,u=l.newIsolateScopeDirective,H=l.templateDirective,E=l.nonTlbTranscludeDirective,M=!1,B=!1,fa=l.hasElementTranscludeDirective,t=d.$$element=F(b),x,I,P,K=e,s,Fa=!1,za=!1,w,A=0,C=a.length;A<C;A++){x=a[A];var G=x.$$start,hb=x.$$end;G&&(t=N(b,G,hb));P=void 0;if(n>x.priority)break;if(w=x.scope)x.templateUrl||(D(w)?(X("new/isolated scope",u||r,x,t),u=x):X("new/isolated scope",u,x,t)),r=r||x;I=x.name;if(!Fa&&(x.replace&&(x.templateUrl||x.template)||x.transclude&&
|
||||
!x.$$tlb)){for(w=A+1;Fa=a[w++];)if(Fa.transclude&&!Fa.$$tlb||Fa.replace&&(Fa.templateUrl||Fa.template)){za=!0;break}Fa=!0}!x.templateUrl&&x.controller&&(w=x.controller,v=v||U(),X("'"+I+"' controller",v[I],x,t),v[I]=x);if(w=x.transclude)if(M=!0,x.$$tlb||(X("transclusion",E,x,t),E=x),"element"==w)fa=!0,n=x.priority,P=t,t=d.$$element=F(aa.$$createComment(I,d[I])),b=t[0],ea(f,va.call(P,0),b),P[0].$$parentNode=P[0].parentNode,K=ac(za,P,e,n,g&&g.name,{nonTlbTranscludeDirective:E});else{var oa=U();P=F(Xb(b)).contents();
|
||||
if(D(w)){P=[];var Q=U(),O=U();q(w,function(a,b){var c="?"===a.charAt(0);a=c?a.substring(1):a;Q[a]=b;oa[b]=null;O[b]=c});q(t.contents(),function(a){var b=Q[Aa(wa(a))];b?(O[b]=!0,oa[b]=oa[b]||[],oa[b].push(a)):P.push(a)});q(O,function(a,b){if(!a)throw ga("reqslot",b);});for(var V in oa)oa[V]&&(oa[V]=ac(za,oa[V],e))}t.empty();K=ac(za,P,e,void 0,void 0,{needsNewScope:x.$$isolateScope||x.$$newScope});K.$$slots=oa}if(x.template)if(B=!0,X("template",H,x,t),H=x,w=z(x.template)?x.template(t,d):x.template,
|
||||
w=xa(w),x.replace){g=x;P=Vb.test(w)?$c(da(x.templateNamespace,W(w))):[];b=P[0];if(1!=P.length||1!==b.nodeType)throw ga("tplrt",I,"");ea(f,t,b);C={$attr:{}};w=$b(b,[],C);var Z=a.splice(A+1,a.length-(A+1));(u||r)&&T(w,u,r);a=a.concat(w).concat(Z);$(d,C);C=a.length}else t.html(w);if(x.templateUrl)B=!0,X("template",H,x,t),H=x,x.replace&&(g=x),p=ba(a.splice(A,a.length-A),t,d,f,M&&K,h,k,{controllerDirectives:v,newScopeDirective:r!==x&&r,newIsolateScopeDirective:u,templateDirective:H,nonTlbTranscludeDirective:E}),
|
||||
C=a.length;else if(x.compile)try{s=x.compile(t,d,K);var Y=x.$$originalDirective||x;z(s)?m(null,ab(Y,s),G,hb):s&&m(ab(Y,s.pre),ab(Y,s.post),G,hb)}catch(ca){c(ca,ya(t))}x.terminal&&(p.terminal=!0,n=Math.max(n,x.priority))}p.scope=r&&!0===r.scope;p.transcludeOnThisElement=M;p.templateOnThisElement=B;p.transclude=K;l.hasElementTranscludeDirective=fa;return p}function ib(a,b,c,d){var e;if(G(b)){var f=b.match(l);b=b.substring(f[0].length);var g=f[1]||f[3],f="?"===f[2];"^^"===g?c=c.parent():e=(e=d&&d[b])&&
|
||||
e.instance;if(!e){var h="$"+b+"Controller";e=g?c.inheritedData(h):c.data(h)}if(!e&&!f)throw ga("ctreq",b,a);}else if(L(b))for(e=[],g=0,f=b.length;g<f;g++)e[g]=ib(a,b[g],c,d);else D(b)&&(e={},q(b,function(b,f){e[f]=ib(a,b,c,d)}));return e||null}function ag(a,b,c,d,e,f,g){var h=U(),k;for(k in d){var l=d[k],m={$scope:l===g||l.$$isolateScope?e:f,$element:a,$attrs:b,$transclude:c},p=l.controller;"@"==p&&(p=b[l.name]);m=t(p,m,!0,l.controllerAs);h[l.name]=m;a.data("$"+l.name+"Controller",m.instance)}return h}
|
||||
function T(a,b,c){for(var d=0,e=a.length;d<e;d++)a[d]=Rb(a[d],{$$isolateScope:b,$$newScope:c})}function O(b,e,g,h,k,l,m){if(e===k)return null;k=null;if(f.hasOwnProperty(e)){var p;e=a.get(e+"Directive");for(var n=0,r=e.length;n<r;n++)try{if(p=e[n],(y(h)||h>p.priority)&&-1!=p.restrict.indexOf(g)){l&&(p=Rb(p,{$$start:l,$$end:m}));if(!p.$$bindings){var u=p,v=p,x=p.name,H={isolateScope:null,bindToController:null};D(v.scope)&&(!0===v.bindToController?(H.bindToController=d(v.scope,x,!0),H.isolateScope={}):
|
||||
H.isolateScope=d(v.scope,x,!1));D(v.bindToController)&&(H.bindToController=d(v.bindToController,x,!0));if(D(H.bindToController)){var E=v.controller,M=v.controllerAs;if(!E)throw ga("noctrl",x);if(!Xc(E,M))throw ga("noident",x);}var t=u.$$bindings=H;D(t.isolateScope)&&(p.$$isolateBindings=t.isolateScope)}b.push(p);k=p}}catch(I){c(I)}}return k}function V(b){if(f.hasOwnProperty(b))for(var c=a.get(b+"Directive"),d=0,e=c.length;d<e;d++)if(b=c[d],b.multiElement)return!0;return!1}function $(a,b){var c=b.$attr,
|
||||
d=a.$attr;q(a,function(d,e){"$"!=e.charAt(0)&&(b[e]&&b[e]!==d&&(d+=("style"===e?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});q(b,function(b,e){a.hasOwnProperty(e)||"$"===e.charAt(0)||(a[e]=b,"class"!==e&&"style"!==e&&(d[e]=c[e]))})}function ba(a,b,c,d,f,g,h,k){var l=[],m,p,n=b[0],r=a.shift(),u=Rb(r,{templateUrl:null,transclude:null,replace:null,$$originalDirective:r}),H=z(r.templateUrl)?r.templateUrl(b,c):r.templateUrl,E=r.templateNamespace;b.empty();e(H).then(function(e){var v,M;e=xa(e);if(r.replace){e=
|
||||
Vb.test(e)?$c(da(E,W(e))):[];v=e[0];if(1!=e.length||1!==v.nodeType)throw ga("tplrt",r.name,H);e={$attr:{}};ea(d,b,v);var B=$b(v,[],e);D(r.scope)&&T(B,!0);a=B.concat(a);$(c,e)}else v=n,b.html(e);a.unshift(u);m=oa(a,v,c,f,b,r,g,h,k);q(d,function(a,c){a==v&&(d[c]=b[0])});for(p=s(b[0].childNodes,f);l.length;){e=l.shift();M=l.shift();var t=l.shift(),I=l.shift(),B=b[0];if(!e.$$destroyed){if(M!==n){var P=M.className;k.hasElementTranscludeDirective&&r.replace||(B=Xb(v));ea(t,F(M),B);x(F(B),P)}M=m.transcludeOnThisElement?
|
||||
za(e,m.transclude,I):I;m(p,e,B,d,M)}}l=null});return function(a,b,c,d,e){a=e;b.$$destroyed||(l?l.push(b,c,d,a):(m.transcludeOnThisElement&&(a=za(b,m.transclude,e)),m(p,b,c,d,a)))}}function Z(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 X(a,b,c,d){function e(a){return a?" (module: "+a+")":""}if(b)throw ga("multidir",b.name,e(b.$$moduleName),c.name,e(c.$$moduleName),a,ya(d));}function ca(a,c){var d=b(c,!0);d&&a.push({priority:0,compile:function(a){a=
|
||||
a.parent();var b=!!a.length;b&&aa.$$addBindingClass(a);return function(a,c){var e=c.parent();b||aa.$$addBindingClass(e);aa.$$addBindingInfo(e,d.expressions);a.$watch(d,function(a){c[0].nodeValue=a})}}})}function da(a,b){a=Q(a||"html");switch(a){case "svg":case "math":var c=C.document.createElement("div");c.innerHTML="<"+a+">"+b+"</"+a+">";return c.childNodes[0].childNodes;default:return b}}function ha(a,b){if("srcdoc"==b)return M.HTML;var c=wa(a);if("xlinkHref"==b||"form"==c&&"action"==b||"img"!=
|
||||
c&&("src"==b||"ngSrc"==b))return M.RESOURCE_URL}function ia(a,c,d,e,f){var g=ha(a,e);f=k[e]||f;var h=b(d,!0,g,f);if(h){if("multiple"===e&&"select"===wa(a))throw ga("selmulti",ya(a));c.push({priority:100,compile:function(){return{pre:function(a,c,k){c=k.$$observers||(k.$$observers=U());if(m.test(e))throw ga("nodomevents");var l=k[e];l!==d&&(h=l&&b(l,!0,g,f),d=l);h&&(k[e]=h(a),(c[e]||(c[e]=[])).$$inter=!0,(k.$$observers&&k.$$observers[e].$$scope||a).$watch(h,function(a,b){"class"===e&&a!=b?k.$updateClass(a,
|
||||
b):k.$set(e,a)}))}}}})}}function ea(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=C.document.createDocumentFragment();for(g=0;g<e;g++)a.appendChild(b[g]);F.hasData(d)&&(F.data(c,F.data(d)),F(d).off("$destroy"));F.cleanData(a.querySelectorAll("*"));for(g=1;g<e;g++)delete b[g];b[0]=c;b.length=1}function ja(a,
|
||||
b){return S(function(){return a.apply(null,arguments)},a,b)}function la(a,b,d,e,f,g){try{a(b,d,e,f,g)}catch(h){c(h,ya(d))}}function ka(a,c,d,e,f){function g(b,c,e){z(d.$onChanges)&&c!==e&&(Y||(a.$$postDigest(I),Y=[]),m||(m={},Y.push(h)),m[b]&&(e=m[b].previousValue),m[b]=new Fb(e,c))}function h(){d.$onChanges(m);m=void 0}var k=[],l={},m;q(e,function(e,h){var m=e.attrName,p=e.optional,v,u,x,H;switch(e.mode){case "@":p||ua.call(c,m)||(d[h]=c[m]=void 0);c.$observe(m,function(a){if(G(a)||Ga(a))g(h,a,d[h]),
|
||||
d[h]=a});c.$$observers[m].$$scope=a;v=c[m];G(v)?d[h]=b(v)(a):Ga(v)&&(d[h]=v);l[h]=new Fb(bc,d[h]);break;case "=":if(!ua.call(c,m)){if(p)break;c[m]=void 0}if(p&&!c[m])break;u=n(c[m]);H=u.literal?na:function(a,b){return a===b||a!==a&&b!==b};x=u.assign||function(){v=d[h]=u(a);throw ga("nonassign",c[m],m,f.name);};v=d[h]=u(a);p=function(b){H(b,d[h])||(H(b,v)?x(a,b=d[h]):d[h]=b);return v=b};p.$stateful=!0;p=e.collection?a.$watchCollection(c[m],p):a.$watch(n(c[m],p),null,u.literal);k.push(p);break;case "<":if(!ua.call(c,
|
||||
m)){if(p)break;c[m]=void 0}if(p&&!c[m])break;u=n(c[m]);var E=d[h]=u(a);l[h]=new Fb(bc,d[h]);p=a.$watch(u,function(a,b){if(b===a){if(b===E)return;b=E}g(h,a,b);d[h]=a},u.literal);k.push(p);break;case "&":u=c.hasOwnProperty(m)?n(c[m]):A;if(u===A&&p)break;d[h]=function(b){return u(a,b)}}});return{initialChanges:l,removeWatches:k.length&&function(){for(var a=0,b=k.length;a<b;++a)k[a]()}}}var ta=/^\w/,pa=C.document.createElement("div"),qa=u,Y;Da.prototype={$normalize:Aa,$addClass:function(a){a&&0<a.length&&
|
||||
H.addClass(this.$$element,a)},$removeClass:function(a){a&&0<a.length&&H.removeClass(this.$$element,a)},$updateClass:function(a,b){var c=ad(a,b);c&&c.length&&H.addClass(this.$$element,c);(c=ad(b,a))&&c.length&&H.removeClass(this.$$element,c)},$set:function(a,b,d,e){var f=Uc(this.$$element[0],a),g=bd[a],h=a;f?(this.$$element.prop(a,b),e=f):g&&(this[g]=b,h=g);this[a]=b;e?this.$attr[a]=e:(e=this.$attr[a])||(this.$attr[a]=e=Cc(a,"-"));f=wa(this.$$element);if("a"===f&&("href"===a||"xlinkHref"===a)||"img"===
|
||||
f&&"src"===a)this[a]=b=E(b,"src"===a);else if("img"===f&&"srcset"===a&&w(b)){for(var f="",g=W(b),k=/(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/,k=/\s/.test(g)?k:/(,)/,g=g.split(k),k=Math.floor(g.length/2),l=0;l<k;l++)var m=2*l,f=f+E(W(g[m]),!0),f=f+(" "+W(g[m+1]));g=W(g[2*l]).split(/\s/);f+=E(W(g[0]),!0);2===g.length&&(f+=" "+W(g[1]));this[a]=b=f}!1!==d&&(null===b||y(b)?this.$$element.removeAttr(e):ta.test(e)?this.$$element.attr(e,b):P(this.$$element[0],e,b));(a=this.$$observers)&&q(a[h],function(a){try{a(b)}catch(d){c(d)}})},
|
||||
$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers=U()),e=d[a]||(d[a]=[]);e.push(b);K.$evalAsync(function(){e.$$inter||!c.hasOwnProperty(a)||y(c[a])||b(c[a])});return function(){Za(e,b)}}};var ra=b.startSymbol(),sa=b.endSymbol(),xa="{{"==ra&&"}}"==sa?Xa:function(a){return a.replace(/\{\{/g,ra).replace(/}}/g,sa)},Ba=/^ngAttr[A-Z]/,Ca=/^(.+)Start$/;aa.$$addBindingInfo=p?function(a,b){var c=a.data("$binding")||[];L(b)?c=c.concat(b):c.push(b);a.data("$binding",c)}:A;aa.$$addBindingClass=
|
||||
p?function(a){x(a,"ng-binding")}:A;aa.$$addScopeInfo=p?function(a,b,c,d){a.data(c?d?"$isolateScopeNoTemplate":"$isolateScope":"$scope",b)}:A;aa.$$addScopeClass=p?function(a,b){x(a,b?"ng-isolate-scope":"ng-scope")}:A;aa.$$createComment=function(a,b){var c="";p&&(c=" "+(a||"")+": ",b&&(c+=b+" "));return C.document.createComment(c)};return aa}]}function Fb(a,b){this.previousValue=a;this.currentValue=b}function Aa(a){return db(a.replace(Yc,""))}function ad(a,b){var d="",c=a.split(/\s+/),e=b.split(/\s+/),
|
||||
f=0;a:for(;f<c.length;f++){for(var g=c[f],h=0;h<e.length;h++)if(g==e[h])continue a;d+=(0<d.length?" ":"")+g}return d}function $c(a){a=F(a);var b=a.length;if(1>=b)return a;for(;b--;)8===a[b].nodeType&&bg.call(a,b,1);return a}function Xc(a,b){if(b&&G(b))return b;if(G(a)){var d=cd.exec(a);if(d)return d[3]}}function ff(){var a={},b=!1;this.has=function(b){return a.hasOwnProperty(b)};this.register=function(b,c){Qa(b,"controller");D(b)?S(a,b):a[b]=c};this.allowGlobals=function(){b=!0};this.$get=["$injector",
|
||||
"$window",function(d,c){function e(a,b,c,d){if(!a||!D(a.$scope))throw N("$controller")("noscp",d,b);a.$scope[b]=c}return function(f,g,h,k){var l,m,n;h=!0===h;k&&G(k)&&(n=k);if(G(f)){k=f.match(cd);if(!k)throw cg("ctrlfmt",f);m=k[1];n=n||k[3];f=a.hasOwnProperty(m)?a[m]:Ec(g.$scope,m,!0)||(b?Ec(c,m,!0):void 0);Pa(f,m,!0)}if(h)return h=(L(f)?f[f.length-1]:f).prototype,l=Object.create(h||null),n&&e(g,n,l,m||f.name),S(function(){var a=d.invoke(f,l,g,m);a!==l&&(D(a)||z(a))&&(l=a,n&&e(g,n,l,m||f.name));return l},
|
||||
{instance:l,identifier:n});l=d.instantiate(f,g,m);n&&e(g,n,l,m||f.name);return l}}]}function gf(){this.$get=["$window",function(a){return F(a.document)}]}function hf(){this.$get=["$log",function(a){return function(b,d){a.error.apply(a,arguments)}}]}function cc(a){return D(a)?da(a)?a.toISOString():bb(a):a}function nf(){this.$get=function(){return function(a){if(!a)return"";var b=[];tc(a,function(a,c){null===a||y(a)||(L(a)?q(a,function(a){b.push(ea(c)+"="+ea(cc(a)))}):b.push(ea(c)+"="+ea(cc(a))))});
|
||||
return b.join("&")}}}function of(){this.$get=function(){return function(a){function b(a,e,f){null===a||y(a)||(L(a)?q(a,function(a,c){b(a,e+"["+(D(a)?c:"")+"]")}):D(a)&&!da(a)?tc(a,function(a,c){b(a,e+(f?"":"[")+c+(f?"":"]"))}):d.push(ea(e)+"="+ea(cc(a))))}if(!a)return"";var d=[];b(a,"",!0);return d.join("&")}}}function dc(a,b){if(G(a)){var d=a.replace(dg,"").trim();if(d){var c=b("Content-Type");(c=c&&0===c.indexOf(dd))||(c=(c=d.match(eg))&&fg[c[0]].test(d));c&&(a=xc(d))}}return a}function ed(a){var b=
|
||||
U(),d;G(a)?q(a.split("\n"),function(a){d=a.indexOf(":");var e=Q(W(a.substr(0,d)));a=W(a.substr(d+1));e&&(b[e]=b[e]?b[e]+", "+a:a)}):D(a)&&q(a,function(a,d){var f=Q(d),g=W(a);f&&(b[f]=b[f]?b[f]+", "+g:g)});return b}function fd(a){var b;return function(d){b||(b=ed(a));return d?(d=b[Q(d)],void 0===d&&(d=null),d):b}}function gd(a,b,d,c){if(z(c))return c(a,b,d);q(c,function(c){a=c(a,b,d)});return a}function mf(){var a=this.defaults={transformResponse:[dc],transformRequest:[function(a){return D(a)&&"[object File]"!==
|
||||
ma.call(a)&&"[object Blob]"!==ma.call(a)&&"[object FormData]"!==ma.call(a)?bb(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},post:ia(ec),put:ia(ec),patch:ia(ec)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",paramSerializer:"$httpParamSerializer"},b=!1;this.useApplyAsync=function(a){return w(a)?(b=!!a,this):b};var d=!0;this.useLegacyPromiseExtensions=function(a){return w(a)?(d=!!a,this):d};var c=this.interceptors=[];this.$get=["$httpBackend","$$cookieReader","$cacheFactory",
|
||||
"$rootScope","$q","$injector",function(e,f,g,h,k,l){function m(b){function c(a,b){for(var d=0,e=b.length;d<e;){var f=b[d++],g=b[d++];a=a.then(f,g)}b.length=0;return a}function e(a,b){var c,d={};q(a,function(a,e){z(a)?(c=a(b),null!=c&&(d[e]=c)):d[e]=a});return d}function f(a){var b=S({},a);b.data=gd(a.data,a.headers,a.status,g.transformResponse);a=a.status;return 200<=a&&300>a?b:k.reject(b)}if(!D(b))throw N("$http")("badreq",b);if(!G(b.url))throw N("$http")("badreq",b.url);var g=S({method:"get",transformRequest:a.transformRequest,
|
||||
transformResponse:a.transformResponse,paramSerializer:a.paramSerializer},b);g.headers=function(b){var c=a.headers,d=S({},b.headers),f,g,h,c=S({},c.common,c[Q(b.method)]);a:for(f in c){g=Q(f);for(h in d)if(Q(h)===g)continue a;d[f]=c[f]}return e(d,ia(b))}(b);g.method=ub(g.method);g.paramSerializer=G(g.paramSerializer)?l.get(g.paramSerializer):g.paramSerializer;var h=[],m=[],p=k.when(g);q(R,function(a){(a.request||a.requestError)&&h.unshift(a.request,a.requestError);(a.response||a.responseError)&&m.push(a.response,
|
||||
a.responseError)});p=c(p,h);p=p.then(function(b){var c=b.headers,d=gd(b.data,fd(c),void 0,b.transformRequest);y(d)&&q(c,function(a,b){"content-type"===Q(b)&&delete c[b]});y(b.withCredentials)&&!y(a.withCredentials)&&(b.withCredentials=a.withCredentials);return n(b,d).then(f,f)});p=c(p,m);d?(p.success=function(a){Pa(a,"fn");p.then(function(b){a(b.data,b.status,b.headers,g)});return p},p.error=function(a){Pa(a,"fn");p.then(null,function(b){a(b.data,b.status,b.headers,g)});return p}):(p.success=hd("success"),
|
||||
p.error=hd("error"));return p}function n(c,d){function g(a){if(a){var c={};q(a,function(a,d){c[d]=function(c){function d(){a(c)}b?h.$applyAsync(d):h.$$phase?d():h.$apply(d)}});return c}}function l(a,c,d,e){function f(){n(c,a,d,e)}E&&(200<=a&&300>a?E.put(P,[a,c,ed(d),e]):E.remove(P));b?h.$applyAsync(f):(f(),h.$$phase||h.$apply())}function n(a,b,d,e){b=-1<=b?b:0;(200<=b&&300>b?M.resolve:M.reject)({data:a,status:b,headers:fd(d),config:c,statusText:e})}function t(a){n(a.data,a.status,ia(a.headers()),
|
||||
a.statusText)}function R(){var a=m.pendingRequests.indexOf(c);-1!==a&&m.pendingRequests.splice(a,1)}var M=k.defer(),H=M.promise,E,I,Da=c.headers,P=p(c.url,c.paramSerializer(c.params));m.pendingRequests.push(c);H.then(R,R);!c.cache&&!a.cache||!1===c.cache||"GET"!==c.method&&"JSONP"!==c.method||(E=D(c.cache)?c.cache:D(a.cache)?a.cache:u);E&&(I=E.get(P),w(I)?I&&z(I.then)?I.then(t,t):L(I)?n(I[1],I[0],ia(I[2]),I[3]):n(I,200,{},"OK"):E.put(P,H));y(I)&&((I=id(c.url)?f()[c.xsrfCookieName||a.xsrfCookieName]:
|
||||
void 0)&&(Da[c.xsrfHeaderName||a.xsrfHeaderName]=I),e(c.method,P,d,l,Da,c.timeout,c.withCredentials,c.responseType,g(c.eventHandlers),g(c.uploadEventHandlers)));return H}function p(a,b){0<b.length&&(a+=(-1==a.indexOf("?")?"?":"&")+b);return a}var u=g("$http");a.paramSerializer=G(a.paramSerializer)?l.get(a.paramSerializer):a.paramSerializer;var R=[];q(c,function(a){R.unshift(G(a)?l.get(a):l.invoke(a))});m.pendingRequests=[];(function(a){q(arguments,function(a){m[a]=function(b,c){return m(S({},c||{},
|
||||
{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){q(arguments,function(a){m[a]=function(b,c,d){return m(S({},d||{},{method:a,url:b,data:c}))}})})("post","put","patch");m.defaults=a;return m}]}function qf(){this.$get=function(){return function(){return new C.XMLHttpRequest}}}function pf(){this.$get=["$browser","$jsonpCallbacks","$document","$xhrFactory",function(a,b,d,c){return gg(a,c,a.defer,b,d[0])}]}function gg(a,b,d,c,e){function f(a,b,d){a=a.replace("JSON_CALLBACK",b);var f=
|
||||
e.createElement("script"),m=null;f.type="text/javascript";f.src=a;f.async=!0;m=function(a){f.removeEventListener("load",m,!1);f.removeEventListener("error",m,!1);e.body.removeChild(f);f=null;var g=-1,u="unknown";a&&("load"!==a.type||c.wasCalled(b)||(a={type:"error"}),u=a.type,g="error"===a.type?404:200);d&&d(g,u)};f.addEventListener("load",m,!1);f.addEventListener("error",m,!1);e.body.appendChild(f);return m}return function(e,h,k,l,m,n,p,u,R,B){function r(){fa&&fa();t&&t.abort()}function J(b,c,e,
|
||||
f,g){w(M)&&d.cancel(M);fa=t=null;b(c,e,f,g);a.$$completeOutstandingRequest(A)}a.$$incOutstandingRequestCount();h=h||a.url();if("jsonp"===Q(e))var v=c.createCallback(h),fa=f(h,v,function(a,b){var d=200===a&&c.getResponse(v);J(l,a,d,"",b);c.removeCallback(v)});else{var t=b(e,h);t.open(e,h,!0);q(m,function(a,b){w(a)&&t.setRequestHeader(b,a)});t.onload=function(){var a=t.statusText||"",b="response"in t?t.response:t.responseText,c=1223===t.status?204:t.status;0===c&&(c=b?200:"file"==Y(h).protocol?404:
|
||||
0);J(l,c,b,t.getAllResponseHeaders(),a)};e=function(){J(l,-1,null,null,"")};t.onerror=e;t.onabort=e;q(R,function(a,b){t.addEventListener(b,a)});q(B,function(a,b){t.upload.addEventListener(b,a)});p&&(t.withCredentials=!0);if(u)try{t.responseType=u}catch(K){if("json"!==u)throw K;}t.send(y(k)?null:k)}if(0<n)var M=d(r,n);else n&&z(n.then)&&n.then(r)}}function kf(){var a="{{",b="}}";this.startSymbol=function(b){return b?(a=b,this):a};this.endSymbol=function(a){return a?(b=a,this):b};this.$get=["$parse",
|
||||
"$exceptionHandler","$sce",function(d,c,e){function f(a){return"\\\\\\"+a}function g(c){return c.replace(n,a).replace(p,b)}function h(a,b,c,d){var e;return e=a.$watch(function(a){e();return d(a)},b,c)}function k(f,k,p,n){function J(a){try{var b=a;a=p?e.getTrusted(p,b):e.valueOf(b);var d;if(n&&!w(a))d=a;else if(null==a)d="";else{switch(typeof a){case "string":break;case "number":a=""+a;break;default:a=bb(a)}d=a}return d}catch(g){c(Ka.interr(f,g))}}if(!f.length||-1===f.indexOf(a)){var v;k||(k=g(f),
|
||||
v=ha(k),v.exp=f,v.expressions=[],v.$$watchDelegate=h);return v}n=!!n;var q,t,K=0,M=[],H=[];v=f.length;for(var E=[],I=[];K<v;)if(-1!=(q=f.indexOf(a,K))&&-1!=(t=f.indexOf(b,q+l)))K!==q&&E.push(g(f.substring(K,q))),K=f.substring(q+l,t),M.push(K),H.push(d(K,J)),K=t+m,I.push(E.length),E.push("");else{K!==v&&E.push(g(f.substring(K)));break}p&&1<E.length&&Ka.throwNoconcat(f);if(!k||M.length){var Da=function(a){for(var b=0,c=M.length;b<c;b++){if(n&&y(a[b]))return;E[I[b]]=a[b]}return E.join("")};return S(function(a){var b=
|
||||
0,d=M.length,e=Array(d);try{for(;b<d;b++)e[b]=H[b](a);return Da(e)}catch(g){c(Ka.interr(f,g))}},{exp:f,expressions:M,$$watchDelegate:function(a,b){var c;return a.$watchGroup(H,function(d,e){var f=Da(d);z(b)&&b.call(this,f,d!==e?c:f,a);c=f})}})}}var l=a.length,m=b.length,n=new RegExp(a.replace(/./g,f),"g"),p=new RegExp(b.replace(/./g,f),"g");k.startSymbol=function(){return a};k.endSymbol=function(){return b};return k}]}function lf(){this.$get=["$rootScope","$window","$q","$$q","$browser",function(a,
|
||||
b,d,c,e){function f(f,k,l,m){function n(){p?f.apply(null,u):f(r)}var p=4<arguments.length,u=p?va.call(arguments,4):[],R=b.setInterval,q=b.clearInterval,r=0,J=w(m)&&!m,v=(J?c:d).defer(),fa=v.promise;l=w(l)?l:0;fa.$$intervalId=R(function(){J?e.defer(n):a.$evalAsync(n);v.notify(r++);0<l&&r>=l&&(v.resolve(r),q(fa.$$intervalId),delete g[fa.$$intervalId]);J||a.$apply()},k);g[fa.$$intervalId]=v;return fa}var g={};f.cancel=function(a){return a&&a.$$intervalId in g?(g[a.$$intervalId].reject("canceled"),b.clearInterval(a.$$intervalId),
|
||||
delete g[a.$$intervalId],!0):!1};return f}]}function fc(a){a=a.split("/");for(var b=a.length;b--;)a[b]=qb(a[b]);return a.join("/")}function jd(a,b){var d=Y(a);b.$$protocol=d.protocol;b.$$host=d.hostname;b.$$port=Z(d.port)||hg[d.protocol]||null}function kd(a,b){var d="/"!==a.charAt(0);d&&(a="/"+a);var c=Y(a);b.$$path=decodeURIComponent(d&&"/"===c.pathname.charAt(0)?c.pathname.substring(1):c.pathname);b.$$search=Ac(c.search);b.$$hash=decodeURIComponent(c.hash);b.$$path&&"/"!=b.$$path.charAt(0)&&(b.$$path=
|
||||
"/"+b.$$path)}function ka(a,b){if(0===b.lastIndexOf(a,0))return b.substr(a.length)}function Ja(a){var b=a.indexOf("#");return-1==b?a:a.substr(0,b)}function jb(a){return a.replace(/(#.+)|#$/,"$1")}function gc(a,b,d){this.$$html5=!0;d=d||"";jd(a,this);this.$$parse=function(a){var d=ka(b,a);if(!G(d))throw Gb("ipthprfx",a,b);kd(d,this);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=Tb(this.$$search),d=this.$$hash?"#"+qb(this.$$hash):"";this.$$url=fc(this.$$path)+(a?"?"+
|
||||
a:"")+d;this.$$absUrl=b+this.$$url.substr(1)};this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;w(f=ka(a,c))?(g=f,g=w(f=ka(d,f))?b+(ka("/",f)||f):a+g):w(f=ka(b,c))?g=b+f:b==c+"/"&&(g=b);g&&this.$$parse(g);return!!g}}function hc(a,b,d){jd(a,this);this.$$parse=function(c){var e=ka(a,c)||ka(b,c),f;y(e)||"#"!==e.charAt(0)?this.$$html5?f=e:(f="",y(e)&&(a=c,this.replace())):(f=ka(d,e),y(f)&&(f=e));kd(f,this);c=this.$$path;var e=a,g=/^\/[A-Z]:(\/.*)/;0===f.lastIndexOf(e,
|
||||
0)&&(f=f.replace(e,""));g.exec(f)||(c=(f=g.exec(c))?f[1]:c);this.$$path=c;this.$$compose()};this.$$compose=function(){var b=Tb(this.$$search),e=this.$$hash?"#"+qb(this.$$hash):"";this.$$url=fc(this.$$path)+(b?"?"+b:"")+e;this.$$absUrl=a+(this.$$url?d+this.$$url:"")};this.$$parseLinkUrl=function(b,d){return Ja(a)==Ja(b)?(this.$$parse(b),!0):!1}}function ld(a,b,d){this.$$html5=!0;hc.apply(this,arguments);this.$$parseLinkUrl=function(c,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;a==Ja(c)?
|
||||
f=c:(g=ka(b,c))?f=a+d+g:b===c+"/"&&(f=b);f&&this.$$parse(f);return!!f};this.$$compose=function(){var b=Tb(this.$$search),e=this.$$hash?"#"+qb(this.$$hash):"";this.$$url=fc(this.$$path)+(b?"?"+b:"")+e;this.$$absUrl=a+d+this.$$url}}function Hb(a){return function(){return this[a]}}function md(a,b){return function(d){if(y(d))return this[a];this[a]=b(d);this.$$compose();return this}}function sf(){var a="",b={enabled:!1,requireBase:!0,rewriteLinks:!0};this.hashPrefix=function(b){return w(b)?(a=b,this):
|
||||
a};this.html5Mode=function(a){return Ga(a)?(b.enabled=a,this):D(a)?(Ga(a.enabled)&&(b.enabled=a.enabled),Ga(a.requireBase)&&(b.requireBase=a.requireBase),Ga(a.rewriteLinks)&&(b.rewriteLinks=a.rewriteLinks),this):b};this.$get=["$rootScope","$browser","$sniffer","$rootElement","$window",function(d,c,e,f,g){function h(a,b,d){var e=l.url(),f=l.$$state;try{c.url(a,b,d),l.$$state=c.state()}catch(g){throw l.url(e),l.$$state=f,g;}}function k(a,b){d.$broadcast("$locationChangeSuccess",l.absUrl(),a,l.$$state,
|
||||
b)}var l,m;m=c.baseHref();var n=c.url(),p;if(b.enabled){if(!m&&b.requireBase)throw Gb("nobase");p=n.substring(0,n.indexOf("/",n.indexOf("//")+2))+(m||"/");m=e.history?gc:ld}else p=Ja(n),m=hc;var u=p.substr(0,Ja(p).lastIndexOf("/")+1);l=new m(p,u,"#"+a);l.$$parseLinkUrl(n,n);l.$$state=c.state();var R=/^\s*(javascript|mailto):/i;f.on("click",function(a){if(b.rewriteLinks&&!a.ctrlKey&&!a.metaKey&&!a.shiftKey&&2!=a.which&&2!=a.button){for(var e=F(a.target);"a"!==wa(e[0]);)if(e[0]===f[0]||!(e=e.parent())[0])return;
|
||||
var h=e.prop("href"),k=e.attr("href")||e.attr("xlink:href");D(h)&&"[object SVGAnimatedString]"===h.toString()&&(h=Y(h.animVal).href);R.test(h)||!h||e.attr("target")||a.isDefaultPrevented()||!l.$$parseLinkUrl(h,k)||(a.preventDefault(),l.absUrl()!=c.url()&&(d.$apply(),g.angular["ff-684208-preventDefault"]=!0))}});jb(l.absUrl())!=jb(n)&&c.url(l.absUrl(),!0);var q=!0;c.onUrlChange(function(a,b){y(ka(u,a))?g.location.href=a:(d.$evalAsync(function(){var c=l.absUrl(),e=l.$$state,f;a=jb(a);l.$$parse(a);l.$$state=
|
||||
b;f=d.$broadcast("$locationChangeStart",a,c,b,e).defaultPrevented;l.absUrl()===a&&(f?(l.$$parse(c),l.$$state=e,h(c,!1,e)):(q=!1,k(c,e)))}),d.$$phase||d.$digest())});d.$watch(function(){var a=jb(c.url()),b=jb(l.absUrl()),f=c.state(),g=l.$$replace,m=a!==b||l.$$html5&&e.history&&f!==l.$$state;if(q||m)q=!1,d.$evalAsync(function(){var b=l.absUrl(),c=d.$broadcast("$locationChangeStart",b,a,l.$$state,f).defaultPrevented;l.absUrl()===b&&(c?(l.$$parse(a),l.$$state=f):(m&&h(b,g,f===l.$$state?null:l.$$state),
|
||||
k(a,f)))});l.$$replace=!1});return l}]}function tf(){var a=!0,b=this;this.debugEnabled=function(b){return w(b)?(a=b,this):a};this.$get=["$window",function(d){function c(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=d.console||{},e=b[a]||b.log||A;a=!1;try{a=!!e.apply}catch(k){}return a?function(){var a=[];q(arguments,function(b){a.push(c(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(){a&&c.apply(b,arguments)}}()}}]}function Sa(a,b){if("__defineGetter__"===a||"__defineSetter__"===a||"__lookupGetter__"===a||"__lookupSetter__"===a||"__proto__"===a)throw X("isecfld",b);return a}function ig(a){return a+""}function ra(a,b){if(a){if(a.constructor===a)throw X("isecfn",b);if(a.window===a)throw X("isecwindow",b);if(a.children&&
|
||||
(a.nodeName||a.prop&&a.attr&&a.find))throw X("isecdom",b);if(a===Object)throw X("isecobj",b);}return a}function nd(a,b){if(a){if(a.constructor===a)throw X("isecfn",b);if(a===jg||a===kg||a===lg)throw X("isecff",b);}}function Ib(a,b){if(a&&(a===(0).constructor||a===(!1).constructor||a==="".constructor||a==={}.constructor||a===[].constructor||a===Function.constructor))throw X("isecaf",b);}function mg(a,b){return"undefined"!==typeof a?a:b}function od(a,b){return"undefined"===typeof a?b:"undefined"===
|
||||
typeof b?a:a+b}function V(a,b){var d,c;switch(a.type){case s.Program:d=!0;q(a.body,function(a){V(a.expression,b);d=d&&a.expression.constant});a.constant=d;break;case s.Literal:a.constant=!0;a.toWatch=[];break;case s.UnaryExpression:V(a.argument,b);a.constant=a.argument.constant;a.toWatch=a.argument.toWatch;break;case s.BinaryExpression:V(a.left,b);V(a.right,b);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.left.toWatch.concat(a.right.toWatch);break;case s.LogicalExpression:V(a.left,b);V(a.right,
|
||||
b);a.constant=a.left.constant&&a.right.constant;a.toWatch=a.constant?[]:[a];break;case s.ConditionalExpression:V(a.test,b);V(a.alternate,b);V(a.consequent,b);a.constant=a.test.constant&&a.alternate.constant&&a.consequent.constant;a.toWatch=a.constant?[]:[a];break;case s.Identifier:a.constant=!1;a.toWatch=[a];break;case s.MemberExpression:V(a.object,b);a.computed&&V(a.property,b);a.constant=a.object.constant&&(!a.computed||a.property.constant);a.toWatch=[a];break;case s.CallExpression:d=a.filter?!b(a.callee.name).$stateful:
|
||||
!1;c=[];q(a.arguments,function(a){V(a,b);d=d&&a.constant;a.constant||c.push.apply(c,a.toWatch)});a.constant=d;a.toWatch=a.filter&&!b(a.callee.name).$stateful?c:[a];break;case s.AssignmentExpression:V(a.left,b);V(a.right,b);a.constant=a.left.constant&&a.right.constant;a.toWatch=[a];break;case s.ArrayExpression:d=!0;c=[];q(a.elements,function(a){V(a,b);d=d&&a.constant;a.constant||c.push.apply(c,a.toWatch)});a.constant=d;a.toWatch=c;break;case s.ObjectExpression:d=!0;c=[];q(a.properties,function(a){V(a.value,
|
||||
b);d=d&&a.value.constant&&!a.computed;a.value.constant||c.push.apply(c,a.value.toWatch)});a.constant=d;a.toWatch=c;break;case s.ThisExpression:a.constant=!1;a.toWatch=[];break;case s.LocalsExpression:a.constant=!1,a.toWatch=[]}}function pd(a){if(1==a.length){a=a[0].expression;var b=a.toWatch;return 1!==b.length?b:b[0]!==a?b:void 0}}function qd(a){return a.type===s.Identifier||a.type===s.MemberExpression}function rd(a){if(1===a.body.length&&qd(a.body[0].expression))return{type:s.AssignmentExpression,
|
||||
left:a.body[0].expression,right:{type:s.NGValueParameter},operator:"="}}function sd(a){return 0===a.body.length||1===a.body.length&&(a.body[0].expression.type===s.Literal||a.body[0].expression.type===s.ArrayExpression||a.body[0].expression.type===s.ObjectExpression)}function td(a,b){this.astBuilder=a;this.$filter=b}function ud(a,b){this.astBuilder=a;this.$filter=b}function Jb(a){return"constructor"==a}function ic(a){return z(a.valueOf)?a.valueOf():ng.call(a)}function uf(){var a=U(),b=U(),d={"true":!0,
|
||||
"false":!1,"null":null,undefined:void 0},c,e;this.addLiteral=function(a,b){d[a]=b};this.setIdentifierFns=function(a,b){c=a;e=b;return this};this.$get=["$filter",function(f){function g(c,d,e){var g,k,H;e=e||J;switch(typeof c){case "string":H=c=c.trim();var E=e?b:a;g=E[H];if(!g){":"===c.charAt(0)&&":"===c.charAt(1)&&(k=!0,c=c.substring(2));g=e?r:B;var q=new jc(g);g=(new kc(q,f,g)).parse(c);g.constant?g.$$watchDelegate=p:k?g.$$watchDelegate=g.literal?n:m:g.inputs&&(g.$$watchDelegate=l);e&&(g=h(g));E[H]=
|
||||
g}return u(g,d);case "function":return u(c,d);default:return u(A,d)}}function h(a){function b(c,d,e,f){var g=J;J=!0;try{return a(c,d,e,f)}finally{J=g}}if(!a)return a;b.$$watchDelegate=a.$$watchDelegate;b.assign=h(a.assign);b.constant=a.constant;b.literal=a.literal;for(var c=0;a.inputs&&c<a.inputs.length;++c)a.inputs[c]=h(a.inputs[c]);b.inputs=a.inputs;return b}function k(a,b){return null==a||null==b?a===b:"object"===typeof a&&(a=ic(a),"object"===typeof a)?!1:a===b||a!==a&&b!==b}function l(a,b,c,d,
|
||||
e){var f=d.inputs,g;if(1===f.length){var h=k,f=f[0];return a.$watch(function(a){var b=f(a);k(b,h)||(g=d(a,void 0,void 0,[b]),h=b&&ic(b));return g},b,c,e)}for(var l=[],m=[],p=0,n=f.length;p<n;p++)l[p]=k,m[p]=null;return a.$watch(function(a){for(var b=!1,c=0,e=f.length;c<e;c++){var h=f[c](a);if(b||(b=!k(h,l[c])))m[c]=h,l[c]=h&&ic(h)}b&&(g=d(a,void 0,void 0,m));return g},b,c,e)}function m(a,b,c,d){var e,f;return e=a.$watch(function(a){return d(a)},function(a,c,d){f=a;z(b)&&b.apply(this,arguments);w(a)&&
|
||||
d.$$postDigest(function(){w(f)&&e()})},c)}function n(a,b,c,d){function e(a){var b=!0;q(a,function(a){w(a)||(b=!1)});return b}var f,g;return f=a.$watch(function(a){return d(a)},function(a,c,d){g=a;z(b)&&b.call(this,a,c,d);e(a)&&d.$$postDigest(function(){e(g)&&f()})},c)}function p(a,b,c,d){var e;return e=a.$watch(function(a){e();return d(a)},b,c)}function u(a,b){if(!b)return a;var c=a.$$watchDelegate,d=!1,c=c!==n&&c!==m?function(c,e,f,g){f=d&&g?g[0]:a(c,e,f,g);return b(f,c,e)}:function(c,d,e,f){e=a(c,
|
||||
d,e,f);c=b(e,c,d);return w(e)?c:e};a.$$watchDelegate&&a.$$watchDelegate!==l?c.$$watchDelegate=a.$$watchDelegate:b.$stateful||(c.$$watchDelegate=l,d=!a.inputs,c.inputs=a.inputs?a.inputs:[a]);return c}var R=Ba().noUnsafeEval,B={csp:R,expensiveChecks:!1,literals:pa(d),isIdentifierStart:z(c)&&c,isIdentifierContinue:z(e)&&e},r={csp:R,expensiveChecks:!0,literals:pa(d),isIdentifierStart:z(c)&&c,isIdentifierContinue:z(e)&&e},J=!1;g.$$runningExpensiveChecks=function(){return J};return g}]}function wf(){this.$get=
|
||||
["$rootScope","$exceptionHandler",function(a,b){return vd(function(b){a.$evalAsync(b)},b)}]}function xf(){this.$get=["$browser","$exceptionHandler",function(a,b){return vd(function(b){a.defer(b)},b)}]}function vd(a,b){function d(){this.$$state={status:0}}function c(a,b){return function(c){b.call(a,c)}}function e(c){!c.processScheduled&&c.pending&&(c.processScheduled=!0,a(function(){var a,d,e;e=c.pending;c.processScheduled=!1;c.pending=void 0;for(var f=0,g=e.length;f<g;++f){d=e[f][0];a=e[f][c.status];
|
||||
try{z(a)?d.resolve(a(c.value)):1===c.status?d.resolve(c.value):d.reject(c.value)}catch(h){d.reject(h),b(h)}}}))}function f(){this.promise=new d}var g=N("$q",TypeError),h=function(){var a=new f;a.resolve=c(a,a.resolve);a.reject=c(a,a.reject);a.notify=c(a,a.notify);return a};S(d.prototype,{then:function(a,b,c){if(y(a)&&y(b)&&y(c))return this;var d=new f;this.$$state.pending=this.$$state.pending||[];this.$$state.pending.push([d,a,b,c]);0<this.$$state.status&&e(this.$$state);return d.promise},"catch":function(a){return this.then(null,
|
||||
a)},"finally":function(a,b){return this.then(function(b){return l(b,!0,a)},function(b){return l(b,!1,a)},b)}});S(f.prototype,{resolve:function(a){this.promise.$$state.status||(a===this.promise?this.$$reject(g("qcycle",a)):this.$$resolve(a))},$$resolve:function(a){function d(a){k||(k=!0,h.$$resolve(a))}function f(a){k||(k=!0,h.$$reject(a))}var g,h=this,k=!1;try{if(D(a)||z(a))g=a&&a.then;z(g)?(this.promise.$$state.status=-1,g.call(a,d,f,c(this,this.notify))):(this.promise.$$state.value=a,this.promise.$$state.status=
|
||||
1,e(this.promise.$$state))}catch(l){f(l),b(l)}},reject:function(a){this.promise.$$state.status||this.$$reject(a)},$$reject:function(a){this.promise.$$state.value=a;this.promise.$$state.status=2;e(this.promise.$$state)},notify:function(c){var d=this.promise.$$state.pending;0>=this.promise.$$state.status&&d&&d.length&&a(function(){for(var a,e,f=0,g=d.length;f<g;f++){e=d[f][0];a=d[f][3];try{e.notify(z(a)?a(c):c)}catch(h){b(h)}}})}});var k=function(a,b){var c=new f;b?c.resolve(a):c.reject(a);return c.promise},
|
||||
l=function(a,b,c){var d=null;try{z(c)&&(d=c())}catch(e){return k(e,!1)}return d&&z(d.then)?d.then(function(){return k(a,b)},function(a){return k(a,!1)}):k(a,b)},m=function(a,b,c,d){var e=new f;e.resolve(a);return e.promise.then(b,c,d)},n=function(a){if(!z(a))throw g("norslvr",a);var b=new f;a(function(a){b.resolve(a)},function(a){b.reject(a)});return b.promise};n.prototype=d.prototype;n.defer=h;n.reject=function(a){var b=new f;b.reject(a);return b.promise};n.when=m;n.resolve=m;n.all=function(a){var b=
|
||||
new f,c=0,d=L(a)?[]:{};q(a,function(a,e){c++;m(a).then(function(a){d.hasOwnProperty(e)||(d[e]=a,--c||b.resolve(d))},function(a){d.hasOwnProperty(e)||b.reject(a)})});0===c&&b.resolve(d);return b.promise};n.race=function(a){var b=h();q(a,function(a){m(a).then(b.resolve,b.reject)});return b.promise};return n}function Gf(){this.$get=["$window","$timeout",function(a,b){var d=a.requestAnimationFrame||a.webkitRequestAnimationFrame,c=a.cancelAnimationFrame||a.webkitCancelAnimationFrame||a.webkitCancelRequestAnimationFrame,
|
||||
e=!!d,f=e?function(a){var b=d(a);return function(){c(b)}}:function(a){var c=b(a,16.66,!1);return function(){b.cancel(c)}};f.supported=e;return f}]}function vf(){function a(a){function b(){this.$$watchers=this.$$nextSibling=this.$$childHead=this.$$childTail=null;this.$$listeners={};this.$$listenerCount={};this.$$watchersCount=0;this.$id=++pb;this.$$ChildScope=null}b.prototype=a;return b}var b=10,d=N("$rootScope"),c=null,e=null;this.digestTtl=function(a){arguments.length&&(b=a);return b};this.$get=
|
||||
["$exceptionHandler","$parse","$browser",function(f,g,h){function k(a){a.currentScope.$$destroyed=!0}function l(a){9===Ea&&(a.$$childHead&&l(a.$$childHead),a.$$nextSibling&&l(a.$$nextSibling));a.$parent=a.$$nextSibling=a.$$prevSibling=a.$$childHead=a.$$childTail=a.$root=a.$$watchers=null}function m(){this.$id=++pb;this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null;this.$root=this;this.$$destroyed=!1;this.$$listeners={};this.$$listenerCount=
|
||||
{};this.$$watchersCount=0;this.$$isolateBindings=null}function n(a){if(J.$$phase)throw d("inprog",J.$$phase);J.$$phase=a}function p(a,b){do a.$$watchersCount+=b;while(a=a.$parent)}function u(a,b,c){do a.$$listenerCount[c]-=b,0===a.$$listenerCount[c]&&delete a.$$listenerCount[c];while(a=a.$parent)}function s(){}function B(){for(;t.length;)try{t.shift()()}catch(a){f(a)}e=null}function r(){null===e&&(e=h.defer(function(){J.$apply(B)}))}m.prototype={constructor:m,$new:function(b,c){var d;c=c||this;b?
|
||||
(d=new m,d.$root=this.$root):(this.$$ChildScope||(this.$$ChildScope=a(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;(b||c!=this)&&d.$on("$destroy",k);return d},$watch:function(a,b,d,e){var f=g(a);if(f.$$watchDelegate)return f.$$watchDelegate(this,b,d,f,a);var h=this,k=h.$$watchers,l={fn:b,last:s,get:f,exp:e||a,eq:!!d};c=null;z(b)||(l.fn=A);k||(k=h.$$watchers=[]);k.unshift(l);p(this,
|
||||
1);return function(){0<=Za(k,l)&&p(h,-1);c=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)});q(a,function(a,b){var k=g.$watch(a,function(a,f){e[b]=a;d[b]=f;h||(h=!0,g.$evalAsync(c))});f.push(k)});return function(){for(;f.length;)f.shift()()}},
|
||||
$watchCollection:function(a,b){function c(a){e=a;var b,d,g,h;if(!y(e)){if(D(e))if(ta(e))for(f!==n&&(f=n,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!==p&&(f=p={},u=0,l++);a=0;for(b in e)ua.call(e,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)ua.call(e,b)||(u--,delete f[b])}else f!==e&&(f=e,l++);return l}}c.$stateful=!0;var d=this,e,f,h,k=1<b.length,l=0,m=
|
||||
g(a,c),n=[],p={},r=!0,u=0;return this.$watch(m,function(){r?(r=!1,b(e,e,d)):b(e,h,d);if(k)if(D(e))if(ta(e)){h=Array(e.length);for(var a=0;a<e.length;a++)h[a]=e[a]}else for(a in h={},e)ua.call(e,a)&&(h[a]=e[a]);else h=e})},$digest:function(){var a,g,k,l,m,p,u,r,q=b,t,y=[],A,C;n("$digest");h.$$checkUrlChange();this===J&&null!==e&&(h.defer.cancel(e),B());c=null;do{r=!1;t=this;for(p=0;p<v.length;p++){try{C=v[p],C.scope.$eval(C.expression,C.locals)}catch(F){f(F)}c=null}v.length=0;a:do{if(p=t.$$watchers)for(u=
|
||||
p.length;u--;)try{if(a=p[u])if(m=a.get,(g=m(t))!==(k=a.last)&&!(a.eq?na(g,k):"number"===typeof g&&"number"===typeof k&&isNaN(g)&&isNaN(k)))r=!0,c=a,a.last=a.eq?pa(g,null):g,l=a.fn,l(g,k===s?g:k,t),5>q&&(A=4-q,y[A]||(y[A]=[]),y[A].push({msg:z(a.exp)?"fn: "+(a.exp.name||a.exp.toString()):a.exp,newVal:g,oldVal:k}));else if(a===c){r=!1;break a}}catch(G){f(G)}if(!(p=t.$$watchersCount&&t.$$childHead||t!==this&&t.$$nextSibling))for(;t!==this&&!(p=t.$$nextSibling);)t=t.$parent}while(t=p);if((r||v.length)&&
|
||||
!q--)throw J.$$phase=null,d("infdig",b,y);}while(r||v.length);for(J.$$phase=null;K<w.length;)try{w[K++]()}catch(D){f(D)}w.length=K=0},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;this===J&&h.$$applicationDestroyed();p(this,-this.$$watchersCount);for(var b in this.$$listenerCount)u(this,this.$$listenerCount[b],b);a&&a.$$childHead==this&&(a.$$childHead=this.$$nextSibling);a&&a.$$childTail==this&&(a.$$childTail=this.$$prevSibling);this.$$prevSibling&&
|
||||
(this.$$prevSibling.$$nextSibling=this.$$nextSibling);this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling);this.$destroy=this.$digest=this.$apply=this.$evalAsync=this.$applyAsync=A;this.$on=this.$watch=this.$watchGroup=function(){return A};this.$$listeners={};this.$$nextSibling=null;l(this)}},$eval:function(a,b){return g(a)(this,b)},$evalAsync:function(a,b){J.$$phase||v.length||h.defer(function(){v.length&&J.$digest()});v.push({scope:this,expression:g(a),locals:b})},$$postDigest:function(a){w.push(a)},
|
||||
$apply:function(a){try{n("$apply");try{return this.$eval(a)}finally{J.$$phase=null}}catch(b){f(b)}finally{try{J.$digest()}catch(c){throw f(c),c;}}},$applyAsync:function(a){function b(){c.$eval(a)}var c=this;a&&t.push(b);a=g(a);r()},$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,u(e,1,a))}},$emit:function(a,
|
||||
b){var c=[],d,e=this,g=!1,h={name:a,targetScope:e,stopPropagation:function(){g=!0},preventDefault:function(){h.defaultPrevented=!0},defaultPrevented:!1},k=$a([h],arguments,1),l,m;do{d=e.$$listeners[a]||c;h.currentScope=e;l=0;for(m=d.length;l<m;l++)if(d[l])try{d[l].apply(null,k)}catch(n){f(n)}else d.splice(l,1),l--,m--;if(g)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 g=$a([e],arguments,1),h,k;c=d;){e.currentScope=c;d=c.$$listeners[a]||[];h=0;for(k=d.length;h<k;h++)if(d[h])try{d[h].apply(null,g)}catch(l){f(l)}else d.splice(h,1),h--,k--;if(!(d=c.$$listenerCount[a]&&c.$$childHead||c!==this&&c.$$nextSibling))for(;c!==this&&!(d=c.$$nextSibling);)c=c.$parent}e.currentScope=null;return e}};var J=new m,v=J.$$asyncQueue=[],w=J.$$postDigestQueue=[],t=J.$$applyAsyncQueue=[],K=0;return J}]}function ne(){var a=
|
||||
/^\s*(https?|ftp|mailto|tel|file):/,b=/^\s*((https?|ftp|file|blob):|data:image\/)/;this.aHrefSanitizationWhitelist=function(b){return w(b)?(a=b,this):a};this.imgSrcSanitizationWhitelist=function(a){return w(a)?(b=a,this):b};this.$get=function(){return function(d,c){var e=c?b:a,f;f=Y(d).href;return""===f||f.match(e)?d:"unsafe:"+f}}}function og(a){if("self"===a)return a;if(G(a)){if(-1<a.indexOf("***"))throw sa("iwcard",a);a=wd(a).replace("\\*\\*",".*").replace("\\*","[^:/.?&;]*");return new RegExp("^"+
|
||||
a+"$")}if(Wa(a))return new RegExp("^"+a.source+"$");throw sa("imatcher");}function xd(a){var b=[];w(a)&&q(a,function(a){b.push(og(a))});return b}function zf(){this.SCE_CONTEXTS=la;var a=["self"],b=[];this.resourceUrlWhitelist=function(b){arguments.length&&(a=xd(b));return a};this.resourceUrlBlacklist=function(a){arguments.length&&(b=xd(a));return b};this.$get=["$injector",function(d){function c(a,b){return"self"===a?id(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 sa("unsafe");};d.has("$sanitize")&&(f=d.get("$sanitize"));var g=e(),h={};h[la.HTML]=e(g);h[la.CSS]=e(g);h[la.URL]=e(g);h[la.JS]=e(g);h[la.RESOURCE_URL]=e(h[la.URL]);return{trustAs:function(a,b){var c=h.hasOwnProperty(a)?h[a]:null;if(!c)throw sa("icontext",a,b);if(null===b||y(b)||
|
||||
""===b)return b;if("string"!==typeof b)throw sa("itype",a);return new c(b)},getTrusted:function(d,e){if(null===e||y(e)||""===e)return e;var g=h.hasOwnProperty(d)?h[d]:null;if(g&&e instanceof g)return e.$$unwrapTrustedValue();if(d===la.RESOURCE_URL){var g=Y(e.toString()),n,p,u=!1;n=0;for(p=a.length;n<p;n++)if(c(a[n],g)){u=!0;break}if(u)for(n=0,p=b.length;n<p;n++)if(c(b[n],g)){u=!1;break}if(u)return e;throw sa("insecurl",e.toString());}if(d===la.HTML)return f(e);throw sa("unsafe");},valueOf:function(a){return a instanceof
|
||||
g?a.$$unwrapTrustedValue():a}}}]}function yf(){var a=!0;this.enabled=function(b){arguments.length&&(a=!!b);return a};this.$get=["$parse","$sceDelegate",function(b,d){if(a&&8>Ea)throw sa("iequirks");var c=ia(la);c.isEnabled=function(){return a};c.trustAs=d.trustAs;c.getTrusted=d.getTrusted;c.valueOf=d.valueOf;a||(c.trustAs=c.getTrusted=function(a,b){return b},c.valueOf=Xa);c.parseAs=function(a,d){var e=b(d);return e.literal&&e.constant?e:b(d,function(b){return c.getTrusted(a,b)})};var e=c.parseAs,
|
||||
f=c.getTrusted,g=c.trustAs;q(la,function(a,b){var d=Q(b);c[db("parse_as_"+d)]=function(b){return e(a,b)};c[db("get_trusted_"+d)]=function(b){return f(a,b)};c[db("trust_as_"+d)]=function(b){return g(a,b)}});return c}]}function Af(){this.$get=["$window","$document",function(a,b){var d={},c=!(a.chrome&&a.chrome.app&&a.chrome.app.runtime)&&a.history&&a.history.pushState,e=Z((/android (\d+)/.exec(Q((a.navigator||{}).userAgent))||[])[1]),f=/Boxee/i.test((a.navigator||{}).userAgent),g=b[0]||{},h,k=/^(Moz|webkit|ms)(?=[A-Z])/,
|
||||
l=g.body&&g.body.style,m=!1,n=!1;if(l){for(var p in l)if(m=k.exec(p)){h=m[0];h=h[0].toUpperCase()+h.substr(1);break}h||(h="WebkitOpacity"in l&&"webkit");m=!!("transition"in l||h+"Transition"in l);n=!!("animation"in l||h+"Animation"in l);!e||m&&n||(m=G(l.webkitTransition),n=G(l.webkitAnimation))}return{history:!(!c||4>e||f),hasEvent:function(a){if("input"===a&&11>=Ea)return!1;if(y(d[a])){var b=g.createElement("div");d[a]="on"+a in b}return d[a]},csp:Ba(),vendorPrefix:h,transitions:m,animations:n,android:e}}]}
|
||||
function Cf(){var a;this.httpOptions=function(b){return b?(a=b,this):a};this.$get=["$templateCache","$http","$q","$sce",function(b,d,c,e){function f(g,h){f.totalPendingRequests++;if(!G(g)||y(b.get(g)))g=e.getTrustedResourceUrl(g);var k=d.defaults&&d.defaults.transformResponse;L(k)?k=k.filter(function(a){return a!==dc}):k===dc&&(k=null);return d.get(g,S({cache:b,transformResponse:k},a))["finally"](function(){f.totalPendingRequests--}).then(function(a){b.put(g,a.data);return a.data},function(a){if(!h)throw pg("tpload",
|
||||
g,a.status,a.statusText);return c.reject(a)})}f.totalPendingRequests=0;return f}]}function Df(){this.$get=["$rootScope","$browser","$location",function(a,b,d){return{findBindings:function(a,b,d){a=a.getElementsByClassName("ng-binding");var g=[];q(a,function(a){var c=ca.element(a).data("$binding");c&&q(c,function(c){d?(new RegExp("(^|\\s)"+wd(b)+"(\\s|\\||$)")).test(c)&&g.push(a):-1!=c.indexOf(b)&&g.push(a)})});return g},findModels:function(a,b,d){for(var g=["ng-","data-ng-","ng\\:"],h=0;h<g.length;++h){var k=
|
||||
a.querySelectorAll("["+g[h]+"model"+(d?"=":"*=")+'"'+b+'"]');if(k.length)return k}},getLocation:function(){return d.url()},setLocation:function(b){b!==d.url()&&(d.url(b),a.$digest())},whenStable:function(a){b.notifyWhenNoOutstandingRequests(a)}}}]}function Ef(){this.$get=["$rootScope","$browser","$q","$$q","$exceptionHandler",function(a,b,d,c,e){function f(f,k,l){z(f)||(l=k,k=f,f=A);var m=va.call(arguments,3),n=w(l)&&!l,p=(n?c:d).defer(),u=p.promise,q;q=b.defer(function(){try{p.resolve(f.apply(null,
|
||||
m))}catch(b){p.reject(b),e(b)}finally{delete g[u.$$timeoutId]}n||a.$apply()},k);u.$$timeoutId=q;g[q]=p;return u}var g={};f.cancel=function(a){return a&&a.$$timeoutId in g?(g[a.$$timeoutId].reject("canceled"),delete g[a.$$timeoutId],b.defer.cancel(a.$$timeoutId)):!1};return f}]}function Y(a){Ea&&($.setAttribute("href",a),a=$.href);$.setAttribute("href",a);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 id(a){a=G(a)?Y(a):a;return a.protocol===yd.protocol&&a.host===yd.host}function Ff(){this.$get=ha(C)}function zd(a){function b(a){try{return decodeURIComponent(a)}catch(b){return a}}var d=a[0]||{},c={},e="";return function(){var a,g,h,k,l;a=d.cookie||"";if(a!==e)for(e=a,a=e.split("; "),c={},h=0;h<a.length;h++)g=a[h],k=g.indexOf("="),0<k&&(l=b(g.substring(0,k)),y(c[l])&&(c[l]=b(g.substring(k+
|
||||
1))));return c}}function Jf(){this.$get=zd}function Mc(a){function b(d,c){if(D(d)){var e={};q(d,function(a,c){e[c]=b(c,a)});return e}return a.factory(d+"Filter",c)}this.register=b;this.$get=["$injector",function(a){return function(b){return a.get(b+"Filter")}}];b("currency",Ad);b("date",Bd);b("filter",qg);b("json",rg);b("limitTo",sg);b("lowercase",tg);b("number",Cd);b("orderBy",Dd);b("uppercase",ug)}function qg(){return function(a,b,d,c){if(!ta(a)){if(null==a)return a;throw N("filter")("notarray",
|
||||
a);}c=c||"$";var e;switch(lc(b)){case "function":break;case "boolean":case "null":case "number":case "string":e=!0;case "object":b=vg(b,d,c,e);break;default:return a}return Array.prototype.filter.call(a,b)}}function vg(a,b,d,c){var e=D(a)&&d in a;!0===b?b=na:z(b)||(b=function(a,b){if(y(a))return!1;if(null===a||null===b)return a===b;if(D(b)||D(a)&&!vc(a))return!1;a=Q(""+a);b=Q(""+b);return-1!==a.indexOf(b)});return function(f){return e&&!D(f)?La(f,a[d],b,d,!1):La(f,a,b,d,c)}}function La(a,b,d,c,e,
|
||||
f){var g=lc(a),h=lc(b);if("string"===h&&"!"===b.charAt(0))return!La(a,b.substring(1),d,c,e);if(L(a))return a.some(function(a){return La(a,b,d,c,e)});switch(g){case "object":var k;if(e){for(k in a)if("$"!==k.charAt(0)&&La(a[k],b,d,c,!0))return!0;return f?!1:La(a,b,d,c,!1)}if("object"===h){for(k in b)if(f=b[k],!z(f)&&!y(f)&&(g=k===c,!La(g?a:a[k],f,d,c,g,g)))return!1;return!0}return d(a,b);case "function":return!1;default:return d(a,b)}}function lc(a){return null===a?"null":typeof a}function Ad(a){var b=
|
||||
a.NUMBER_FORMATS;return function(a,c,e){y(c)&&(c=b.CURRENCY_SYM);y(e)&&(e=b.PATTERNS[1].maxFrac);return null==a?a:Ed(a,b.PATTERNS[1],b.GROUP_SEP,b.DECIMAL_SEP,e).replace(/\u00A4/g,c)}}function Cd(a){var b=a.NUMBER_FORMATS;return function(a,c){return null==a?a:Ed(a,b.PATTERNS[0],b.GROUP_SEP,b.DECIMAL_SEP,c)}}function wg(a){var b=0,d,c,e,f,g;-1<(c=a.indexOf(Fd))&&(a=a.replace(Fd,""));0<(e=a.search(/e/i))?(0>c&&(c=e),c+=+a.slice(e+1),a=a.substring(0,e)):0>c&&(c=a.length);for(e=0;a.charAt(e)==mc;e++);
|
||||
if(e==(g=a.length))d=[0],c=1;else{for(g--;a.charAt(g)==mc;)g--;c-=e;d=[];for(f=0;e<=g;e++,f++)d[f]=+a.charAt(e)}c>Gd&&(d=d.splice(0,Gd-1),b=c-1,c=1);return{d:d,e:b,i:c}}function xg(a,b,d,c){var e=a.d,f=e.length-a.i;b=y(b)?Math.min(Math.max(d,f),c):+b;d=b+a.i;c=e[d];if(0<d){e.splice(Math.max(a.i,d));for(var g=d;g<e.length;g++)e[g]=0}else for(f=Math.max(0,f),a.i=1,e.length=Math.max(1,d=b+1),e[0]=0,g=1;g<d;g++)e[g]=0;if(5<=c)if(0>d-1){for(c=0;c>d;c--)e.unshift(0),a.i++;e.unshift(1);a.i++}else e[d-1]++;
|
||||
for(;f<Math.max(0,b);f++)e.push(0);if(b=e.reduceRight(function(a,b,c,d){b+=a;d[c]=b%10;return Math.floor(b/10)},0))e.unshift(b),a.i++}function Ed(a,b,d,c,e){if(!G(a)&&!T(a)||isNaN(a))return"";var f=!isFinite(a),g=!1,h=Math.abs(a)+"",k="";if(f)k="\u221e";else{g=wg(h);xg(g,e,b.minFrac,b.maxFrac);k=g.d;h=g.i;e=g.e;f=[];for(g=k.reduce(function(a,b){return a&&!b},!0);0>h;)k.unshift(0),h++;0<h?f=k.splice(h,k.length):(f=k,k=[0]);h=[];for(k.length>=b.lgSize&&h.unshift(k.splice(-b.lgSize,k.length).join(""));k.length>
|
||||
b.gSize;)h.unshift(k.splice(-b.gSize,k.length).join(""));k.length&&h.unshift(k.join(""));k=h.join(d);f.length&&(k+=c+f.join(""));e&&(k+="e+"+e)}return 0>a&&!g?b.negPre+k+b.negSuf:b.posPre+k+b.posSuf}function Kb(a,b,d,c){var e="";if(0>a||c&&0>=a)c?a=-a+1:(a=-a,e="-");for(a=""+a;a.length<b;)a=mc+a;d&&(a=a.substr(a.length-b));return e+a}function ba(a,b,d,c,e){d=d||0;return function(f){f=f["get"+a]();if(0<d||f>-d)f+=d;0===f&&-12==d&&(f=12);return Kb(f,b,c,e)}}function kb(a,b,d){return function(c,e){var f=
|
||||
c["get"+a](),g=ub((d?"STANDALONE":"")+(b?"SHORT":"")+a);return e[g][f]}}function Hd(a){var b=(new Date(a,0,1)).getDay();return new Date(a,0,(4>=b?5:12)-b)}function Id(a){return function(b){var d=Hd(b.getFullYear());b=+new Date(b.getFullYear(),b.getMonth(),b.getDate()+(4-b.getDay()))-+d;b=1+Math.round(b/6048E5);return Kb(b,a)}}function nc(a,b){return 0>=a.getFullYear()?b.ERAS[0]:b.ERAS[1]}function Bd(a){function b(a){var b;if(b=a.match(d)){a=new Date(0);var f=0,g=0,h=b[8]?a.setUTCFullYear:a.setFullYear,
|
||||
k=b[8]?a.setUTCHours:a.setHours;b[9]&&(f=Z(b[9]+b[10]),g=Z(b[9]+b[11]));h.call(a,Z(b[1]),Z(b[2])-1,Z(b[3]));f=Z(b[4]||0)-f;g=Z(b[5]||0)-g;h=Z(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));k.call(a,f,g,h,b)}return a}var d=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,d,f){var g="",h=[],k,l;d=d||"mediumDate";d=a.DATETIME_FORMATS[d]||d;G(c)&&(c=yg.test(c)?Z(c):b(c));T(c)&&(c=new Date(c));if(!da(c)||!isFinite(c.getTime()))return c;
|
||||
for(;d;)(l=zg.exec(d))?(h=$a(h,l,1),d=h.pop()):(h.push(d),d=null);var m=c.getTimezoneOffset();f&&(m=yc(f,m),c=Sb(c,f,!0));q(h,function(b){k=Ag[b];g+=k?k(c,a.DATETIME_FORMATS,m):"''"===b?"'":b.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function rg(){return function(a,b){y(b)&&(b=2);return bb(a,b)}}function sg(){return function(a,b,d){b=Infinity===Math.abs(Number(b))?Number(b):Z(b);if(isNaN(b))return a;T(a)&&(a=a.toString());if(!ta(a))return a;d=!d||isNaN(d)?0:Z(d);d=0>d?Math.max(0,a.length+
|
||||
d):d;return 0<=b?oc(a,d,d+b):0===d?oc(a,b,a.length):oc(a,Math.max(0,d+b),d)}}function oc(a,b,d){return G(a)?a.slice(b,d):va.call(a,b,d)}function Dd(a){function b(b){return b.map(function(b){var c=1,d=Xa;if(z(b))d=b;else if(G(b)){if("+"==b.charAt(0)||"-"==b.charAt(0))c="-"==b.charAt(0)?-1:1,b=b.substring(1);if(""!==b&&(d=a(b),d.constant))var e=d(),d=function(a){return a[e]}}return{get:d,descending:c}})}function d(a){switch(typeof a){case "number":case "boolean":case "string":return!0;default:return!1}}
|
||||
function c(a,b){var c=0,d=a.type,k=b.type;if(d===k){var k=a.value,l=b.value;"string"===d?(k=k.toLowerCase(),l=l.toLowerCase()):"object"===d&&(D(k)&&(k=a.index),D(l)&&(l=b.index));k!==l&&(c=k<l?-1:1)}else c=d<k?-1:1;return c}return function(a,f,g,h){if(null==a)return a;if(!ta(a))throw N("orderBy")("notarray",a);L(f)||(f=[f]);0===f.length&&(f=["+"]);var k=b(f),l=g?-1:1,m=z(h)?h:c;a=Array.prototype.map.call(a,function(a,b){return{value:a,tieBreaker:{value:b,type:"number",index:b},predicateValues:k.map(function(c){var e=
|
||||
c.get(a);c=typeof e;if(null===e)c="string",e="null";else if("object"===c)a:{if(z(e.valueOf)&&(e=e.valueOf(),d(e)))break a;vc(e)&&(e=e.toString(),d(e))}return{value:e,type:c,index:b}})}});a.sort(function(a,b){for(var c=0,d=k.length;c<d;c++){var e=m(a.predicateValues[c],b.predicateValues[c]);if(e)return e*k[c].descending*l}return m(a.tieBreaker,b.tieBreaker)*l});return a=a.map(function(a){return a.value})}}function Ta(a){z(a)&&(a={link:a});a.restrict=a.restrict||"AC";return ha(a)}function Jd(a,b,d,
|
||||
c,e){var f=this,g=[];f.$error={};f.$$success={};f.$pending=void 0;f.$name=e(b.name||b.ngForm||"")(d);f.$dirty=!1;f.$pristine=!0;f.$valid=!0;f.$invalid=!1;f.$submitted=!1;f.$$parentForm=Lb;f.$rollbackViewValue=function(){q(g,function(a){a.$rollbackViewValue()})};f.$commitViewValue=function(){q(g,function(a){a.$commitViewValue()})};f.$addControl=function(a){Qa(a.$name,"input");g.push(a);a.$name&&(f[a.$name]=a);a.$$parentForm=f};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];q(f.$pending,function(b,c){f.$setValidity(c,null,a)});q(f.$error,function(b,c){f.$setValidity(c,null,a)});q(f.$$success,function(b,c){f.$setValidity(c,null,a)});Za(g,a);a.$$parentForm=Lb};Kd({ctrl:this,$element:a,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&&(Za(d,c),0===d.length&&delete a[b])},$animate:c});f.$setDirty=function(){c.removeClass(a,Ua);c.addClass(a,
|
||||
Mb);f.$dirty=!0;f.$pristine=!1;f.$$parentForm.$setDirty()};f.$setPristine=function(){c.setClass(a,Ua,Mb+" ng-submitted");f.$dirty=!1;f.$pristine=!0;f.$submitted=!1;q(g,function(a){a.$setPristine()})};f.$setUntouched=function(){q(g,function(a){a.$setUntouched()})};f.$setSubmitted=function(){c.addClass(a,"ng-submitted");f.$submitted=!0;f.$$parentForm.$setSubmitted()}}function pc(a){a.$formatters.push(function(b){return a.$isEmpty(b)?b:b.toString()})}function lb(a,b,d,c,e,f){var g=Q(b[0].type);if(!e.android){var h=
|
||||
!1;b.on("compositionstart",function(){h=!0});b.on("compositionend",function(){h=!1;l()})}var k,l=function(a){k&&(f.defer.cancel(k),k=null);if(!h){var e=b.val();a=a&&a.type;"password"===g||d.ngTrim&&"false"===d.ngTrim||(e=W(e));(c.$viewValue!==e||""===e&&c.$$hasNativeValidators)&&c.$setViewValue(e,a)}};if(e.hasEvent("input"))b.on("input",l);else{var m=function(a,b,c){k||(k=f.defer(function(){k=null;b&&b.value===c||l(a)}))};b.on("keydown",function(a){var b=a.keyCode;91===b||15<b&&19>b||37<=b&&40>=b||
|
||||
m(a,this,this.value)});if(e.hasEvent("paste"))b.on("paste cut",m)}b.on("change",l);if(Ld[g]&&c.$$hasNativeValidators&&g===d.type)b.on("keydown wheel mousedown",function(a){if(!k){var b=this.validity,c=b.badInput,d=b.typeMismatch;k=f.defer(function(){k=null;b.badInput===c&&b.typeMismatch===d||l(a)})}});c.$render=function(){var a=c.$isEmpty(c.$viewValue)?"":c.$viewValue;b.val()!==a&&b.val(a)}}function Nb(a,b){return function(d,c){var e,f;if(da(d))return d;if(G(d)){'"'==d.charAt(0)&&'"'==d.charAt(d.length-
|
||||
1)&&(d=d.substring(1,d.length-1));if(Bg.test(d))return new Date(d);a.lastIndex=0;if(e=a.exec(d))return e.shift(),f=c?{yyyy:c.getFullYear(),MM:c.getMonth()+1,dd:c.getDate(),HH:c.getHours(),mm:c.getMinutes(),ss:c.getSeconds(),sss:c.getMilliseconds()/1E3}:{yyyy:1970,MM:1,dd:1,HH:0,mm:0,ss:0,sss:0},q(e,function(a,c){c<b.length&&(f[b[c]]=+a)}),new Date(f.yyyy,f.MM-1,f.dd,f.HH,f.mm,f.ss||0,1E3*f.sss||0)}return NaN}}function mb(a,b,d,c){return function(e,f,g,h,k,l,m){function n(a){return a&&!(a.getTime&&
|
||||
a.getTime()!==a.getTime())}function p(a){return w(a)&&!da(a)?d(a)||void 0:a}Md(e,f,g,h);lb(e,f,g,h,k,l);var u=h&&h.$options&&h.$options.timezone,q;h.$$parserName=a;h.$parsers.push(function(a){if(h.$isEmpty(a))return null;if(b.test(a))return a=d(a,q),u&&(a=Sb(a,u)),a});h.$formatters.push(function(a){if(a&&!da(a))throw nb("datefmt",a);if(n(a))return(q=a)&&u&&(q=Sb(q,u,!0)),m("date")(a,c,u);q=null;return""});if(w(g.min)||g.ngMin){var s;h.$validators.min=function(a){return!n(a)||y(s)||d(a)>=s};g.$observe("min",
|
||||
function(a){s=p(a);h.$validate()})}if(w(g.max)||g.ngMax){var r;h.$validators.max=function(a){return!n(a)||y(r)||d(a)<=r};g.$observe("max",function(a){r=p(a);h.$validate()})}}}function Md(a,b,d,c){(c.$$hasNativeValidators=D(b[0].validity))&&c.$parsers.push(function(a){var c=b.prop("validity")||{};return c.badInput||c.typeMismatch?void 0:a})}function Nd(a,b,d,c,e){if(w(c)){a=a(c);if(!a.constant)throw nb("constexpr",d,c);return a(b)}return e}function qc(a,b){a="ngClass"+a;return["$animate",function(d){function c(a,
|
||||
b){var c=[],d=0;a:for(;d<a.length;d++){for(var e=a[d],m=0;m<b.length;m++)if(e==b[m])continue a;c.push(e)}return c}function e(a){var b=[];return L(a)?(q(a,function(a){b=b.concat(e(a))}),b):G(a)?a.split(" "):D(a)?(q(a,function(a,c){a&&(b=b.concat(c.split(" ")))}),b):a}return{restrict:"AC",link:function(f,g,h){function k(a){a=l(a,1);h.$addClass(a)}function l(a,b){var c=g.data("$classCounts")||U(),d=[];q(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 m(a,b){var e=c(b,a),f=c(a,b),e=l(e,1),f=l(f,-1);e&&e.length&&d.addClass(g,e);f&&f.length&&d.removeClass(g,f)}function n(a){if(!0===b||(f.$index&1)===b){var c=e(a||[]);if(!p)k(c);else if(!na(a,p)){var d=e(p);m(d,c)}}p=L(a)?a.map(function(a){return ia(a)}):ia(a)}var p;f.$watch(h[a],n,!0);h.$observe("class",function(b){n(f.$eval(h[a]))});"ngClass"!==a&&f.$watch("$index",function(c,d){var g=c&1;if(g!==(d&1)){var m=e(f.$eval(h[a]));g===b?k(m):(g=l(m,-1),h.$removeClass(g))}})}}}]}function Kd(a){function b(a,
|
||||
b){b&&!f[a]?(k.addClass(e,a),f[a]=!0):!b&&f[a]&&(k.removeClass(e,a),f[a]=!1)}function d(a,c){a=a?"-"+Cc(a,"-"):"";b(ob+a,!0===c);b(Od+a,!1===c)}var c=a.ctrl,e=a.$element,f={},g=a.set,h=a.unset,k=a.$animate;f[Od]=!(f[ob]=e.hasClass(ob));c.$setValidity=function(a,e,f){y(e)?(c.$pending||(c.$pending={}),g(c.$pending,a,f)):(c.$pending&&h(c.$pending,a,f),Pd(c.$pending)&&(c.$pending=void 0));Ga(e)?e?(h(c.$error,a,f),g(c.$$success,a,f)):(g(c.$error,a,f),h(c.$$success,a,f)):(h(c.$error,a,f),h(c.$$success,
|
||||
a,f));c.$pending?(b(Qd,!0),c.$valid=c.$invalid=void 0,d("",null)):(b(Qd,!1),c.$valid=Pd(c.$error),c.$invalid=!c.$valid,d("",c.$valid));e=c.$pending&&c.$pending[a]?void 0:c.$error[a]?!1:c.$$success[a]?!0:null;d(a,e);c.$$parentForm.$setValidity(a,e,c)}}function Pd(a){if(a)for(var b in a)if(a.hasOwnProperty(b))return!1;return!0}var Cg=/^\/(.+)\/([a-z]*)$/,ua=Object.prototype.hasOwnProperty,Q=function(a){return G(a)?a.toLowerCase():a},ub=function(a){return G(a)?a.toUpperCase():a},Ea,F,qa,va=[].slice,
|
||||
bg=[].splice,Dg=[].push,ma=Object.prototype.toString,wc=Object.getPrototypeOf,xa=N("ng"),ca=C.angular||(C.angular={}),Ub,pb=0;Ea=C.document.documentMode;A.$inject=[];Xa.$inject=[];var L=Array.isArray,ae=/^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/,W=function(a){return G(a)?a.trim():a},wd=function(a){return a.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08")},Ba=function(){if(!w(Ba.rules)){var a=C.document.querySelector("[ng-csp]")||
|
||||
C.document.querySelector("[data-ng-csp]");if(a){var b=a.getAttribute("ng-csp")||a.getAttribute("data-ng-csp");Ba.rules={noUnsafeEval:!b||-1!==b.indexOf("no-unsafe-eval"),noInlineStyle:!b||-1!==b.indexOf("no-inline-style")}}else{a=Ba;try{new Function(""),b=!1}catch(d){b=!0}a.rules={noUnsafeEval:b,noInlineStyle:!1}}}return Ba.rules},rb=function(){if(w(rb.name_))return rb.name_;var a,b,d=Na.length,c,e;for(b=0;b<d;++b)if(c=Na[b],a=C.document.querySelector("["+c.replace(":","\\:")+"jq]")){e=a.getAttribute(c+
|
||||
"jq");break}return rb.name_=e},de=/:/g,Na=["ng-","data-ng-","ng:","x-ng-"],ie=/[A-Z]/g,Dc=!1,Ma=3,me={full:"1.5.8",major:1,minor:5,dot:8,codeName:"arbitrary-fallbacks"};O.expando="ng339";var fb=O.cache={},Pf=1;O._data=function(a){return this.cache[a[this.expando]]||{}};var Kf=/([\:\-\_]+(.))/g,Lf=/^moz([A-Z])/,yb={mouseleave:"mouseout",mouseenter:"mouseover"},Wb=N("jqLite"),Of=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,Vb=/<|&#?\w+;/,Mf=/<([\w:-]+)/,Nf=/<(?!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 Uf=C.Node.prototype.contains||function(a){return!!(this.compareDocumentPosition(a)&16)},Oa=O.prototype={ready:function(a){function b(){d||(d=!0,a())}var d=!1;"complete"===
|
||||
C.document.readyState?C.setTimeout(b):(this.on("DOMContentLoaded",b),O(C).on("load",b))},toString:function(){var a=[];q(this,function(b){a.push(""+b)});return"["+a.join(", ")+"]"},eq:function(a){return 0<=a?F(this[a]):F(this[this.length+a])},length:0,push:Dg,sort:[].sort,splice:[].splice},Eb={};q("multiple selected checked disabled readOnly required open".split(" "),function(a){Eb[Q(a)]=a});var Vc={};q("input select option textarea button form details".split(" "),function(a){Vc[a]=!0});var bd={ngMinlength:"minlength",
|
||||
ngMaxlength:"maxlength",ngMin:"min",ngMax:"max",ngPattern:"pattern"};q({data:Yb,removeData:eb,hasData:function(a){for(var b in fb[a.ng339])return!0;return!1},cleanData:function(a){for(var b=0,d=a.length;b<d;b++)eb(a[b])}},function(a,b){O[b]=a});q({data:Yb,inheritedData:Cb,scope:function(a){return F.data(a,"$scope")||Cb(a.parentNode||a,["$isolateScope","$scope"])},isolateScope:function(a){return F.data(a,"$isolateScope")||F.data(a,"$isolateScopeNoTemplate")},controller:Sc,injector:function(a){return Cb(a,
|
||||
"$injector")},removeAttr:function(a,b){a.removeAttribute(b)},hasClass:zb,css:function(a,b,d){b=db(b);if(w(d))a.style[b]=d;else return a.style[b]},attr:function(a,b,d){var c=a.nodeType;if(c!==Ma&&2!==c&&8!==c)if(c=Q(b),Eb[c])if(w(d))d?(a[b]=!0,a.setAttribute(b,c)):(a[b]=!1,a.removeAttribute(c));else return a[b]||(a.attributes.getNamedItem(b)||A).specified?c:void 0;else if(w(d))a.setAttribute(b,d);else if(a.getAttribute)return a=a.getAttribute(b,2),null===a?void 0:a},prop:function(a,b,d){if(w(d))a[b]=
|
||||
d;else return a[b]},text:function(){function a(a,d){if(y(d)){var c=a.nodeType;return 1===c||c===Ma?a.textContent:""}a.textContent=d}a.$dv="";return a}(),val:function(a,b){if(y(b)){if(a.multiple&&"select"===wa(a)){var d=[];q(a.options,function(a){a.selected&&d.push(a.value||a.text)});return 0===d.length?null:d}return a.value}a.value=b},html:function(a,b){if(y(b))return a.innerHTML;wb(a,!0);a.innerHTML=b},empty:Tc},function(a,b){O.prototype[b]=function(b,c){var e,f,g=this.length;if(a!==Tc&&y(2==a.length&&
|
||||
a!==zb&&a!==Sc?b:c)){if(D(b)){for(e=0;e<g;e++)if(a===Yb)a(this[e],b);else for(f in b)a(this[e],f,b[f]);return this}e=a.$dv;g=y(e)?Math.min(g,1):g;for(f=0;f<g;f++){var h=a(this[f],b,c);e=e?e+h:h}return e}for(e=0;e<g;e++)a(this[e],b,c);return this}});q({removeData:eb,on:function(a,b,d,c){if(w(c))throw Wb("onargs");if(Nc(a)){c=xb(a,!0);var e=c.events,f=c.handle;f||(f=c.handle=Rf(a,e));c=0<=b.indexOf(" ")?b.split(" "):[b];for(var g=c.length,h=function(b,c,g){var h=e[b];h||(h=e[b]=[],h.specialHandlerWrapper=
|
||||
c,"$destroy"===b||g||a.addEventListener(b,f,!1));h.push(d)};g--;)b=c[g],yb[b]?(h(yb[b],Tf),h(b,void 0,!0)):h(b)}},off:Rc,one:function(a,b,d){a=F(a);a.on(b,function e(){a.off(b,d);a.off(b,e)});a.on(b,d)},replaceWith:function(a,b){var d,c=a.parentNode;wb(a);q(new O(b),function(b){d?c.insertBefore(b,d.nextSibling):c.replaceChild(b,a);d=b})},children:function(a){var b=[];q(a.childNodes,function(a){1===a.nodeType&&b.push(a)});return b},contents:function(a){return a.contentDocument||a.childNodes||[]},append:function(a,
|
||||
b){var d=a.nodeType;if(1===d||11===d){b=new O(b);for(var d=0,c=b.length;d<c;d++)a.appendChild(b[d])}},prepend:function(a,b){if(1===a.nodeType){var d=a.firstChild;q(new O(b),function(b){a.insertBefore(b,d)})}},wrap:function(a,b){Pc(a,F(b).eq(0).clone()[0])},remove:Db,detach:function(a){Db(a,!0)},after:function(a,b){var d=a,c=a.parentNode;b=new O(b);for(var e=0,f=b.length;e<f;e++){var g=b[e];c.insertBefore(g,d.nextSibling);d=g}},addClass:Bb,removeClass:Ab,toggleClass:function(a,b,d){b&&q(b.split(" "),
|
||||
function(b){var e=d;y(e)&&(e=!zb(a,b));(e?Bb:Ab)(a,b)})},parent:function(a){return(a=a.parentNode)&&11!==a.nodeType?a:null},next:function(a){return a.nextElementSibling},find:function(a,b){return a.getElementsByTagName?a.getElementsByTagName(b):[]},clone:Xb,triggerHandler:function(a,b,d){var c,e,f=b.type||b,g=xb(a);if(g=(g=g&&g.events)&&g[f])c={preventDefault:function(){this.defaultPrevented=!0},isDefaultPrevented:function(){return!0===this.defaultPrevented},stopImmediatePropagation:function(){this.immediatePropagationStopped=
|
||||
!0},isImmediatePropagationStopped:function(){return!0===this.immediatePropagationStopped},stopPropagation:A,type:f,target:a},b.type&&(c=S(c,b)),b=ia(g),e=d?[c].concat(d):[c],q(b,function(b){c.isImmediatePropagationStopped()||b.apply(a,e)})}},function(a,b){O.prototype[b]=function(b,c,e){for(var f,g=0,h=this.length;g<h;g++)y(f)?(f=a(this[g],b,c,e),w(f)&&(f=F(f))):Qc(f,a(this[g],b,c,e));return w(f)?f:this};O.prototype.bind=O.prototype.on;O.prototype.unbind=O.prototype.off});Ra.prototype={put:function(a,
|
||||
b){this[Ca(a,this.nextUid)]=b},get:function(a){return this[Ca(a,this.nextUid)]},remove:function(a){var b=this[a=Ca(a,this.nextUid)];delete this[a];return b}};var If=[function(){this.$get=[function(){return Ra}]}],Wf=/^([^\(]+?)=>/,Xf=/^[^\(]*\(\s*([^\)]*)\)/m,Eg=/,/,Fg=/^\s*(_?)(\S+?)\1\s*$/,Vf=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Ha=N("$injector");cb.$$annotate=function(a,b,d){var c;if("function"===typeof a){if(!(c=a.$inject)){c=[];if(a.length){if(b)throw G(d)&&d||(d=a.name||Yf(a)),Ha("strictdi",d);
|
||||
b=Wc(a);q(b[1].split(Eg),function(a){a.replace(Fg,function(a,b,d){c.push(d)})})}a.$inject=c}}else L(a)?(b=a.length-1,Pa(a[b],"fn"),c=a.slice(0,b)):Pa(a,"fn",!0);return c};var Rd=N("$animate"),$e=function(){this.$get=A},af=function(){var a=new Ra,b=[];this.$get=["$$AnimateRunner","$rootScope",function(d,c){function e(a,b,c){var d=!1;b&&(b=G(b)?b.split(" "):L(b)?b:[],q(b,function(b){b&&(d=!0,a[b]=c)}));return d}function f(){q(b,function(b){var c=a.get(b);if(c){var d=Zf(b.attr("class")),e="",f="";q(c,
|
||||
function(a,b){a!==!!d[b]&&(a?e+=(e.length?" ":"")+b:f+=(f.length?" ":"")+b)});q(b,function(a){e&&Bb(a,e);f&&Ab(a,f)});a.remove(b)}});b.length=0}return{enabled:A,on:A,off:A,pin:A,push:function(g,h,k,l){l&&l();k=k||{};k.from&&g.css(k.from);k.to&&g.css(k.to);if(k.addClass||k.removeClass)if(h=k.addClass,l=k.removeClass,k=a.get(g)||{},h=e(k,h,!0),l=e(k,l,!1),h||l)a.put(g,k),b.push(g),1===b.length&&c.$$postDigest(f);g=new d;g.complete();return g}}}]},Ye=["$provide",function(a){var b=this;this.$$registeredAnimations=
|
||||
Object.create(null);this.register=function(d,c){if(d&&"."!==d.charAt(0))throw Rd("notcsel",d);var e=d+"-animation";b.$$registeredAnimations[d.substr(1)]=e;a.factory(e,c)};this.classNameFilter=function(a){if(1===arguments.length&&(this.$$classNameFilter=a instanceof RegExp?a:null)&&/(\s+|\/)ng-animate(\s+|\/)/.test(this.$$classNameFilter.toString()))throw Rd("nongcls","ng-animate");return this.$$classNameFilter};this.$get=["$$animateQueue",function(a){function b(a,c,d){if(d){var h;a:{for(h=0;h<d.length;h++){var k=
|
||||
d[h];if(1===k.nodeType){h=k;break a}}h=void 0}!h||h.parentNode||h.previousElementSibling||(d=null)}d?d.after(a):c.prepend(a)}return{on:a.on,off:a.off,pin:a.pin,enabled:a.enabled,cancel:function(a){a.end&&a.end()},enter:function(e,f,g,h){f=f&&F(f);g=g&&F(g);f=f||g.parent();b(e,f,g);return a.push(e,"enter",Ia(h))},move:function(e,f,g,h){f=f&&F(f);g=g&&F(g);f=f||g.parent();b(e,f,g);return a.push(e,"move",Ia(h))},leave:function(b,c){return a.push(b,"leave",Ia(c),function(){b.remove()})},addClass:function(b,
|
||||
c,g){g=Ia(g);g.addClass=gb(g.addclass,c);return a.push(b,"addClass",g)},removeClass:function(b,c,g){g=Ia(g);g.removeClass=gb(g.removeClass,c);return a.push(b,"removeClass",g)},setClass:function(b,c,g,h){h=Ia(h);h.addClass=gb(h.addClass,c);h.removeClass=gb(h.removeClass,g);return a.push(b,"setClass",h)},animate:function(b,c,g,h,k){k=Ia(k);k.from=k.from?S(k.from,c):c;k.to=k.to?S(k.to,g):g;k.tempClasses=gb(k.tempClasses,h||"ng-inline-animate");return a.push(b,"animate",k)}}}]}],cf=function(){this.$get=
|
||||
["$$rAF",function(a){function b(b){d.push(b);1<d.length||a(function(){for(var a=0;a<d.length;a++)d[a]();d=[]})}var d=[];return function(){var a=!1;b(function(){a=!0});return function(d){a?d():b(d)}}}]},bf=function(){this.$get=["$q","$sniffer","$$animateAsyncRun","$document","$timeout",function(a,b,d,c,e){function f(a){this.setHost(a);var b=d();this._doneCallbacks=[];this._tick=function(a){var d=c[0];d&&d.hidden?e(a,0,!1):b(a)};this._state=0}f.chain=function(a,b){function c(){if(d===a.length)b(!0);
|
||||
else a[d](function(a){!1===a?b(!1):(d++,c())})}var d=0;c()};f.all=function(a,b){function c(f){e=e&&f;++d===a.length&&b(e)}var d=0,e=!0;q(a,function(a){a.done(c)})};f.prototype={setHost:function(a){this.host=a||{}},done:function(a){2===this._state?a():this._doneCallbacks.push(a)},progress:A,getPromise:function(){if(!this.promise){var b=this;this.promise=a(function(a,c){b.done(function(b){!1===b?c():a()})})}return this.promise},then:function(a,b){return this.getPromise().then(a,b)},"catch":function(a){return this.getPromise()["catch"](a)},
|
||||
"finally":function(a){return this.getPromise()["finally"](a)},pause:function(){this.host.pause&&this.host.pause()},resume:function(){this.host.resume&&this.host.resume()},end:function(){this.host.end&&this.host.end();this._resolve(!0)},cancel:function(){this.host.cancel&&this.host.cancel();this._resolve(!1)},complete:function(a){var b=this;0===b._state&&(b._state=1,b._tick(function(){b._resolve(a)}))},_resolve:function(a){2!==this._state&&(q(this._doneCallbacks,function(b){b(a)}),this._doneCallbacks.length=
|
||||
0,this._state=2)}};return f}]},Ze=function(){this.$get=["$$rAF","$q","$$AnimateRunner",function(a,b,d){return function(b,e){function f(){a(function(){g.addClass&&(b.addClass(g.addClass),g.addClass=null);g.removeClass&&(b.removeClass(g.removeClass),g.removeClass=null);g.to&&(b.css(g.to),g.to=null);h||k.complete();h=!0});return k}var g=e||{};g.$$prepared||(g=pa(g));g.cleanupStyles&&(g.from=g.to=null);g.from&&(b.css(g.from),g.from=null);var h,k=new d;return{start:f,end:f}}}]},ga=N("$compile"),bc=new function(){};
|
||||
Fc.$inject=["$provide","$$sanitizeUriProvider"];Fb.prototype.isFirstChange=function(){return this.previousValue===bc};var Yc=/^((?:x|data)[\:\-_])/i,cg=N("$controller"),cd=/^(\S+)(\s+as\s+([\w$]+))?$/,jf=function(){this.$get=["$document",function(a){return function(b){b?!b.nodeType&&b instanceof F&&(b=b[0]):b=a[0].body;return b.offsetWidth+1}}]},dd="application/json",ec={"Content-Type":dd+";charset=utf-8"},eg=/^\[|^\{(?!\{)/,fg={"[":/]$/,"{":/}$/},dg=/^\)\]\}',?\n/,Gg=N("$http"),hd=function(a){return function(){throw Gg("legacy",
|
||||
a);}},Ka=ca.$interpolateMinErr=N("$interpolate");Ka.throwNoconcat=function(a){throw Ka("noconcat",a);};Ka.interr=function(a,b){return Ka("interr",a,b.toString())};var rf=function(){this.$get=["$window",function(a){function b(a){var b=function(a){b.data=a;b.called=!0};b.id=a;return b}var d=a.angular.callbacks,c={};return{createCallback:function(a){a="_"+(d.$$counter++).toString(36);var f="angular.callbacks."+a,g=b(a);c[f]=d[a]=g;return f},wasCalled:function(a){return c[a].called},getResponse:function(a){return c[a].data},
|
||||
removeCallback:function(a){delete d[c[a].id];delete c[a]}}}]},Hg=/^([^\?#]*)(\?([^#]*))?(#(.*))?$/,hg={http:80,https:443,ftp:21},Gb=N("$location"),Ig={$$absUrl:"",$$html5:!1,$$replace:!1,absUrl:Hb("$$absUrl"),url:function(a){if(y(a))return this.$$url;var b=Hg.exec(a);(b[1]||""===a)&&this.path(decodeURIComponent(b[1]));(b[2]||b[1]||""===a)&&this.search(b[3]||"");this.hash(b[5]||"");return this},protocol:Hb("$$protocol"),host:Hb("$$host"),port:Hb("$$port"),path:md("$$path",function(a){a=null!==a?a.toString():
|
||||
"";return"/"==a.charAt(0)?a:"/"+a}),search:function(a,b){switch(arguments.length){case 0:return this.$$search;case 1:if(G(a)||T(a))a=a.toString(),this.$$search=Ac(a);else if(D(a))a=pa(a,{}),q(a,function(b,c){null==b&&delete a[c]}),this.$$search=a;else throw Gb("isrcharg");break;default:y(b)||null===b?delete this.$$search[a]:this.$$search[a]=b}this.$$compose();return this},hash:md("$$hash",function(a){return null!==a?a.toString():""}),replace:function(){this.$$replace=!0;return this}};q([ld,hc,gc],
|
||||
function(a){a.prototype=Object.create(Ig);a.prototype.state=function(b){if(!arguments.length)return this.$$state;if(a!==gc||!this.$$html5)throw Gb("nostate");this.$$state=y(b)?null:b;return this}});var X=N("$parse"),jg=Function.prototype.call,kg=Function.prototype.apply,lg=Function.prototype.bind,Ob=U();q("+ - * / % === !== == != < > <= >= && || ! = |".split(" "),function(a){Ob[a]=!0});var Jg={n:"\n",f:"\f",r:"\r",t:"\t",v:"\v","'":"'",'"':'"'},jc=function(a){this.options=a};jc.prototype={constructor:jc,
|
||||
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.isIdentifierStart(this.peekMultichar()))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 b=a+this.peek(),d=b+this.peek(2),c=Ob[b],e=Ob[d];Ob[a]||
|
||||
c||e?(a=e?d:c?b: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,b){return-1!==b.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},isIdentifierStart:function(a){return this.options.isIdentifierStart?
|
||||
this.options.isIdentifierStart(a,this.codePointAt(a)):this.isValidIdentifierStart(a)},isValidIdentifierStart:function(a){return"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isIdentifierContinue:function(a){return this.options.isIdentifierContinue?this.options.isIdentifierContinue(a,this.codePointAt(a)):this.isValidIdentifierContinue(a)},isValidIdentifierContinue:function(a,b){return this.isValidIdentifierStart(a,b)||this.isNumber(a)},codePointAt:function(a){return 1===a.length?a.charCodeAt(0):
|
||||
(a.charCodeAt(0)<<10)+a.charCodeAt(1)-56613888},peekMultichar:function(){var a=this.text.charAt(this.index),b=this.peek();if(!b)return a;var d=a.charCodeAt(0),c=b.charCodeAt(0);return 55296<=d&&56319>=d&&56320<=c&&57343>=c?a+b:a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},throwError:function(a,b,d){d=d||this.index;b=w(b)?"s "+b+"-"+this.index+" ["+this.text.substring(b,d)+"]":" "+d;throw X("lexerr",a,b,this.text);},readNumber:function(){for(var a="",b=this.index;this.index<
|
||||
this.text.length;){var d=Q(this.text.charAt(this.index));if("."==d||this.isNumber(d))a+=d;else{var c=this.peek();if("e"==d&&this.isExpOperator(c))a+=d;else if(this.isExpOperator(d)&&c&&this.isNumber(c)&&"e"==a.charAt(a.length-1))a+=d;else if(!this.isExpOperator(d)||c&&this.isNumber(c)||"e"!=a.charAt(a.length-1))break;else this.throwError("Invalid exponent")}this.index++}this.tokens.push({index:b,text:a,constant:!0,value:Number(a)})},readIdent:function(){var a=this.index;for(this.index+=this.peekMultichar().length;this.index<
|
||||
this.text.length;){var b=this.peekMultichar();if(!this.isIdentifierContinue(b))break;this.index+=b.length}this.tokens.push({index:a,text:this.text.slice(a,this.index),identifier:!0})},readString:function(a){var b=this.index;this.index++;for(var d="",c=a,e=!1;this.index<this.text.length;){var f=this.text.charAt(this.index),c=c+f;if(e)"u"===f?(e=this.text.substring(this.index+1,this.index+5),e.match(/[\da-f]{4}/i)||this.throwError("Invalid unicode escape [\\u"+e+"]"),this.index+=4,d+=String.fromCharCode(parseInt(e,
|
||||
16))):d+=Jg[f]||f,e=!1;else if("\\"===f)e=!0;else{if(f===a){this.index++;this.tokens.push({index:b,text:c,constant:!0,value:d});return}d+=f}this.index++}this.throwError("Unterminated quote",b)}};var s=function(a,b){this.lexer=a;this.options=b};s.Program="Program";s.ExpressionStatement="ExpressionStatement";s.AssignmentExpression="AssignmentExpression";s.ConditionalExpression="ConditionalExpression";s.LogicalExpression="LogicalExpression";s.BinaryExpression="BinaryExpression";s.UnaryExpression="UnaryExpression";
|
||||
s.CallExpression="CallExpression";s.MemberExpression="MemberExpression";s.Identifier="Identifier";s.Literal="Literal";s.ArrayExpression="ArrayExpression";s.Property="Property";s.ObjectExpression="ObjectExpression";s.ThisExpression="ThisExpression";s.LocalsExpression="LocalsExpression";s.NGValueParameter="NGValueParameter";s.prototype={ast:function(a){this.text=a;this.tokens=this.lexer.lex(a);a=this.program();0!==this.tokens.length&&this.throwError("is an unexpected token",this.tokens[0]);return a},
|
||||
program:function(){for(var a=[];;)if(0<this.tokens.length&&!this.peek("}",")",";","]")&&a.push(this.expressionStatement()),!this.expect(";"))return{type:s.Program,body:a}},expressionStatement:function(){return{type:s.ExpressionStatement,expression:this.filterChain()}},filterChain:function(){for(var a=this.expression();this.expect("|");)a=this.filter(a);return a},expression:function(){return this.assignment()},assignment:function(){var a=this.ternary();this.expect("=")&&(a={type:s.AssignmentExpression,
|
||||
left:a,right:this.assignment(),operator:"="});return a},ternary:function(){var a=this.logicalOR(),b,d;return this.expect("?")&&(b=this.expression(),this.consume(":"))?(d=this.expression(),{type:s.ConditionalExpression,test:a,alternate:b,consequent:d}):a},logicalOR:function(){for(var a=this.logicalAND();this.expect("||");)a={type:s.LogicalExpression,operator:"||",left:a,right:this.logicalAND()};return a},logicalAND:function(){for(var a=this.equality();this.expect("&&");)a={type:s.LogicalExpression,
|
||||
operator:"&&",left:a,right:this.equality()};return a},equality:function(){for(var a=this.relational(),b;b=this.expect("==","!=","===","!==");)a={type:s.BinaryExpression,operator:b.text,left:a,right:this.relational()};return a},relational:function(){for(var a=this.additive(),b;b=this.expect("<",">","<=",">=");)a={type:s.BinaryExpression,operator:b.text,left:a,right:this.additive()};return a},additive:function(){for(var a=this.multiplicative(),b;b=this.expect("+","-");)a={type:s.BinaryExpression,operator:b.text,
|
||||
left:a,right:this.multiplicative()};return a},multiplicative:function(){for(var a=this.unary(),b;b=this.expect("*","/","%");)a={type:s.BinaryExpression,operator:b.text,left:a,right:this.unary()};return a},unary:function(){var a;return(a=this.expect("+","-","!"))?{type:s.UnaryExpression,operator:a.text,prefix:!0,argument:this.unary()}:this.primary()},primary:function(){var a;this.expect("(")?(a=this.filterChain(),this.consume(")")):this.expect("[")?a=this.arrayDeclaration():this.expect("{")?a=this.object():
|
||||
this.selfReferential.hasOwnProperty(this.peek().text)?a=pa(this.selfReferential[this.consume().text]):this.options.literals.hasOwnProperty(this.peek().text)?a={type:s.Literal,value:this.options.literals[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 b;b=this.expect("(","[",".");)"("===b.text?(a={type:s.CallExpression,callee:a,arguments:this.parseArguments()},this.consume(")")):
|
||||
"["===b.text?(a={type:s.MemberExpression,object:a,property:this.expression(),computed:!0},this.consume("]")):"."===b.text?a={type:s.MemberExpression,object:a,property:this.identifier(),computed:!1}:this.throwError("IMPOSSIBLE");return a},filter:function(a){a=[a];for(var b={type:s.CallExpression,callee:this.identifier(),arguments:a,filter:!0};this.expect(":");)a.push(this.expression());return b},parseArguments:function(){var a=[];if(")"!==this.peekToken().text){do a.push(this.filterChain());while(this.expect(","))
|
||||
}return a},identifier:function(){var a=this.consume();a.identifier||this.throwError("is not a valid identifier",a);return{type:s.Identifier,name:a.text}},constant:function(){return{type:s.Literal,value:this.consume().value}},arrayDeclaration:function(){var a=[];if("]"!==this.peekToken().text){do{if(this.peek("]"))break;a.push(this.expression())}while(this.expect(","))}this.consume("]");return{type:s.ArrayExpression,elements:a}},object:function(){var a=[],b;if("}"!==this.peekToken().text){do{if(this.peek("}"))break;
|
||||
b={type:s.Property,kind:"init"};this.peek().constant?(b.key=this.constant(),b.computed=!1,this.consume(":"),b.value=this.expression()):this.peek().identifier?(b.key=this.identifier(),b.computed=!1,this.peek(":")?(this.consume(":"),b.value=this.expression()):b.value=b.key):this.peek("[")?(this.consume("["),b.key=this.expression(),this.consume("]"),b.computed=!0,this.consume(":"),b.value=this.expression()):this.throwError("invalid key",this.peek());a.push(b)}while(this.expect(","))}this.consume("}");
|
||||
return{type:s.ObjectExpression,properties:a}},throwError:function(a,b){throw X("syntax",b.text,a,b.index+1,this.text,this.text.substring(b.index));},consume:function(a){if(0===this.tokens.length)throw X("ueoe",this.text);var b=this.expect(a);b||this.throwError("is unexpected, expecting ["+a+"]",this.peek());return b},peekToken:function(){if(0===this.tokens.length)throw X("ueoe",this.text);return this.tokens[0]},peek:function(a,b,d,c){return this.peekAhead(0,a,b,d,c)},peekAhead:function(a,b,d,c,e){if(this.tokens.length>
|
||||
a){a=this.tokens[a];var f=a.text;if(f===b||f===d||f===c||f===e||!(b||d||c||e))return a}return!1},expect:function(a,b,d,c){return(a=this.peek(a,b,d,c))?(this.tokens.shift(),a):!1},selfReferential:{"this":{type:s.ThisExpression},$locals:{type:s.LocalsExpression}}};td.prototype={compile:function(a,b){var d=this,c=this.astBuilder.ast(a);this.state={nextId:0,filters:{},expensiveChecks:b,fn:{vars:[],body:[],own:{}},assign:{vars:[],body:[],own:{}},inputs:[]};V(c,d.$filter);var e="",f;this.stage="assign";
|
||||
if(f=rd(c))this.state.computing="assign",e=this.nextId(),this.recurse(f,e),this.return_(e),e="fn.assign="+this.generateFunction("assign","s,v,l");f=pd(c.body);d.stage="inputs";q(f,function(a,b){var c="fn"+b;d.state[c]={vars:[],body:[],own:{}};d.state.computing=c;var e=d.nextId();d.recurse(a,e);d.return_(e);d.state.inputs.push(c);a.watchId=b});this.state.computing="fn";this.stage="main";this.recurse(c);e='"'+this.USE+" "+this.STRICT+'";\n'+this.filterPrefix()+"var fn="+this.generateFunction("fn","s,l,a,i")+
|
||||
e+this.watchFns()+"return fn;";e=(new Function("$filter","ensureSafeMemberName","ensureSafeObject","ensureSafeFunction","getStringValue","ensureSafeAssignContext","ifDefined","plus","text",e))(this.$filter,Sa,ra,nd,ig,Ib,mg,od,a);this.state=this.stage=void 0;e.literal=sd(c);e.constant=c.constant;return e},USE:"use",STRICT:"strict",watchFns:function(){var a=[],b=this.state.inputs,d=this;q(b,function(b){a.push("var "+b+"="+d.generateFunction(b,"s"))});b.length&&a.push("fn.inputs=["+b.join(",")+"];");
|
||||
return a.join("")},generateFunction:function(a,b){return"function("+b+"){"+this.varsPrefix(a)+this.body(a)+"};"},filterPrefix:function(){var a=[],b=this;q(this.state.filters,function(d,c){a.push(d+"=$filter("+b.escape(c)+")")});return a.length?"var "+a.join(",")+";":""},varsPrefix:function(a){return this.state[a].vars.length?"var "+this.state[a].vars.join(",")+";":""},body:function(a){return this.state[a].body.join("")},recurse:function(a,b,d,c,e,f){var g,h,k=this,l,m,n;c=c||A;if(!f&&w(a.watchId))b=
|
||||
b||this.nextId(),this.if_("i",this.lazyAssign(b,this.computedMember("i",a.watchId)),this.lazyRecurse(a,b,d,c,e,!0));else switch(a.type){case s.Program:q(a.body,function(b,c){k.recurse(b.expression,void 0,void 0,function(a){h=a});c!==a.body.length-1?k.current().body.push(h,";"):k.return_(h)});break;case s.Literal:m=this.escape(a.value);this.assign(b,m);c(m);break;case s.UnaryExpression:this.recurse(a.argument,void 0,void 0,function(a){h=a});m=a.operator+"("+this.ifDefined(h,0)+")";this.assign(b,m);
|
||||
c(m);break;case s.BinaryExpression:this.recurse(a.left,void 0,void 0,function(a){g=a});this.recurse(a.right,void 0,void 0,function(a){h=a});m="+"===a.operator?this.plus(g,h):"-"===a.operator?this.ifDefined(g,0)+a.operator+this.ifDefined(h,0):"("+g+")"+a.operator+"("+h+")";this.assign(b,m);c(m);break;case s.LogicalExpression:b=b||this.nextId();k.recurse(a.left,b);k.if_("&&"===a.operator?b:k.not(b),k.lazyRecurse(a.right,b));c(b);break;case s.ConditionalExpression:b=b||this.nextId();k.recurse(a.test,
|
||||
b);k.if_(b,k.lazyRecurse(a.alternate,b),k.lazyRecurse(a.consequent,b));c(b);break;case s.Identifier:b=b||this.nextId();d&&(d.context="inputs"===k.stage?"s":this.assign(this.nextId(),this.getHasOwnProperty("l",a.name)+"?l:s"),d.computed=!1,d.name=a.name);Sa(a.name);k.if_("inputs"===k.stage||k.not(k.getHasOwnProperty("l",a.name)),function(){k.if_("inputs"===k.stage||"s",function(){e&&1!==e&&k.if_(k.not(k.nonComputedMember("s",a.name)),k.lazyAssign(k.nonComputedMember("s",a.name),"{}"));k.assign(b,k.nonComputedMember("s",
|
||||
a.name))})},b&&k.lazyAssign(b,k.nonComputedMember("l",a.name)));(k.state.expensiveChecks||Jb(a.name))&&k.addEnsureSafeObject(b);c(b);break;case s.MemberExpression:g=d&&(d.context=this.nextId())||this.nextId();b=b||this.nextId();k.recurse(a.object,g,void 0,function(){k.if_(k.notNull(g),function(){e&&1!==e&&k.addEnsureSafeAssignContext(g);if(a.computed)h=k.nextId(),k.recurse(a.property,h),k.getStringValue(h),k.addEnsureSafeMemberName(h),e&&1!==e&&k.if_(k.not(k.computedMember(g,h)),k.lazyAssign(k.computedMember(g,
|
||||
h),"{}")),m=k.ensureSafeObject(k.computedMember(g,h)),k.assign(b,m),d&&(d.computed=!0,d.name=h);else{Sa(a.property.name);e&&1!==e&&k.if_(k.not(k.nonComputedMember(g,a.property.name)),k.lazyAssign(k.nonComputedMember(g,a.property.name),"{}"));m=k.nonComputedMember(g,a.property.name);if(k.state.expensiveChecks||Jb(a.property.name))m=k.ensureSafeObject(m);k.assign(b,m);d&&(d.computed=!1,d.name=a.property.name)}},function(){k.assign(b,"undefined")});c(b)},!!e);break;case s.CallExpression:b=b||this.nextId();
|
||||
a.filter?(h=k.filter(a.callee.name),l=[],q(a.arguments,function(a){var b=k.nextId();k.recurse(a,b);l.push(b)}),m=h+"("+l.join(",")+")",k.assign(b,m),c(b)):(h=k.nextId(),g={},l=[],k.recurse(a.callee,h,g,function(){k.if_(k.notNull(h),function(){k.addEnsureSafeFunction(h);q(a.arguments,function(a){k.recurse(a,k.nextId(),void 0,function(a){l.push(k.ensureSafeObject(a))})});g.name?(k.state.expensiveChecks||k.addEnsureSafeObject(g.context),m=k.member(g.context,g.name,g.computed)+"("+l.join(",")+")"):m=
|
||||
h+"("+l.join(",")+")";m=k.ensureSafeObject(m);k.assign(b,m)},function(){k.assign(b,"undefined")});c(b)}));break;case s.AssignmentExpression:h=this.nextId();g={};if(!qd(a.left))throw X("lval");this.recurse(a.left,void 0,g,function(){k.if_(k.notNull(g.context),function(){k.recurse(a.right,h);k.addEnsureSafeObject(k.member(g.context,g.name,g.computed));k.addEnsureSafeAssignContext(g.context);m=k.member(g.context,g.name,g.computed)+a.operator+h;k.assign(b,m);c(b||m)})},1);break;case s.ArrayExpression:l=
|
||||
[];q(a.elements,function(a){k.recurse(a,k.nextId(),void 0,function(a){l.push(a)})});m="["+l.join(",")+"]";this.assign(b,m);c(m);break;case s.ObjectExpression:l=[];n=!1;q(a.properties,function(a){a.computed&&(n=!0)});n?(b=b||this.nextId(),this.assign(b,"{}"),q(a.properties,function(a){a.computed?(g=k.nextId(),k.recurse(a.key,g)):g=a.key.type===s.Identifier?a.key.name:""+a.key.value;h=k.nextId();k.recurse(a.value,h);k.assign(k.member(b,g,a.computed),h)})):(q(a.properties,function(b){k.recurse(b.value,
|
||||
a.constant?void 0:k.nextId(),void 0,function(a){l.push(k.escape(b.key.type===s.Identifier?b.key.name:""+b.key.value)+":"+a)})}),m="{"+l.join(",")+"}",this.assign(b,m));c(b||m);break;case s.ThisExpression:this.assign(b,"s");c("s");break;case s.LocalsExpression:this.assign(b,"l");c("l");break;case s.NGValueParameter:this.assign(b,"v"),c("v")}},getHasOwnProperty:function(a,b){var d=a+"."+b,c=this.current().own;c.hasOwnProperty(d)||(c[d]=this.nextId(!1,a+"&&("+this.escape(b)+" in "+a+")"));return c[d]},
|
||||
assign:function(a,b){if(a)return this.current().body.push(a,"=",b,";"),a},filter:function(a){this.state.filters.hasOwnProperty(a)||(this.state.filters[a]=this.nextId(!0));return this.state.filters[a]},ifDefined:function(a,b){return"ifDefined("+a+","+this.escape(b)+")"},plus:function(a,b){return"plus("+a+","+b+")"},return_:function(a){this.current().body.push("return ",a,";")},if_:function(a,b,d){if(!0===a)b();else{var c=this.current().body;c.push("if(",a,"){");b();c.push("}");d&&(c.push("else{"),
|
||||
d(),c.push("}"))}},not:function(a){return"!("+a+")"},notNull:function(a){return a+"!=null"},nonComputedMember:function(a,b){var d=/[^$_a-zA-Z0-9]/g;return/[$_a-zA-Z][$_a-zA-Z0-9]*/.test(b)?a+"."+b:a+'["'+b.replace(d,this.stringEscapeFn)+'"]'},computedMember:function(a,b){return a+"["+b+"]"},member:function(a,b,d){return d?this.computedMember(a,b):this.nonComputedMember(a,b)},addEnsureSafeObject:function(a){this.current().body.push(this.ensureSafeObject(a),";")},addEnsureSafeMemberName:function(a){this.current().body.push(this.ensureSafeMemberName(a),
|
||||
";")},addEnsureSafeFunction:function(a){this.current().body.push(this.ensureSafeFunction(a),";")},addEnsureSafeAssignContext:function(a){this.current().body.push(this.ensureSafeAssignContext(a),";")},ensureSafeObject:function(a){return"ensureSafeObject("+a+",text)"},ensureSafeMemberName:function(a){return"ensureSafeMemberName("+a+",text)"},ensureSafeFunction:function(a){return"ensureSafeFunction("+a+",text)"},getStringValue:function(a){this.assign(a,"getStringValue("+a+")")},ensureSafeAssignContext:function(a){return"ensureSafeAssignContext("+
|
||||
a+",text)"},lazyRecurse:function(a,b,d,c,e,f){var g=this;return function(){g.recurse(a,b,d,c,e,f)}},lazyAssign:function(a,b){var d=this;return function(){d.assign(a,b)}},stringEscapeRegex:/[^ a-zA-Z0-9]/g,stringEscapeFn:function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)},escape:function(a){if(G(a))return"'"+a.replace(this.stringEscapeRegex,this.stringEscapeFn)+"'";if(T(a))return a.toString();if(!0===a)return"true";if(!1===a)return"false";if(null===a)return"null";if("undefined"===
|
||||
typeof a)return"undefined";throw X("esc");},nextId:function(a,b){var d="v"+this.state.nextId++;a||this.current().vars.push(d+(b?"="+b:""));return d},current:function(){return this.state[this.state.computing]}};ud.prototype={compile:function(a,b){var d=this,c=this.astBuilder.ast(a);this.expression=a;this.expensiveChecks=b;V(c,d.$filter);var e,f;if(e=rd(c))f=this.recurse(e);e=pd(c.body);var g;e&&(g=[],q(e,function(a,b){var c=d.recurse(a);a.input=c;g.push(c);a.watchId=b}));var h=[];q(c.body,function(a){h.push(d.recurse(a.expression))});
|
||||
e=0===c.body.length?A:1===c.body.length?h[0]:function(a,b){var c;q(h,function(d){c=d(a,b)});return c};f&&(e.assign=function(a,b,c){return f(a,c,b)});g&&(e.inputs=g);e.literal=sd(c);e.constant=c.constant;return e},recurse:function(a,b,d){var c,e,f=this,g;if(a.input)return this.inputs(a.input,a.watchId);switch(a.type){case s.Literal:return this.value(a.value,b);case s.UnaryExpression:return e=this.recurse(a.argument),this["unary"+a.operator](e,b);case s.BinaryExpression:return c=this.recurse(a.left),
|
||||
e=this.recurse(a.right),this["binary"+a.operator](c,e,b);case s.LogicalExpression:return c=this.recurse(a.left),e=this.recurse(a.right),this["binary"+a.operator](c,e,b);case s.ConditionalExpression:return this["ternary?:"](this.recurse(a.test),this.recurse(a.alternate),this.recurse(a.consequent),b);case s.Identifier:return Sa(a.name,f.expression),f.identifier(a.name,f.expensiveChecks||Jb(a.name),b,d,f.expression);case s.MemberExpression:return c=this.recurse(a.object,!1,!!d),a.computed||(Sa(a.property.name,
|
||||
f.expression),e=a.property.name),a.computed&&(e=this.recurse(a.property)),a.computed?this.computedMember(c,e,b,d,f.expression):this.nonComputedMember(c,e,f.expensiveChecks,b,d,f.expression);case s.CallExpression:return g=[],q(a.arguments,function(a){g.push(f.recurse(a))}),a.filter&&(e=this.$filter(a.callee.name)),a.filter||(e=this.recurse(a.callee,!0)),a.filter?function(a,c,d,f){for(var n=[],p=0;p<g.length;++p)n.push(g[p](a,c,d,f));a=e.apply(void 0,n,f);return b?{context:void 0,name:void 0,value:a}:
|
||||
a}:function(a,c,d,m){var n=e(a,c,d,m),p;if(null!=n.value){ra(n.context,f.expression);nd(n.value,f.expression);p=[];for(var q=0;q<g.length;++q)p.push(ra(g[q](a,c,d,m),f.expression));p=ra(n.value.apply(n.context,p),f.expression)}return b?{value:p}:p};case s.AssignmentExpression:return c=this.recurse(a.left,!0,1),e=this.recurse(a.right),function(a,d,g,m){var n=c(a,d,g,m);a=e(a,d,g,m);ra(n.value,f.expression);Ib(n.context);n.context[n.name]=a;return b?{value:a}:a};case s.ArrayExpression:return g=[],q(a.elements,
|
||||
function(a){g.push(f.recurse(a))}),function(a,c,d,e){for(var f=[],p=0;p<g.length;++p)f.push(g[p](a,c,d,e));return b?{value:f}:f};case s.ObjectExpression:return g=[],q(a.properties,function(a){a.computed?g.push({key:f.recurse(a.key),computed:!0,value:f.recurse(a.value)}):g.push({key:a.key.type===s.Identifier?a.key.name:""+a.key.value,computed:!1,value:f.recurse(a.value)})}),function(a,c,d,e){for(var f={},p=0;p<g.length;++p)g[p].computed?f[g[p].key(a,c,d,e)]=g[p].value(a,c,d,e):f[g[p].key]=g[p].value(a,
|
||||
c,d,e);return b?{value:f}:f};case s.ThisExpression:return function(a){return b?{value:a}:a};case s.LocalsExpression:return function(a,c){return b?{value:c}:c};case s.NGValueParameter:return function(a,c,d){return b?{value:d}:d}}},"unary+":function(a,b){return function(d,c,e,f){d=a(d,c,e,f);d=w(d)?+d:0;return b?{value:d}:d}},"unary-":function(a,b){return function(d,c,e,f){d=a(d,c,e,f);d=w(d)?-d:0;return b?{value:d}:d}},"unary!":function(a,b){return function(d,c,e,f){d=!a(d,c,e,f);return b?{value:d}:
|
||||
d}},"binary+":function(a,b,d){return function(c,e,f,g){var h=a(c,e,f,g);c=b(c,e,f,g);h=od(h,c);return d?{value:h}:h}},"binary-":function(a,b,d){return function(c,e,f,g){var h=a(c,e,f,g);c=b(c,e,f,g);h=(w(h)?h:0)-(w(c)?c:0);return d?{value:h}:h}},"binary*":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)*b(c,e,f,g);return d?{value:c}:c}},"binary/":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)/b(c,e,f,g);return d?{value:c}:c}},"binary%":function(a,b,d){return function(c,e,f,g){c=a(c,e,
|
||||
f,g)%b(c,e,f,g);return d?{value:c}:c}},"binary===":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)===b(c,e,f,g);return d?{value:c}:c}},"binary!==":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)!==b(c,e,f,g);return d?{value:c}:c}},"binary==":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)==b(c,e,f,g);return d?{value:c}:c}},"binary!=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)!=b(c,e,f,g);return d?{value:c}:c}},"binary<":function(a,b,d){return function(c,e,f,g){c=a(c,e,
|
||||
f,g)<b(c,e,f,g);return d?{value:c}:c}},"binary>":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)>b(c,e,f,g);return d?{value:c}:c}},"binary<=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)<=b(c,e,f,g);return d?{value:c}:c}},"binary>=":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)>=b(c,e,f,g);return d?{value:c}:c}},"binary&&":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)&&b(c,e,f,g);return d?{value:c}:c}},"binary||":function(a,b,d){return function(c,e,f,g){c=a(c,e,f,g)||
|
||||
b(c,e,f,g);return d?{value:c}:c}},"ternary?:":function(a,b,d,c){return function(e,f,g,h){e=a(e,f,g,h)?b(e,f,g,h):d(e,f,g,h);return c?{value:e}:e}},value:function(a,b){return function(){return b?{context:void 0,name:void 0,value:a}:a}},identifier:function(a,b,d,c,e){return function(f,g,h,k){f=g&&a in g?g:f;c&&1!==c&&f&&!f[a]&&(f[a]={});g=f?f[a]:void 0;b&&ra(g,e);return d?{context:f,name:a,value:g}:g}},computedMember:function(a,b,d,c,e){return function(f,g,h,k){var l=a(f,g,h,k),m,n;null!=l&&(m=b(f,
|
||||
g,h,k),m+="",Sa(m,e),c&&1!==c&&(Ib(l),l&&!l[m]&&(l[m]={})),n=l[m],ra(n,e));return d?{context:l,name:m,value:n}:n}},nonComputedMember:function(a,b,d,c,e,f){return function(g,h,k,l){g=a(g,h,k,l);e&&1!==e&&(Ib(g),g&&!g[b]&&(g[b]={}));h=null!=g?g[b]:void 0;(d||Jb(b))&&ra(h,f);return c?{context:g,name:b,value:h}:h}},inputs:function(a,b){return function(d,c,e,f){return f?f[b]:a(d,c,e)}}};var kc=function(a,b,d){this.lexer=a;this.$filter=b;this.options=d;this.ast=new s(a,d);this.astCompiler=d.csp?new ud(this.ast,
|
||||
b):new td(this.ast,b)};kc.prototype={constructor:kc,parse:function(a){return this.astCompiler.compile(a,this.options.expensiveChecks)}};var ng=Object.prototype.valueOf,sa=N("$sce"),la={HTML:"html",CSS:"css",URL:"url",RESOURCE_URL:"resourceUrl",JS:"js"},pg=N("$compile"),$=C.document.createElement("a"),yd=Y(C.location.href);zd.$inject=["$document"];Mc.$inject=["$provide"];var Gd=22,Fd=".",mc="0";Ad.$inject=["$locale"];Cd.$inject=["$locale"];var Ag={yyyy:ba("FullYear",4,0,!1,!0),yy:ba("FullYear",2,0,
|
||||
!0,!0),y:ba("FullYear",1,0,!1,!0),MMMM:kb("Month"),MMM:kb("Month",!0),MM:ba("Month",2,1),M:ba("Month",1,1),LLLL:kb("Month",!1,!0),dd:ba("Date",2),d:ba("Date",1),HH:ba("Hours",2),H:ba("Hours",1),hh:ba("Hours",2,-12),h:ba("Hours",1,-12),mm:ba("Minutes",2),m:ba("Minutes",1),ss:ba("Seconds",2),s:ba("Seconds",1),sss:ba("Milliseconds",3),EEEE:kb("Day"),EEE:kb("Day",!0),a:function(a,b){return 12>a.getHours()?b.AMPMS[0]:b.AMPMS[1]},Z:function(a,b,d){a=-1*d;return a=(0<=a?"+":"")+(Kb(Math[0<a?"floor":"ceil"](a/
|
||||
60),2)+Kb(Math.abs(a%60),2))},ww:Id(2),w:Id(1),G:nc,GG:nc,GGG:nc,GGGG:function(a,b){return 0>=a.getFullYear()?b.ERANAMES[0]:b.ERANAMES[1]}},zg=/((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,yg=/^\-?\d+$/;Bd.$inject=["$locale"];var tg=ha(Q),ug=ha(ub);Dd.$inject=["$parse"];var oe=ha({restrict:"E",compile:function(a,b){if(!b.href&&!b.xlinkHref)return function(a,b){if("a"===b[0].nodeName.toLowerCase()){var e="[object SVGAnimatedString]"===ma.call(b.prop("href"))?
|
||||
"xlink:href":"href";b.on("click",function(a){b.attr(e)||a.preventDefault()})}}}}),vb={};q(Eb,function(a,b){function d(a,d,e){a.$watch(e[c],function(a){e.$set(b,!!a)})}if("multiple"!=a){var c=Aa("ng-"+b),e=d;"checked"===a&&(e=function(a,b,e){e.ngModel!==e[c]&&d(a,b,e)});vb[c]=function(){return{restrict:"A",priority:100,link:e}}}});q(bd,function(a,b){vb[b]=function(){return{priority:100,link:function(a,c,e){if("ngPattern"===b&&"/"==e.ngPattern.charAt(0)&&(c=e.ngPattern.match(Cg))){e.$set("ngPattern",
|
||||
new RegExp(c[1],c[2]));return}a.$watch(e[b],function(a){e.$set(b,a)})}}}});q(["src","srcset","href"],function(a){var b=Aa("ng-"+a);vb[b]=function(){return{priority:99,link:function(d,c,e){var f=a,g=a;"href"===a&&"[object SVGAnimatedString]"===ma.call(c.prop("href"))&&(g="xlinkHref",e.$attr[g]="xlink:href",f=null);e.$observe(b,function(b){b?(e.$set(g,b),Ea&&f&&c.prop(f,e[g])):"href"===a&&e.$set(g,null)})}}}});var Lb={$addControl:A,$$renameControl:function(a,b){a.$name=b},$removeControl:A,$setValidity:A,
|
||||
$setDirty:A,$setPristine:A,$setSubmitted:A};Jd.$inject=["$element","$attrs","$scope","$animate","$interpolate"];var Sd=function(a){return["$timeout","$parse",function(b,d){function c(a){return""===a?d('this[""]').assign:d(a).assign||A}return{name:"form",restrict:a?"EAC":"E",require:["form","^^?form"],controller:Jd,compile:function(d,f){d.addClass(Ua).addClass(ob);var g=f.name?"name":a&&f.ngForm?"ngForm":!1;return{pre:function(a,d,e,f){var n=f[0];if(!("action"in e)){var p=function(b){a.$apply(function(){n.$commitViewValue();
|
||||
n.$setSubmitted()});b.preventDefault()};d[0].addEventListener("submit",p,!1);d.on("$destroy",function(){b(function(){d[0].removeEventListener("submit",p,!1)},0,!1)})}(f[1]||n.$$parentForm).$addControl(n);var q=g?c(n.$name):A;g&&(q(a,n),e.$observe(g,function(b){n.$name!==b&&(q(a,void 0),n.$$parentForm.$$renameControl(n,b),q=c(n.$name),q(a,n))}));d.on("$destroy",function(){n.$$parentForm.$removeControl(n);q(a,void 0);S(n,Lb)})}}}}}]},pe=Sd(),Ce=Sd(!0),Bg=/^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z)$/,
|
||||
Kg=/^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i,Lg=/^(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+\/0-9=?A-Z^_`a-z{|}~]+(\.[-!#$%&'*+\/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$/,Mg=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/,Td=/^(\d{4,})-(\d{2})-(\d{2})$/,Ud=/^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,rc=/^(\d{4,})-W(\d\d)$/,Vd=/^(\d{4,})-(\d\d)$/,
|
||||
Wd=/^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,Ld=U();q(["date","datetime-local","month","time","week"],function(a){Ld[a]=!0});var Xd={text:function(a,b,d,c,e,f){lb(a,b,d,c,e,f);pc(c)},date:mb("date",Td,Nb(Td,["yyyy","MM","dd"]),"yyyy-MM-dd"),"datetime-local":mb("datetimelocal",Ud,Nb(Ud,"yyyy MM dd HH mm ss sss".split(" ")),"yyyy-MM-ddTHH:mm:ss.sss"),time:mb("time",Wd,Nb(Wd,["HH","mm","ss","sss"]),"HH:mm:ss.sss"),week:mb("week",rc,function(a,b){if(da(a))return a;if(G(a)){rc.lastIndex=0;var d=rc.exec(a);
|
||||
if(d){var c=+d[1],e=+d[2],f=d=0,g=0,h=0,k=Hd(c),e=7*(e-1);b&&(d=b.getHours(),f=b.getMinutes(),g=b.getSeconds(),h=b.getMilliseconds());return new Date(c,0,k.getDate()+e,d,f,g,h)}}return NaN},"yyyy-Www"),month:mb("month",Vd,Nb(Vd,["yyyy","MM"]),"yyyy-MM"),number:function(a,b,d,c,e,f){Md(a,b,d,c);lb(a,b,d,c,e,f);c.$$parserName="number";c.$parsers.push(function(a){if(c.$isEmpty(a))return null;if(Mg.test(a))return parseFloat(a)});c.$formatters.push(function(a){if(!c.$isEmpty(a)){if(!T(a))throw nb("numfmt",
|
||||
a);a=a.toString()}return a});if(w(d.min)||d.ngMin){var g;c.$validators.min=function(a){return c.$isEmpty(a)||y(g)||a>=g};d.$observe("min",function(a){w(a)&&!T(a)&&(a=parseFloat(a));g=T(a)&&!isNaN(a)?a:void 0;c.$validate()})}if(w(d.max)||d.ngMax){var h;c.$validators.max=function(a){return c.$isEmpty(a)||y(h)||a<=h};d.$observe("max",function(a){w(a)&&!T(a)&&(a=parseFloat(a));h=T(a)&&!isNaN(a)?a:void 0;c.$validate()})}},url:function(a,b,d,c,e,f){lb(a,b,d,c,e,f);pc(c);c.$$parserName="url";c.$validators.url=
|
||||
function(a,b){var d=a||b;return c.$isEmpty(d)||Kg.test(d)}},email:function(a,b,d,c,e,f){lb(a,b,d,c,e,f);pc(c);c.$$parserName="email";c.$validators.email=function(a,b){var d=a||b;return c.$isEmpty(d)||Lg.test(d)}},radio:function(a,b,d,c){y(d.name)&&b.attr("name",++pb);b.on("click",function(a){b[0].checked&&c.$setViewValue(d.value,a&&a.type)});c.$render=function(){b[0].checked=d.value==c.$viewValue};d.$observe("value",c.$render)},checkbox:function(a,b,d,c,e,f,g,h){var k=Nd(h,a,"ngTrueValue",d.ngTrueValue,
|
||||
!0),l=Nd(h,a,"ngFalseValue",d.ngFalseValue,!1);b.on("click",function(a){c.$setViewValue(b[0].checked,a&&a.type)});c.$render=function(){b[0].checked=c.$viewValue};c.$isEmpty=function(a){return!1===a};c.$formatters.push(function(a){return na(a,k)});c.$parsers.push(function(a){return a?k:l})},hidden:A,button:A,submit:A,reset:A,file:A},Gc=["$browser","$sniffer","$filter","$parse",function(a,b,d,c){return{restrict:"E",require:["?ngModel"],link:{pre:function(e,f,g,h){h[0]&&(Xd[Q(g.type)]||Xd.text)(e,f,
|
||||
g,h[0],b,a,d,c)}}}}],Ng=/^(true|false|\d+)$/,Ue=function(){return{restrict:"A",priority:100,compile:function(a,b){return Ng.test(b.ngValue)?function(a,b,e){e.$set("value",a.$eval(e.ngValue))}:function(a,b,e){a.$watch(e.ngValue,function(a){e.$set("value",a)})}}}},ue=["$compile",function(a){return{restrict:"AC",compile:function(b){a.$$addBindingClass(b);return function(b,c,e){a.$$addBindingInfo(c,e.ngBind);c=c[0];b.$watch(e.ngBind,function(a){c.textContent=y(a)?"":a})}}}}],we=["$interpolate","$compile",
|
||||
function(a,b){return{compile:function(d){b.$$addBindingClass(d);return function(c,d,f){c=a(d.attr(f.$attr.ngBindTemplate));b.$$addBindingInfo(d,c.expressions);d=d[0];f.$observe("ngBindTemplate",function(a){d.textContent=y(a)?"":a})}}}}],ve=["$sce","$parse","$compile",function(a,b,d){return{restrict:"A",compile:function(c,e){var f=b(e.ngBindHtml),g=b(e.ngBindHtml,function(b){return a.valueOf(b)});d.$$addBindingClass(c);return function(b,c,e){d.$$addBindingInfo(c,e.ngBindHtml);b.$watch(g,function(){var d=
|
||||
f(b);c.html(a.getTrustedHtml(d)||"")})}}}}],Te=ha({restrict:"A",require:"ngModel",link:function(a,b,d,c){c.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),xe=qc("",!0),ze=qc("Odd",0),ye=qc("Even",1),Ae=Ta({compile:function(a,b){b.$set("ngCloak",void 0);a.removeClass("ng-cloak")}}),Be=[function(){return{restrict:"A",scope:!0,controller:"@",priority:500}}],Lc={},Og={blur:!0,focus:!0};q("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),
|
||||
function(a){var b=Aa("ng-"+a);Lc[b]=["$parse","$rootScope",function(d,c){return{restrict:"A",compile:function(e,f){var g=d(f[b],null,!0);return function(b,d){d.on(a,function(d){var e=function(){g(b,{$event:d})};Og[a]&&c.$$phase?b.$evalAsync(e):b.$apply(e)})}}}}]});var Ee=["$animate","$compile",function(a,b){return{multiElement:!0,transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(d,c,e,f,g){var h,k,l;d.$watch(e.ngIf,function(d){d?k||g(function(d,f){k=f;d[d.length++]=
|
||||
b.$$createComment("end ngIf",e.ngIf);h={clone:d};a.enter(d,c.parent(),c)}):(l&&(l.remove(),l=null),k&&(k.$destroy(),k=null),h&&(l=tb(h.clone),a.leave(l).then(function(){l=null}),h=null))})}}}],Fe=["$templateRequest","$anchorScroll","$animate",function(a,b,d){return{restrict:"ECA",priority:400,terminal:!0,transclude:"element",controller:ca.noop,compile:function(c,e){var f=e.ngInclude||e.src,g=e.onload||"",h=e.autoscroll;return function(c,e,m,n,p){var q=0,s,B,r,y=function(){B&&(B.remove(),B=null);s&&
|
||||
(s.$destroy(),s=null);r&&(d.leave(r).then(function(){B=null}),B=r,r=null)};c.$watch(f,function(f){var m=function(){!w(h)||h&&!c.$eval(h)||b()},t=++q;f?(a(f,!0).then(function(a){if(!c.$$destroyed&&t===q){var b=c.$new();n.template=a;a=p(b,function(a){y();d.enter(a,null,e).then(m)});s=b;r=a;s.$emit("$includeContentLoaded",f);c.$eval(g)}},function(){c.$$destroyed||t!==q||(y(),c.$emit("$includeContentError",f))}),c.$emit("$includeContentRequested",f)):(y(),n.template=null)})}}}}],We=["$compile",function(a){return{restrict:"ECA",
|
||||
priority:-400,require:"ngInclude",link:function(b,d,c,e){ma.call(d[0]).match(/SVG/)?(d.empty(),a(Oc(e.template,C.document).childNodes)(b,function(a){d.append(a)},{futureParentElement:d})):(d.html(e.template),a(d.contents())(b))}}}],Ge=Ta({priority:450,compile:function(){return{pre:function(a,b,d){a.$eval(d.ngInit)}}}}),Se=function(){return{restrict:"A",priority:100,require:"ngModel",link:function(a,b,d,c){var e=b.attr(d.$attr.ngList)||", ",f="false"!==d.ngTrim,g=f?W(e):e;c.$parsers.push(function(a){if(!y(a)){var b=
|
||||
[];a&&q(a.split(g),function(a){a&&b.push(f?W(a):a)});return b}});c.$formatters.push(function(a){if(L(a))return a.join(e)});c.$isEmpty=function(a){return!a||!a.length}}}},ob="ng-valid",Od="ng-invalid",Ua="ng-pristine",Mb="ng-dirty",Qd="ng-pending",nb=N("ngModel"),Pg=["$scope","$exceptionHandler","$attrs","$element","$parse","$animate","$timeout","$rootScope","$q","$interpolate",function(a,b,d,c,e,f,g,h,k,l){this.$modelValue=this.$viewValue=Number.NaN;this.$$rawModelValue=void 0;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=void 0;this.$name=l(d.name||"",!1)(a);this.$$parentForm=Lb;var m=e(d.ngModel),n=m.assign,p=m,u=n,s=null,B,r=this;this.$$setOptions=function(a){if((r.$options=a)&&a.getterSetter){var b=e(d.ngModel+"()"),f=e(d.ngModel+"($$$p)");p=function(a){var c=m(a);z(c)&&(c=b(a));
|
||||
return c};u=function(a,b){z(m(a))?f(a,{$$$p:b}):n(a,b)}}else if(!m.assign)throw nb("nonassign",d.ngModel,ya(c));};this.$render=A;this.$isEmpty=function(a){return y(a)||""===a||null===a||a!==a};this.$$updateEmptyClasses=function(a){r.$isEmpty(a)?(f.removeClass(c,"ng-not-empty"),f.addClass(c,"ng-empty")):(f.removeClass(c,"ng-empty"),f.addClass(c,"ng-not-empty"))};var J=0;Kd({ctrl:this,$element:c,set:function(a,b){a[b]=!0},unset:function(a,b){delete a[b]},$animate:f});this.$setPristine=function(){r.$dirty=
|
||||
!1;r.$pristine=!0;f.removeClass(c,Mb);f.addClass(c,Ua)};this.$setDirty=function(){r.$dirty=!0;r.$pristine=!1;f.removeClass(c,Ua);f.addClass(c,Mb);r.$$parentForm.$setDirty()};this.$setUntouched=function(){r.$touched=!1;r.$untouched=!0;f.setClass(c,"ng-untouched","ng-touched")};this.$setTouched=function(){r.$touched=!0;r.$untouched=!1;f.setClass(c,"ng-touched","ng-untouched")};this.$rollbackViewValue=function(){g.cancel(s);r.$viewValue=r.$$lastCommittedViewValue;r.$render()};this.$validate=function(){if(!T(r.$modelValue)||
|
||||
!isNaN(r.$modelValue)){var a=r.$$rawModelValue,b=r.$valid,c=r.$modelValue,d=r.$options&&r.$options.allowInvalid;r.$$runValidators(a,r.$$lastCommittedViewValue,function(e){d||b===e||(r.$modelValue=e?a:void 0,r.$modelValue!==c&&r.$$writeModelToScope())})}};this.$$runValidators=function(a,b,c){function d(){var c=!0;q(r.$validators,function(d,e){var g=d(a,b);c=c&&g;f(e,g)});return c?!0:(q(r.$asyncValidators,function(a,b){f(b,null)}),!1)}function e(){var c=[],d=!0;q(r.$asyncValidators,function(e,g){var h=
|
||||
e(a,b);if(!h||!z(h.then))throw nb("nopromise",h);f(g,void 0);c.push(h.then(function(){f(g,!0)},function(){d=!1;f(g,!1)}))});c.length?k.all(c).then(function(){g(d)},A):g(!0)}function f(a,b){h===J&&r.$setValidity(a,b)}function g(a){h===J&&c(a)}J++;var h=J;(function(){var a=r.$$parserName||"parse";if(y(B))f(a,null);else return B||(q(r.$validators,function(a,b){f(b,null)}),q(r.$asyncValidators,function(a,b){f(b,null)})),f(a,B),B;return!0})()?d()?e():g(!1):g(!1)};this.$commitViewValue=function(){var a=
|
||||
r.$viewValue;g.cancel(s);if(r.$$lastCommittedViewValue!==a||""===a&&r.$$hasNativeValidators)r.$$updateEmptyClasses(a),r.$$lastCommittedViewValue=a,r.$pristine&&this.$setDirty(),this.$$parseAndValidate()};this.$$parseAndValidate=function(){var b=r.$$lastCommittedViewValue;if(B=y(b)?void 0:!0)for(var c=0;c<r.$parsers.length;c++)if(b=r.$parsers[c](b),y(b)){B=!1;break}T(r.$modelValue)&&isNaN(r.$modelValue)&&(r.$modelValue=p(a));var d=r.$modelValue,e=r.$options&&r.$options.allowInvalid;r.$$rawModelValue=
|
||||
b;e&&(r.$modelValue=b,r.$modelValue!==d&&r.$$writeModelToScope());r.$$runValidators(b,r.$$lastCommittedViewValue,function(a){e||(r.$modelValue=a?b:void 0,r.$modelValue!==d&&r.$$writeModelToScope())})};this.$$writeModelToScope=function(){u(a,r.$modelValue);q(r.$viewChangeListeners,function(a){try{a()}catch(c){b(c)}})};this.$setViewValue=function(a,b){r.$viewValue=a;r.$options&&!r.$options.updateOnDefault||r.$$debounceViewValueCommit(b)};this.$$debounceViewValueCommit=function(b){var c=0,d=r.$options;
|
||||
d&&w(d.debounce)&&(d=d.debounce,T(d)?c=d:T(d[b])?c=d[b]:T(d["default"])&&(c=d["default"]));g.cancel(s);c?s=g(function(){r.$commitViewValue()},c):h.$$phase?r.$commitViewValue():a.$apply(function(){r.$commitViewValue()})};a.$watch(function(){var b=p(a);if(b!==r.$modelValue&&(r.$modelValue===r.$modelValue||b===b)){r.$modelValue=r.$$rawModelValue=b;B=void 0;for(var c=r.$formatters,d=c.length,e=b;d--;)e=c[d](e);r.$viewValue!==e&&(r.$$updateEmptyClasses(e),r.$viewValue=r.$$lastCommittedViewValue=e,r.$render(),
|
||||
r.$$runValidators(b,e,A))}return b})}],Re=["$rootScope",function(a){return{restrict:"A",require:["ngModel","^?form","^?ngModelOptions"],controller:Pg,priority:1,compile:function(b){b.addClass(Ua).addClass("ng-untouched").addClass(ob);return{pre:function(a,b,e,f){var g=f[0];b=f[1]||g.$$parentForm;g.$$setOptions(f[2]&&f[2].$options);b.$addControl(g);e.$observe("name",function(a){g.$name!==a&&g.$$parentForm.$$renameControl(g,a)});a.$on("$destroy",function(){g.$$parentForm.$removeControl(g)})},post:function(b,
|
||||
c,e,f){var g=f[0];if(g.$options&&g.$options.updateOn)c.on(g.$options.updateOn,function(a){g.$$debounceViewValueCommit(a&&a.type)});c.on("blur",function(){g.$touched||(a.$$phase?b.$evalAsync(g.$setTouched):b.$apply(g.$setTouched))})}}}}}],Qg=/(\s+|^)default(\s+|$)/,Ve=function(){return{restrict:"A",controller:["$scope","$attrs",function(a,b){var d=this;this.$options=pa(a.$eval(b.ngModelOptions));w(this.$options.updateOn)?(this.$options.updateOnDefault=!1,this.$options.updateOn=W(this.$options.updateOn.replace(Qg,
|
||||
function(){d.$options.updateOnDefault=!0;return" "}))):this.$options.updateOnDefault=!0}]}},He=Ta({terminal:!0,priority:1E3}),Rg=N("ngOptions"),Sg=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/,Pe=["$compile","$document","$parse",function(a,b,d){function c(a,b,c){function e(a,b,c,d,f){this.selectValue=a;this.viewValue=
|
||||
b;this.label=c;this.group=d;this.disabled=f}function f(a){var b;if(!q&&ta(a))b=a;else{b=[];for(var c in a)a.hasOwnProperty(c)&&"$"!==c.charAt(0)&&b.push(c)}return b}var n=a.match(Sg);if(!n)throw Rg("iexp",a,ya(b));var p=n[5]||n[7],q=n[6];a=/ as /.test(n[0])&&n[1];var s=n[9];b=d(n[2]?n[1]:p);var w=a&&d(a)||b,r=s&&d(s),y=s?function(a,b){return r(c,b)}:function(a){return Ca(a)},v=function(a,b){return y(a,E(a,b))},A=d(n[2]||n[1]),t=d(n[3]||""),K=d(n[4]||""),z=d(n[8]),H={},E=q?function(a,b){H[q]=b;H[p]=
|
||||
a;return H}:function(a){H[p]=a;return H};return{trackBy:s,getTrackByValue:v,getWatchables:d(z,function(a){var b=[];a=a||[];for(var d=f(a),e=d.length,g=0;g<e;g++){var h=a===d?g:d[g],l=a[h],h=E(l,h),l=y(l,h);b.push(l);if(n[2]||n[1])l=A(c,h),b.push(l);n[4]&&(h=K(c,h),b.push(h))}return b}),getOptions:function(){for(var a=[],b={},d=z(c)||[],g=f(d),h=g.length,n=0;n<h;n++){var p=d===g?n:g[n],q=E(d[p],p),r=w(c,q),p=y(r,q),u=A(c,q),H=t(c,q),q=K(c,q),r=new e(p,r,u,H,q);a.push(r);b[p]=r}return{items:a,selectValueMap:b,
|
||||
getOptionFromViewValue:function(a){return b[v(a)]},getViewValueFromOption:function(a){return s?ca.copy(a.viewValue):a.viewValue}}}}}var e=C.document.createElement("option"),f=C.document.createElement("optgroup");return{restrict:"A",terminal:!0,require:["select","ngModel"],link:{pre:function(a,b,c,d){d[0].registerOption=A},post:function(d,h,k,l){function m(a,b){a.element=b;b.disabled=a.disabled;a.label!==b.label&&(b.label=a.label,b.textContent=a.label);a.value!==b.value&&(b.value=a.selectValue)}function n(){var a=
|
||||
t&&p.readValue();if(t)for(var b=t.items.length-1;0<=b;b--){var c=t.items[b];w(c.group)?Db(c.element.parentNode):Db(c.element)}t=K.getOptions();var d={};v&&h.prepend(B);t.items.forEach(function(a){var b;if(w(a.group)){b=d[a.group];b||(b=f.cloneNode(!1),C.appendChild(b),b.label=null===a.group?"null":a.group,d[a.group]=b);var c=e.cloneNode(!1)}else b=C,c=e.cloneNode(!1);b.appendChild(c);m(a,c)});h[0].appendChild(C);s.$render();s.$isEmpty(a)||(b=p.readValue(),(K.trackBy||y?na(a,b):a===b)||(s.$setViewValue(b),
|
||||
s.$render()))}var p=l[0],s=l[1],y=k.multiple,B;l=0;for(var r=h.children(),A=r.length;l<A;l++)if(""===r[l].value){B=r.eq(l);break}var v=!!B,z=F(e.cloneNode(!1));z.val("?");var t,K=c(k.ngOptions,h,d),C=b[0].createDocumentFragment();y?(s.$isEmpty=function(a){return!a||0===a.length},p.writeValue=function(a){t.items.forEach(function(a){a.element.selected=!1});a&&a.forEach(function(a){if(a=t.getOptionFromViewValue(a))a.element.selected=!0})},p.readValue=function(){var a=h.val()||[],b=[];q(a,function(a){(a=
|
||||
t.selectValueMap[a])&&!a.disabled&&b.push(t.getViewValueFromOption(a))});return b},K.trackBy&&d.$watchCollection(function(){if(L(s.$viewValue))return s.$viewValue.map(function(a){return K.getTrackByValue(a)})},function(){s.$render()})):(p.writeValue=function(a){var b=t.getOptionFromViewValue(a);b?(h[0].value!==b.selectValue&&(z.remove(),v||B.remove(),h[0].value=b.selectValue,b.element.selected=!0),b.element.setAttribute("selected","selected")):null===a||v?(z.remove(),v||h.prepend(B),h.val(""),B.prop("selected",
|
||||
!0),B.attr("selected",!0)):(v||B.remove(),h.prepend(z),h.val("?"),z.prop("selected",!0),z.attr("selected",!0))},p.readValue=function(){var a=t.selectValueMap[h.val()];return a&&!a.disabled?(v||B.remove(),z.remove(),t.getViewValueFromOption(a)):null},K.trackBy&&d.$watch(function(){return K.getTrackByValue(s.$viewValue)},function(){s.$render()}));v?(B.remove(),a(B)(d),B.removeClass("ng-scope")):B=F(e.cloneNode(!1));h.empty();n();d.$watchCollection(K.getWatchables,n)}}}}],Ie=["$locale","$interpolate",
|
||||
"$log",function(a,b,d){var c=/{}/g,e=/^when(Minus)?(.+)$/;return{link:function(f,g,h){function k(a){g.text(a||"")}var l=h.count,m=h.$attr.when&&g.attr(h.$attr.when),n=h.offset||0,p=f.$eval(m)||{},s={},w=b.startSymbol(),B=b.endSymbol(),r=w+l+"-"+n+B,z=ca.noop,v;q(h,function(a,b){var c=e.exec(b);c&&(c=(c[1]?"-":"")+Q(c[2]),p[c]=g.attr(h.$attr[b]))});q(p,function(a,d){s[d]=b(a.replace(c,r))});f.$watch(l,function(b){var c=parseFloat(b),e=isNaN(c);e||c in p||(c=a.pluralCat(c-n));c===v||e&&T(v)&&isNaN(v)||
|
||||
(z(),e=s[c],y(e)?(null!=b&&d.debug("ngPluralize: no rule defined for '"+c+"' in "+m),z=A,k()):z=f.$watch(e,k),v=c)})}}}],Je=["$parse","$animate","$compile",function(a,b,d){var c=N("ngRepeat"),e=function(a,b,c,d,e,m,n){a[c]=d;e&&(a[e]=m);a.$index=b;a.$first=0===b;a.$last=b===n-1;a.$middle=!(a.$first||a.$last);a.$odd=!(a.$even=0===(b&1))};return{restrict:"A",multiElement:!0,transclude:"element",priority:1E3,terminal:!0,$$tlb:!0,compile:function(f,g){var h=g.ngRepeat,k=d.$$createComment("end ngRepeat",
|
||||
h),l=h.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);if(!l)throw c("iexp",h);var m=l[1],n=l[2],p=l[3],s=l[4],l=m.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);if(!l)throw c("iidexp",m);var w=l[3]||l[1],y=l[2];if(p&&(!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(p)||/^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(p)))throw c("badident",p);var r,z,v,A,t={$id:Ca};s?r=a(s):(v=function(a,b){return Ca(b)},
|
||||
A=function(a){return a});return function(a,d,f,g,l){r&&(z=function(b,c,d){y&&(t[y]=b);t[w]=c;t.$index=d;return r(a,t)});var m=U();a.$watchCollection(n,function(f){var g,n,r=d[0],s,u=U(),t,C,F,E,G,D,H;p&&(a[p]=f);if(ta(f))G=f,n=z||v;else for(H in n=z||A,G=[],f)ua.call(f,H)&&"$"!==H.charAt(0)&&G.push(H);t=G.length;H=Array(t);for(g=0;g<t;g++)if(C=f===G?g:G[g],F=f[C],E=n(C,F,g),m[E])D=m[E],delete m[E],u[E]=D,H[g]=D;else{if(u[E])throw q(H,function(a){a&&a.scope&&(m[a.id]=a)}),c("dupes",h,E,F);H[g]={id:E,
|
||||
scope:void 0,clone:void 0};u[E]=!0}for(s in m){D=m[s];E=tb(D.clone);b.leave(E);if(E[0].parentNode)for(g=0,n=E.length;g<n;g++)E[g].$$NG_REMOVED=!0;D.scope.$destroy()}for(g=0;g<t;g++)if(C=f===G?g:G[g],F=f[C],D=H[g],D.scope){s=r;do s=s.nextSibling;while(s&&s.$$NG_REMOVED);D.clone[0]!=s&&b.move(tb(D.clone),null,r);r=D.clone[D.clone.length-1];e(D.scope,g,w,F,y,C,t)}else l(function(a,c){D.scope=c;var d=k.cloneNode(!1);a[a.length++]=d;b.enter(a,null,r);r=d;D.clone=a;u[D.id]=D;e(D.scope,g,w,F,y,C,t)});m=
|
||||
u})}}}}],Ke=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(b,d,c){b.$watch(c.ngShow,function(b){a[b?"removeClass":"addClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],De=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(b,d,c){b.$watch(c.ngHide,function(b){a[b?"addClass":"removeClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],Le=Ta(function(a,b,d){a.$watch(d.ngStyle,function(a,d){d&&a!==d&&q(d,function(a,c){b.css(c,"")});a&&b.css(a)},
|
||||
!0)}),Me=["$animate","$compile",function(a,b){return{require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(d,c,e,f){var g=[],h=[],k=[],l=[],m=function(a,b){return function(){a.splice(b,1)}};d.$watch(e.ngSwitch||e.on,function(c){var d,e;d=0;for(e=k.length;d<e;++d)a.cancel(k[d]);d=k.length=0;for(e=l.length;d<e;++d){var s=tb(h[d].clone);l[d].$destroy();(k[d]=a.leave(s)).then(m(k,d))}h.length=0;l.length=0;(g=f.cases["!"+c]||f.cases["?"])&&q(g,function(c){c.transclude(function(d,
|
||||
e){l.push(e);var f=c.element;d[d.length++]=b.$$createComment("end ngSwitchWhen");h.push({clone:d});a.enter(d,f.parent(),f)})})})}}}],Ne=Ta({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,b,d,c,e){c.cases["!"+d.ngSwitchWhen]=c.cases["!"+d.ngSwitchWhen]||[];c.cases["!"+d.ngSwitchWhen].push({transclude:e,element:b})}}),Oe=Ta({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,b,d,c,e){c.cases["?"]=c.cases["?"]||[];c.cases["?"].push({transclude:e,
|
||||
element:b})}}),Tg=N("ngTransclude"),Qe=["$compile",function(a){return{restrict:"EAC",terminal:!0,compile:function(b){var d=a(b.contents());b.empty();return function(a,b,f,g,h){function k(){d(a,function(a){b.append(a)})}if(!h)throw Tg("orphan",ya(b));f.ngTransclude===f.$attr.ngTransclude&&(f.ngTransclude="");f=f.ngTransclude||f.ngTranscludeSlot;h(function(a,c){a.length?b.append(a):(k(),c.$destroy())},null,f);f&&!h.isSlotFilled(f)&&k()}}}}],qe=["$templateCache",function(a){return{restrict:"E",terminal:!0,
|
||||
compile:function(b,d){"text/ng-template"==d.type&&a.put(d.id,b[0].text)}}}],Ug={$setViewValue:A,$render:A},Vg=["$element","$scope",function(a,b){var d=this,c=new Ra;d.ngModelCtrl=Ug;d.unknownOption=F(C.document.createElement("option"));d.renderUnknownOption=function(b){b="? "+Ca(b)+" ?";d.unknownOption.val(b);a.prepend(d.unknownOption);a.val(b)};b.$on("$destroy",function(){d.renderUnknownOption=A});d.removeUnknownOption=function(){d.unknownOption.parent()&&d.unknownOption.remove()};d.readValue=function(){d.removeUnknownOption();
|
||||
return a.val()};d.writeValue=function(b){d.hasOption(b)?(d.removeUnknownOption(),a.val(b),""===b&&d.emptyOption.prop("selected",!0)):null==b&&d.emptyOption?(d.removeUnknownOption(),a.val("")):d.renderUnknownOption(b)};d.addOption=function(a,b){if(8!==b[0].nodeType){Qa(a,'"option value"');""===a&&(d.emptyOption=b);var g=c.get(a)||0;c.put(a,g+1);d.ngModelCtrl.$render();b[0].hasAttribute("selected")&&(b[0].selected=!0)}};d.removeOption=function(a){var b=c.get(a);b&&(1===b?(c.remove(a),""===a&&(d.emptyOption=
|
||||
void 0)):c.put(a,b-1))};d.hasOption=function(a){return!!c.get(a)};d.registerOption=function(a,b,c,h,k){if(h){var l;c.$observe("value",function(a){w(l)&&d.removeOption(l);l=a;d.addOption(a,b)})}else k?a.$watch(k,function(a,e){c.$set("value",a);e!==a&&d.removeOption(e);d.addOption(a,b)}):d.addOption(c.value,b);b.on("$destroy",function(){d.removeOption(c.value);d.ngModelCtrl.$render()})}}],re=function(){return{restrict:"E",require:["select","?ngModel"],controller:Vg,priority:1,link:{pre:function(a,b,
|
||||
d,c){var e=c[1];if(e){var f=c[0];f.ngModelCtrl=e;b.on("change",function(){a.$apply(function(){e.$setViewValue(f.readValue())})});if(d.multiple){f.readValue=function(){var a=[];q(b.find("option"),function(b){b.selected&&a.push(b.value)});return a};f.writeValue=function(a){var c=new Ra(a);q(b.find("option"),function(a){a.selected=w(c.get(a.value))})};var g,h=NaN;a.$watch(function(){h!==e.$viewValue||na(g,e.$viewValue)||(g=ia(e.$viewValue),e.$render());h=e.$viewValue});e.$isEmpty=function(a){return!a||
|
||||
0===a.length}}}},post:function(a,b,d,c){var e=c[1];if(e){var f=c[0];e.$render=function(){f.writeValue(e.$viewValue)}}}}}},te=["$interpolate",function(a){return{restrict:"E",priority:100,compile:function(b,d){if(w(d.value))var c=a(d.value,!0);else{var e=a(b.text(),!0);e||d.$set("value",b.text())}return function(a,b,d){var k=b.parent();(k=k.data("$selectController")||k.parent().data("$selectController"))&&k.registerOption(a,b,d,c,e)}}}}],se=ha({restrict:"E",terminal:!1}),Ic=function(){return{restrict:"A",
|
||||
require:"?ngModel",link:function(a,b,d,c){c&&(d.required=!0,c.$validators.required=function(a,b){return!d.required||!c.$isEmpty(b)},d.$observe("required",function(){c.$validate()}))}}},Hc=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){if(c){var e,f=d.ngPattern||d.pattern;d.$observe("pattern",function(a){G(a)&&0<a.length&&(a=new RegExp("^"+a+"$"));if(a&&!a.test)throw N("ngPattern")("noregexp",f,a,ya(b));e=a||void 0;c.$validate()});c.$validators.pattern=function(a,b){return c.$isEmpty(b)||
|
||||
y(e)||e.test(b)}}}}},Kc=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){if(c){var e=-1;d.$observe("maxlength",function(a){a=Z(a);e=isNaN(a)?-1:a;c.$validate()});c.$validators.maxlength=function(a,b){return 0>e||c.$isEmpty(b)||b.length<=e}}}}},Jc=function(){return{restrict:"A",require:"?ngModel",link:function(a,b,d,c){if(c){var e=0;d.$observe("minlength",function(a){e=Z(a)||0;c.$validate()});c.$validators.minlength=function(a,b){return c.$isEmpty(b)||b.length>=e}}}}};C.angular.bootstrap?
|
||||
C.console&&console.log("WARNING: Tried to load angular more than once."):(je(),le(ca),ca.module("ngLocale",[],["$provide",function(a){function b(a){a+="";var b=a.indexOf(".");return-1==b?0:a.length-b-1}a.value("$locale",{DATETIME_FORMATS:{AMPMS:["AM","PM"],DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),ERANAMES:["Before Christ","Anno Domini"],ERAS:["BC","AD"],FIRSTDAYOFWEEK:6,MONTH:"January February March April May June July August September October November December".split(" "),
|
||||
SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),STANDALONEMONTH:"January February March April May June July August September October November December".split(" "),WEEKENDRANGE:[5,6],fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",medium:"MMM d, y h:mm:ss a",mediumDate:"MMM d, y",mediumTime:"h:mm:ss a","short":"M/d/yy h:mm a",shortDate:"M/d/yy",shortTime:"h:mm a"},NUMBER_FORMATS:{CURRENCY_SYM:"$",DECIMAL_SEP:".",GROUP_SEP:",",
|
||||
PATTERNS:[{gSize:3,lgSize:3,maxFrac:3,minFrac:0,minInt:1,negPre:"-",negSuf:"",posPre:"",posSuf:""},{gSize:3,lgSize:3,maxFrac:2,minFrac:2,minInt:1,negPre:"-\u00a4",negSuf:"",posPre:"\u00a4",posSuf:""}]},id:"en-us",localeID:"en_US",pluralCat:function(a,c){var e=a|0,f=c;void 0===f&&(f=Math.min(b(a),3));Math.pow(10,f);return 1==e&&0==f?"one":"other"}})}]),F(C.document).ready(function(){fe(C.document,Bc)}))})(window);!window.angular.$$csp().noInlineStyle&&window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');
|
||||
//# sourceMappingURL=angular.min.js.map
|
|
@ -0,0 +1,52 @@
|
|||
var phonecatApp = angular.module('phonecatApp', []);
|
||||
|
||||
// Define the `PhoneListController` controller on the `phonecatApp` module
|
||||
phonecatApp.controller('PhoneListController', function PhoneListController($scope, $http) {
|
||||
$scope.ssids = [];
|
||||
|
||||
// $scope.url = "http://" + location.host.split(":")[0] + ":4000";
|
||||
$scope.url = "http://192.168.24.1:4000";
|
||||
$http.get($scope.url + "/scan").then(function(resp){
|
||||
console.log(resp.data);
|
||||
$scope.ssids = resp.data;
|
||||
}).catch(function(error){
|
||||
console.log("not running on device?");
|
||||
$scope.ssids = ["test_ssid", "meep", "lawl"];
|
||||
})
|
||||
|
||||
$scope.select_ssid = function(ssid){
|
||||
document.getElementById("wifissid").value = ssid;
|
||||
};
|
||||
|
||||
$scope.submit = function(){
|
||||
|
||||
ssid = document.getElementById("wifissid").value;
|
||||
psk = document.getElementById("wifipsk").value;
|
||||
email = document.getElementById("fbemail").value;
|
||||
password = document.getElementById("fbpwd").value;
|
||||
server = document.getElementById("fbserver").value;
|
||||
port = document.getElementById("fbport").value;
|
||||
|
||||
realSrv = "http://" + server + ":" + port;
|
||||
if(ssid != ""){
|
||||
json = {
|
||||
"email": email,
|
||||
"password": password,
|
||||
"server": realSrv,
|
||||
"wifi":{
|
||||
"ssid": ssid,
|
||||
"psk": psk
|
||||
}
|
||||
};
|
||||
console.log(JSON.stringify(json));
|
||||
$http.post($scope.url + "/login", json).then(function(resp){
|
||||
console.log("Should never see this...");
|
||||
}).catch(function(error){
|
||||
console.log("will probably see this a lot...");
|
||||
console.log(JSON.stringify(error));
|
||||
});
|
||||
} else{
|
||||
console.log("write some better error handling n00b.")
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,93 @@
|
|||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
background: linear-gradient(-135deg, #089460, #159ACC);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 50px 15px;
|
||||
max-width: 425px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: Arial;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
font-size: 38px;
|
||||
font-weight: 300;
|
||||
color: #f4f4f4;
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
font-family: Arial;
|
||||
font-size: 18px;
|
||||
color: #f4f4f4;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-family: Arial;
|
||||
color: #f4f4f4;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.configuration-group {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 7px 10px;
|
||||
font-size: 16px;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
margin-bottom: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn {
|
||||
color: #159ACC;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
border-radius: 2px;
|
||||
border: none;
|
||||
padding: 7px 10px;
|
||||
background: #f4f4f4;
|
||||
margin-bottom: 10px;
|
||||
font-family: Arial;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: white;
|
||||
}
|
||||
|
||||
ul {
|
||||
color: #f4f4f4;
|
||||
padding-left: 20px;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
li a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
li a {
|
||||
color: #f4f4f4;
|
||||
text-decoration: none;
|
||||
font-family: Arial;
|
||||
}
|
||||
|
||||
.save-button {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin-top: 50px;
|
||||
font-size: 30px;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" ng-app="phonecatApp">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
<link rel="stylesheet" type="text/css" href="configurator-styles.css">
|
||||
<title>Welcome to Configurator</title>
|
||||
</head>
|
||||
|
||||
<body ng-controller="PhoneListController">
|
||||
<div class="container">
|
||||
<h1>Configure Your Farmbot</h1>
|
||||
<h2>Enter your Wi-Fi and FarmBot Web App credentials</h2>
|
||||
<div class="jumbotron">
|
||||
|
||||
<!-- wifi -->
|
||||
<div class="configuration-group">
|
||||
<h3 class="input-group-addon" id="sizing-addon1">Wi-Fi</h3>
|
||||
<div class="input-group input-group-lg">
|
||||
|
||||
<!-- /btn-group -->
|
||||
<input id="wifissid" type="text" class="form-control" placeholder="Wireless SSID" aria-describedby="sizing-addon1" required="required">
|
||||
<div class="input-group-btn input-group-btn-lg">
|
||||
<!-- Scan button -->
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Scan for Networks
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right">
|
||||
<li ng-repeat=" ssid in ssids "><a href="#" ng-click="select_ssid(ssid)"> {{ssid}} </a></li>
|
||||
</ul>
|
||||
<!-- Scan button -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- /input-group -->
|
||||
|
||||
<input id="wifipsk" type="password" class="form-control" placeholder="Wireless PSK" aria-describedby="sizing-addon1" required="required">
|
||||
</div>
|
||||
<!-- /wifi -->
|
||||
|
||||
<!-- farmbot config-->
|
||||
<div class="configuration-group">
|
||||
<h3 class="input-group-addon" id="sizing-addon1">Web App</h3>
|
||||
<input id="fbemail" type="email" class="form-control" placeholder="Username" aria-describedby="sizing-addon1" required="required">
|
||||
<input id="fbpwd" type="password" class="form-control" placeholder="Password" aria-describedby="sizing-addon1" required="required">
|
||||
<input id="fbserver" type="url" value="my.farmbot.io" placeholder="my.farmbot.io" class="form-control" aria-describedby="sizing-addon1" required="required">
|
||||
<input id="fbport" type="integer" value="80" placeholder="80" class="form-control" aria-describedby="sizing-addon1" required="required">
|
||||
</div>
|
||||
<!-- /farmbot config-->
|
||||
|
||||
<a href="#" ng-click="submit()" class="btn btn-lg form-control save-button"> <strong>Save Configuration</strong> </a>
|
||||
</div>
|
||||
|
||||
</div> <!-- /container -->
|
||||
<script src="jquery-3.1.0.min.js"></script>
|
||||
<script src="angular.min.js"></script>
|
||||
<script src="app/app.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,5 @@
|
|||
use Mix.Config
|
||||
import_config "dev.exs"
|
||||
|
||||
config :json_rpc,
|
||||
transport: FakeMqtt
|
|
@ -1,31 +0,0 @@
|
|||
default: &development
|
||||
adapter: sqlite3
|
||||
database: db/db.sqlite3
|
||||
pool: 5
|
||||
timeout: 5000
|
||||
|
||||
# SQLite version 3.x
|
||||
# gem install sqlite3
|
||||
#
|
||||
# Ensure the SQLite 3 gem is defined in your Gemfile
|
||||
# gem 'sqlite3'
|
||||
development:
|
||||
adapter: sqlite3
|
||||
database: db/db.sqlite3
|
||||
pool: 5
|
||||
timeout: 5000
|
||||
|
||||
# Warning: The database defined as "test" will be erased and
|
||||
# re-generated from your development database when you run "rake".
|
||||
# Do not set this db to the same as development or production.
|
||||
test:
|
||||
adapter: sqlite3
|
||||
database: db/test.sqlite3
|
||||
pool: 5
|
||||
timeout: 5000
|
||||
|
||||
production:
|
||||
adapter: sqlite3
|
||||
database: db/production.sqlite3
|
||||
pool: 5
|
||||
timeout: 5000
|
|
@ -1,32 +0,0 @@
|
|||
class AddScheduleSequenceStep < ActiveRecord::Migration
|
||||
def change
|
||||
create_table :sequences do |t|
|
||||
t.string :name
|
||||
t.timestamps null: false
|
||||
end
|
||||
|
||||
create_table :schedules do |t|
|
||||
t.references :sequence
|
||||
t.string :id_on_web_app
|
||||
t.string :time_unit
|
||||
t.float :repeat
|
||||
t.time :start_time
|
||||
t.time :end_time
|
||||
t.time :next_time
|
||||
t.timestamps null: false
|
||||
end
|
||||
|
||||
create_table :steps do |t|
|
||||
t.string :message_type
|
||||
t.integer :x
|
||||
t.integer :y
|
||||
t.integer :z
|
||||
t.integer :speed
|
||||
t.integer :pin
|
||||
t.integer :value
|
||||
t.integer :mode
|
||||
t.references :sequence
|
||||
t.timestamps null: false
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,7 +0,0 @@
|
|||
class ChangeAllToDatetime < ActiveRecord::Migration
|
||||
def change
|
||||
change_column :schedules, :start_time, :datetime
|
||||
change_column :schedules, :end_time, :datetime
|
||||
change_column :schedules, :next_time, :datetime
|
||||
end
|
||||
end
|
|
@ -1,5 +0,0 @@
|
|||
class AddPositionToStep < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :steps, :position, :integer
|
||||
end
|
||||
end
|
|
@ -1,5 +0,0 @@
|
|||
class ChangeStepsValueFromIntegerToString < ActiveRecord::Migration
|
||||
def change
|
||||
change_column :steps, :value, :text
|
||||
end
|
||||
end
|
|
@ -1,8 +0,0 @@
|
|||
class AddNewIfStatementFieldsToStep < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :steps, :operator, :string
|
||||
add_column :steps, :variable, :string
|
||||
add_column :steps, :sub_sequence_id, :string
|
||||
add_column :sequences, :id_on_web_app, :string
|
||||
end
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
class CreatePlantTable < ActiveRecord::Migration
|
||||
def change
|
||||
create_table :plants do |t|
|
||||
t.string :id_on_web_app
|
||||
t.integer :x
|
||||
t.integer :y
|
||||
end
|
||||
end
|
||||
end
|
59
db/schema.rb
59
db/schema.rb
|
@ -1,59 +0,0 @@
|
|||
# encoding: UTF-8
|
||||
# This file is auto-generated from the current state of the database. Instead
|
||||
# of editing this file, please use the migrations feature of Active Record to
|
||||
# incrementally modify your database, and then regenerate this schema definition.
|
||||
#
|
||||
# Note that this schema.rb definition is the authoritative source for your
|
||||
# database schema. If you need to create the application database on another
|
||||
# system, you should be using db:schema:load, not running all the migrations
|
||||
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
||||
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20151009154651) do
|
||||
|
||||
create_table "plants", force: :cascade do |t|
|
||||
t.string "id_on_web_app"
|
||||
t.integer "x"
|
||||
t.integer "y"
|
||||
end
|
||||
|
||||
create_table "schedules", force: :cascade do |t|
|
||||
t.integer "sequence_id"
|
||||
t.string "time_unit"
|
||||
t.float "repeat"
|
||||
t.datetime "start_time"
|
||||
t.datetime "end_time"
|
||||
t.datetime "next_time"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "id_on_web_app"
|
||||
end
|
||||
|
||||
create_table "sequences", force: :cascade do |t|
|
||||
t.string "name"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "id_on_web_app"
|
||||
end
|
||||
|
||||
create_table "steps", force: :cascade do |t|
|
||||
t.string "message_type"
|
||||
t.integer "x"
|
||||
t.integer "y"
|
||||
t.integer "z"
|
||||
t.integer "speed"
|
||||
t.integer "pin"
|
||||
t.text "value"
|
||||
t.integer "mode"
|
||||
t.integer "sequence_id"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.integer "position"
|
||||
t.string "operator"
|
||||
t.string "variable"
|
||||
t.string "sub_sequence_id"
|
||||
end
|
||||
|
||||
end
|
|
@ -1,7 +0,0 @@
|
|||
# This file should contain all the record creation needed to seed the database with its default values.
|
||||
# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
|
||||
# Mayor.create(name: 'Emanuel', city: cities.first)
|
|
@ -1,16 +0,0 @@
|
|||
# config/application.yml
|
||||
defaults: &defaults
|
||||
secrets_file: 'secrets.txt'
|
||||
mqtt_port: 1883
|
||||
serial_ports:
|
||||
- "/dev/ttyUSB0"
|
||||
- "/dev/ttyACM0"
|
||||
|
||||
development:
|
||||
<<: *defaults
|
||||
|
||||
test:
|
||||
<<: *defaults
|
||||
|
||||
production:
|
||||
<<: *defaults
|
20
farmbot.god
20
farmbot.god
|
@ -1,20 +0,0 @@
|
|||
# god -c farmbot.god -D
|
||||
God.watch do |w|
|
||||
w.name = "farmbot"
|
||||
w.dir = File.expand_path(File.dirname(__FILE__))
|
||||
w.start = "ruby farmbot.rb"
|
||||
|
||||
w.restart_if do |restart|
|
||||
restart.condition(:memory_usage) do |c|
|
||||
c.interval = 5.seconds
|
||||
c.above = 150.megabytes
|
||||
c.times = [4, 5] # 4 out of 5 intervals
|
||||
end
|
||||
|
||||
restart.condition(:cpu_usage) do |c|
|
||||
c.interval = 5.seconds
|
||||
c.above = 50.percent
|
||||
c.times = [4, 5] # 4 out of 5 intervals
|
||||
end
|
||||
end
|
||||
end
|
10
farmbot.rb
10
farmbot.rb
|
@ -1,10 +0,0 @@
|
|||
require_relative 'lib/farmbot-pi'
|
||||
|
||||
begin
|
||||
puts "Connecting..."
|
||||
FarmBotPi.new.start
|
||||
rescue MQTT::NotConnectedException => e
|
||||
puts "OH NOES! Farmbot was disconnected from MQTT. Retrying..."
|
||||
sleep 1
|
||||
retry
|
||||
end
|
|
@ -0,0 +1,102 @@
|
|||
defmodule Auth do
|
||||
@path Application.get_env(:fb, :ro_path)
|
||||
require GenServer
|
||||
require Logger
|
||||
|
||||
@moduledoc """
|
||||
This is a small disaster ever growing into a bigger disaster.
|
||||
"""
|
||||
|
||||
def get_public_key(server) do
|
||||
resp = HTTPotion.get("#{server}/api/public_key")
|
||||
case resp do
|
||||
%HTTPotion.ErrorResponse{message: "enetunreach"} -> get_public_key(server)
|
||||
%HTTPotion.ErrorResponse{message: "econnrefused"} -> {:error, "econnrefused"}
|
||||
_ -> RSA.decode_key(resp.body)
|
||||
end
|
||||
end
|
||||
|
||||
def encrypt(email, pass, server) do
|
||||
# Json to encrypt.
|
||||
json = Poison.encode!(%{"email": email,"password": pass,
|
||||
"id": Nerves.Lib.UUID.generate,"version": 1})
|
||||
secret = String.Chars.to_string(RSA.encrypt(json, {:public, get_public_key(server)}))
|
||||
save_encrypted(secret, server)
|
||||
secret
|
||||
end
|
||||
|
||||
@doc """
|
||||
Saves the secret and the server to disk.
|
||||
"""
|
||||
def save_encrypted(secret, server) do
|
||||
File.write("#{@path}/secretes.txt", :erlang.term_to_binary(%{secret: secret, server: server}))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Loads the secret and server from disk
|
||||
if there is no file returns nil
|
||||
"""
|
||||
def load_encrypted do
|
||||
file = File.read("#{@path}/secretes.txt")
|
||||
case file do
|
||||
{:ok, contents} -> t = :erlang.binary_to_term(contents)
|
||||
get_token(Map.get(t, :secret), Map.get(t, :server))
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
# Gets a token from the API with given secret and server
|
||||
def get_token(secret, server) do
|
||||
payload = Poison.encode!(%{user: %{credentials: :base64.encode_to_string(secret) |> String.Chars.to_string }} )
|
||||
resp = HTTPotion.post "#{server}/api/tokens", [body: payload, headers: ["Content-Type": "application/json"]]
|
||||
case resp do
|
||||
%HTTPotion.ErrorResponse{message: "enetunreach"} -> get_token(secret, server)
|
||||
%HTTPotion.ErrorResponse{message: reason} -> {:error, reason}
|
||||
_ -> Map.get(Poison.decode!(resp.body), "token")
|
||||
end
|
||||
end
|
||||
|
||||
def get_token do
|
||||
GenServer.call(__MODULE__, {:get_token})
|
||||
end
|
||||
|
||||
# Infinite recursion until we have a token.
|
||||
# Not concerned about performance yet because the bot can't do anything yet.
|
||||
def fetch_token do
|
||||
case Auth.get_token do
|
||||
nil -> fetch_token
|
||||
token -> token
|
||||
end
|
||||
end
|
||||
|
||||
# Genserver stuff
|
||||
def init(_args) do
|
||||
{:ok, load_encrypted}
|
||||
end
|
||||
|
||||
def start_link(args) do
|
||||
GenServer.start_link(__MODULE__, args, name: __MODULE__ )
|
||||
end
|
||||
|
||||
# Am i even using this?
|
||||
def login(email,pass,server) when is_bitstring(email)
|
||||
and is_bitstring(pass)
|
||||
and is_bitstring(server) do
|
||||
case Wifi.connected? do
|
||||
true ->
|
||||
GenServer.call(__MODULE__, {:login, email,pass,server})
|
||||
_ ->
|
||||
login(email,pass,server) # Probably process heavy here but im lazy
|
||||
end
|
||||
end
|
||||
|
||||
def handle_call({:login, email,pass,server}, _from, _old_token) do
|
||||
secret = encrypt(email,pass,server)
|
||||
token = get_token(secret, server)
|
||||
{:reply,token,token}
|
||||
end
|
||||
|
||||
def handle_call({:get_token}, _from, token) do
|
||||
{:reply, token, token}
|
||||
end
|
||||
end
|
|
@ -0,0 +1,129 @@
|
|||
defmodule BotCommandHandler do
|
||||
require Logger
|
||||
use GenServer
|
||||
|
||||
@moduledoc """
|
||||
This is the log mechanism for bot commands.
|
||||
"""
|
||||
|
||||
def init(_args) do
|
||||
{:ok, pid} = GenEvent.start_link
|
||||
GenEvent.add_handler(pid, BotCommandManager, [])
|
||||
spawn fn -> get_events(pid) end
|
||||
{:ok, pid}
|
||||
end
|
||||
|
||||
def start_link(args) do
|
||||
GenServer.start_link(__MODULE__, args, name: __MODULE__)
|
||||
end
|
||||
|
||||
def handle_cast({:add_event, event}, pid) do
|
||||
GenEvent.notify(pid, event)
|
||||
{:noreply, pid}
|
||||
end
|
||||
|
||||
def handle_call(:get_pid, _from, pid) do
|
||||
{:reply, pid, pid}
|
||||
end
|
||||
|
||||
def handle_call(:e_stop, _from, pid) do
|
||||
GenEvent.notify(pid, :e_stop)
|
||||
{:reply, :ok, pid}
|
||||
end
|
||||
|
||||
def get_pid do
|
||||
GenServer.call(__MODULE__, :get_pid, 5000)
|
||||
end
|
||||
|
||||
def e_stop do
|
||||
GenServer.call(__MODULE__, :e_stop, 5000)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets events from the GenEvent server (pid)
|
||||
"""
|
||||
def get_events(pid) do
|
||||
events = GenEvent.call(pid, BotCommandManager, :events)
|
||||
for event <- events do
|
||||
Process.sleep(150)
|
||||
check_busy
|
||||
BotStatus.busy true
|
||||
do_handle(event)
|
||||
Process.sleep(50)
|
||||
RPCMessageHandler.send_status
|
||||
end
|
||||
get_events(pid)
|
||||
end
|
||||
|
||||
defp check_busy do
|
||||
case BotStatus.busy? do
|
||||
true -> check_busy
|
||||
false -> :ok
|
||||
end
|
||||
end
|
||||
|
||||
def notify(event) do
|
||||
GenServer.cast(__MODULE__, {:add_event, event})
|
||||
end
|
||||
|
||||
###
|
||||
## This might not be the best implementation.
|
||||
## PROBLEM: There is a small chance for the bot to be out of sync from the
|
||||
## Arduino.
|
||||
##
|
||||
## EXAMPLE: If Farmbot recieves a command and sends it properly over UART
|
||||
## but the Arduino decides for any reason to not accept that command,
|
||||
## Farmbot and the frontend think the bot is at one position
|
||||
## and the actual bot is at a different position.
|
||||
###
|
||||
|
||||
defp do_handle({:home_x, {speed}}) do
|
||||
Logger.info("HOME X")
|
||||
SerialMessageManager.sync_notify( {:send, "F11"} )
|
||||
end
|
||||
|
||||
defp do_handle({:home_y, {speed}}) do
|
||||
Logger.info("HOME Y")
|
||||
SerialMessageManager.sync_notify( {:send, "F12"} )
|
||||
end
|
||||
|
||||
defp do_handle({:home_z, {speed}}) do
|
||||
Logger.info("HOME Z")
|
||||
SerialMessageManager.sync_notify( {:send, "F13"} )
|
||||
end
|
||||
|
||||
# These need to be "safe" commands. IE they shouldnt crash anythin.
|
||||
defp do_handle({:write_pin, {pin, value, mode}}) do
|
||||
Logger.info("WRITE_PIN " <> "F41 P#{pin} V#{value} M#{mode}")
|
||||
SerialMessageManager.sync_notify( {:send, "F41 P#{pin} V#{value} M#{mode}"} )
|
||||
end
|
||||
|
||||
defp do_handle({:move_absolute, {x,y,z,_s}}) do
|
||||
Logger.info("MOVE_ABSOLUTE " <> "G00 X#{x} Y#{y} Z#{z}")
|
||||
SerialMessageManager.sync_notify( {:send, "G00 X#{x} Y#{y} Z#{z}"} )
|
||||
end
|
||||
|
||||
defp do_handle({:read_param, param}) do
|
||||
Logger.info("READ_PARAM "<> "#{param}")
|
||||
SerialMessageManager.sync_notify({:send, "F21 P#{param}" })
|
||||
end
|
||||
|
||||
defp do_handle({:read_pin, {pin, mode}}) do
|
||||
Logger.info("READ PIN "<> "#{pin}")
|
||||
SerialMessageManager.sync_notify({:send, "F42 P#{pin} M#{mode}" })
|
||||
end
|
||||
|
||||
defp do_handle({:update_param, {param, value}}) do
|
||||
Logger.info("UPDATE PARAM " <> "#{param} #{value}")
|
||||
SerialMessageManager.sync_notify({:send, "F22 P#{param} V#{value}"})
|
||||
end
|
||||
|
||||
defp do_handle({method, params}) do
|
||||
Logger.debug("Unhandled method: #{inspect method} with params: #{inspect params}")
|
||||
end
|
||||
|
||||
# Unhandled event. Probably not implemented if it got this far.
|
||||
defp do_handle(event) do
|
||||
Logger.debug("[Command Handler] (Probably not implemented) Unhandled Event: #{inspect event}")
|
||||
end
|
||||
end
|
|
@ -0,0 +1,24 @@
|
|||
defmodule BotCommandManager do
|
||||
use GenEvent
|
||||
require Logger
|
||||
|
||||
def start_link() do
|
||||
GenEvent.start_link([])
|
||||
end
|
||||
|
||||
# add event to the log
|
||||
def handle_event({event, params}, events) do
|
||||
{:ok, [{event, params} | events]}
|
||||
end
|
||||
|
||||
def handle_event(:e_stop, _events) do
|
||||
# Destroy the event queue
|
||||
{:ok, []}
|
||||
end
|
||||
|
||||
# dump teh current events to be handled
|
||||
def handle_call(:events, events) do
|
||||
{:ok, Enum.reverse(events), []}
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
defmodule BotCommandSupervisor do
|
||||
@moduledoc """
|
||||
Supervises Bot Commands
|
||||
"""
|
||||
def start_link(_) do
|
||||
import Supervisor.Spec
|
||||
children = [
|
||||
worker(BotCommandHandler, [[]])
|
||||
]
|
||||
Supervisor.start_link(children, strategy: :one_for_one, name: __MODULE__)
|
||||
end
|
||||
|
||||
def init(_) do
|
||||
{:ok, %{}}
|
||||
end
|
||||
end
|
|
@ -0,0 +1,162 @@
|
|||
defmodule Command do
|
||||
require Logger
|
||||
@moduledoc """
|
||||
BotCommands.
|
||||
"""
|
||||
|
||||
@doc """
|
||||
EMERGENCY STOP
|
||||
"""
|
||||
def e_stop do
|
||||
UartHandler.send("E")
|
||||
Process.exit(Process.whereis(BotCommandHandler), :kill)
|
||||
:ok
|
||||
end
|
||||
|
||||
@doc """
|
||||
Home All (TODO: this might be broken)
|
||||
"""
|
||||
def home_all(speed) do
|
||||
Logger.info("HOME ALL")
|
||||
# SerialMessageManager.sync_notify( {:send, "G28"} )
|
||||
Command.move_absolute(0, 0, 0, speed)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Home x
|
||||
I dont think anything uses these.
|
||||
"""
|
||||
def home_x(speed) do
|
||||
BotCommandHandler.notify({:home_x, speed})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Home y
|
||||
"""
|
||||
def home_y(speed) do
|
||||
BotCommandHandler.notify({:home_y, speed})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Home z
|
||||
"""
|
||||
def home_z(speed) do
|
||||
BotCommandHandler.notify({:home_z, speed})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Writes a pin high or low
|
||||
"""
|
||||
def write_pin(pin, value, mode \\ "1")
|
||||
def write_pin(pin, value, mode) do
|
||||
BotStatus.set_pin(pin, value)
|
||||
BotCommandHandler.notify({:write_pin, {pin, value, mode}})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Moves to (x,y,z) point.
|
||||
Sets the bot status to given coords
|
||||
replies to the mqtt message that caused it (if one exists)
|
||||
adds the move to the command queue.
|
||||
"""
|
||||
def move_absolute(x \\ 0,y \\ 0,z \\ 0,s \\ 100)
|
||||
def move_absolute(x, y, z, s) when x >= 0 and y >= 0 do
|
||||
BotStatus.set_pos(x,y,z)
|
||||
BotCommandHandler.notify({:move_absolute, {x,y,z,s}})
|
||||
end
|
||||
|
||||
# When both x and y are negative
|
||||
def move_absolute(x, y, z, s) when x < 0 and y < 0 do
|
||||
BotStatus.set_pos(0,0,z)
|
||||
BotCommandHandler.notify({:move_absolute, {0,0,z,s}})
|
||||
end
|
||||
|
||||
# when x is negative
|
||||
def move_absolute(x, y, z, s) when x < 0 do
|
||||
BotStatus.set_pos(0,y,z)
|
||||
BotCommandHandler.notify({:move_absolute, {0,y,z,s}})
|
||||
end
|
||||
|
||||
# when y is negative
|
||||
def move_absolute(x, y, z, s ) when y < 0 do
|
||||
BotStatus.set_pos(x,0,z)
|
||||
BotCommandHandler.notify({:move_absolute, {x,0,z,s}})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the current position
|
||||
then pipes into move_absolute
|
||||
"""
|
||||
def move_relative(e)
|
||||
def move_relative({:x, s, move_by}) when is_integer move_by do
|
||||
[x,y,z] = BotStatus.get_current_pos
|
||||
move_absolute(x + move_by,y,z,s)
|
||||
end
|
||||
|
||||
def move_relative({:y, s, move_by}) when is_integer move_by do
|
||||
[x,y,z] = BotStatus.get_current_pos
|
||||
move_absolute(x,y + move_by,z,s)
|
||||
end
|
||||
|
||||
def move_relative({:z, s, move_by}) when is_integer move_by do
|
||||
[x,y,z] = BotStatus.get_current_pos
|
||||
move_absolute(x,y,z + move_by,s)
|
||||
end
|
||||
|
||||
# This is a funky one. Only used in sequences right now.
|
||||
def move_relative(%{x: x_move_by, y: y_move_by, z: z_move_by, speed: speed})
|
||||
when is_integer x_move_by and
|
||||
is_integer y_move_by and
|
||||
is_integer z_move_by
|
||||
do
|
||||
[x,y,z] = BotStatus.get_current_pos
|
||||
move_absolute(x + x_move_by,y + y_move_by,z + z_move_by, speed)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Used when bootstrapping the bot.
|
||||
Reads pins 0-13 in digital mode.
|
||||
"""
|
||||
def read_all_pins do
|
||||
spawn fn -> Enum.each(0..13, fn pin -> Command.read_pin(pin) end) end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Used when bootstrapping the bot.
|
||||
Reads all the params.
|
||||
"""
|
||||
def read_all_params do
|
||||
rel_params = [0,11,12,13,21,22,23,
|
||||
31,32,33,41,42,43,51,
|
||||
52,53,61,62,63,71,72,73]
|
||||
spawn fn -> Enum.each(rel_params, fn param -> Command.read_param(param) end ) end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Reads a pin value.
|
||||
mode: 1 = digital.
|
||||
mode: 0 = analog.
|
||||
"""
|
||||
def read_pin(pin, mode \\ 0) do
|
||||
BotCommandHandler.notify({:read_pin, {pin, mode} })
|
||||
end
|
||||
|
||||
@doc """
|
||||
Reads a param. Needs the integer version of said param.
|
||||
"""
|
||||
def read_param(param) when is_integer param do
|
||||
BotCommandHandler.notify({:read_param, param })
|
||||
end
|
||||
|
||||
@doc """
|
||||
Update a param. Param needs to be an integer.
|
||||
"""
|
||||
def update_param(param, value) when is_integer param do
|
||||
BotCommandHandler.notify({:update_param, {param, value} })
|
||||
Command.read_param(param)
|
||||
end
|
||||
|
||||
def update_param(nil, _value) do
|
||||
{:error, "Unknown param"}
|
||||
end
|
||||
end
|
|
@ -1,59 +0,0 @@
|
|||
require_relative 'telemetry_message'
|
||||
|
||||
module FBPi
|
||||
# This class wraps around the FB::Arduino class (farmbot-serial gem) to add
|
||||
# extra functionality that is application specific / not available in
|
||||
# farmbot-serial.
|
||||
class BotDecorator < SimpleDelegator
|
||||
attr_accessor :status_storage, :mesh, :rest_client
|
||||
|
||||
def self.build(status_storage,
|
||||
mesh,
|
||||
rest,
|
||||
arduino_klass = FB::Arduino,
|
||||
serial_obj = default_serial_object)
|
||||
bot = self.new(arduino_klass.new(serial_port: serial_obj))
|
||||
bot.status_storage, bot.mesh, bot.rest_client = status_storage, mesh, rest
|
||||
bot
|
||||
end
|
||||
|
||||
def self.default_serial_object
|
||||
begin
|
||||
return FB::DefaultSerialPort.new(serial_port)
|
||||
rescue TypeError => e
|
||||
raise "\n\nSomething went wrong while connecting to the arduino."\
|
||||
" Usually, this is a sign that the USB cable is broke or unplugged.\n"
|
||||
end
|
||||
end
|
||||
|
||||
def self.serial_port
|
||||
FBPi::Settings.serial_ports.detect { |f| File.exists?(f) }
|
||||
end
|
||||
|
||||
def bootstrap
|
||||
ColdStart.run!(bot: self)
|
||||
end
|
||||
|
||||
# Called when the socket connection is ready. Go nuts!
|
||||
def ready
|
||||
log "Online at #{Time.now}"
|
||||
end
|
||||
|
||||
def log(message, priority = 'low')
|
||||
mesh.emit '*', id: nil, result: TelemetryMessage.build(message.to_s, status: self.status.to_h)
|
||||
end
|
||||
|
||||
def emit_changes
|
||||
mesh.emit '*', id: nil, result: FBPi::ReportBotStatus.run!(bot: self)
|
||||
end
|
||||
|
||||
# This method violates intergalactic law.
|
||||
def template_variables
|
||||
status
|
||||
.to_h
|
||||
.merge('time' => Time.now.to_i)
|
||||
.reduce({}){ |a, (b, c)| a[b.to_s.downcase] = c; a}
|
||||
.tap { |h| h['pins'].map { |k, v| h["pin#{k}"] = v.to_s } }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,151 @@
|
|||
defmodule BotStatus do
|
||||
use GenServer
|
||||
require Logger
|
||||
def init(_) do
|
||||
# MAKE SURE THE STATUS STAYS FLAT. YOU WILL THANK YOURSELF LATER.
|
||||
initial_status = %{ x: 0,y: 0,z: 0,speed: 10,
|
||||
version: Fw.version,
|
||||
busy: true,
|
||||
last_sync: -1,
|
||||
movement_axis_nr_steps_x: 222,
|
||||
movement_axis_nr_steps_y: 222,
|
||||
movement_axis_nr_steps_z: 222 }
|
||||
{ :ok, initial_status }
|
||||
end
|
||||
|
||||
def start_link(_) do
|
||||
GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
|
||||
end
|
||||
|
||||
def get_status do
|
||||
GenServer.call(__MODULE__, {:get_status}, 90000)
|
||||
end
|
||||
|
||||
def handle_call({:get_status}, _from, current_status) do
|
||||
{:reply, current_status, current_status}
|
||||
end
|
||||
|
||||
def handle_call({:get_busy}, _from, current_status ) do
|
||||
{:reply, current_status.busy, current_status}
|
||||
end
|
||||
|
||||
def handle_call(:get_controller_version, _from, current_status) do
|
||||
{:reply, current_status.version, current_status }
|
||||
end
|
||||
|
||||
def handle_call({:get_pin, pin}, _from, current_status) when is_bitstring(pin) do
|
||||
pin = Map.get(current_status, "pin"<>pin, -1)
|
||||
{:reply, pin, current_status}
|
||||
end
|
||||
|
||||
def handle_cast({:set_pin, pin, value}, current_status) when is_bitstring(pin) and is_integer(value) do
|
||||
{:noreply, Map.put(current_status, "pin"<>pin, value)}
|
||||
end
|
||||
|
||||
def handle_cast({:set_param, param, value}, current_status)
|
||||
when is_bitstring(param) and
|
||||
is_integer(value)
|
||||
do
|
||||
{:noreply, Map.put(current_status, param, value)}
|
||||
end
|
||||
|
||||
def handle_cast({:set_busy, b}, current_status ) when is_boolean b do
|
||||
{:noreply, Map.put(current_status, :busy, b)}
|
||||
end
|
||||
|
||||
def handle_cast({:set_pos, x,y,z}, current_status)
|
||||
when is_integer x and
|
||||
is_integer y and
|
||||
is_integer z do
|
||||
new_status = Map.put(current_status, :x, x)
|
||||
|> Map.put(:y, y)
|
||||
|> Map.put(:z, z)
|
||||
{:noreply, new_status}
|
||||
end
|
||||
|
||||
def handle_cast({:set_end_stop, _stop, _value}, current_status) do
|
||||
#TODO: this?
|
||||
# Logger.debug("EndStop reporting is TODO")
|
||||
{:noreply, current_status}
|
||||
end
|
||||
|
||||
# Sets the pin value in the bot's status
|
||||
def set_pin(pin, value) when is_integer pin and is_integer value do
|
||||
GenServer.cast(__MODULE__, {:set_pin, Integer.to_string(pin), value})
|
||||
end
|
||||
|
||||
def set_param(param, value) when is_bitstring(param)
|
||||
and is_integer(value) do
|
||||
GenServer.cast(__MODULE__, {:set_param, param, value})
|
||||
end
|
||||
|
||||
def set_param(param, value) when is_bitstring(param)
|
||||
and is_bitstring(value) do
|
||||
set_param(param, String.to_integer(value))
|
||||
end
|
||||
|
||||
# Gets the pin value from the bot's status
|
||||
def get_pin(pin) when is_integer pin do
|
||||
GenServer.call(__MODULE__, {:get_pin, Integer.to_string(pin)})
|
||||
end
|
||||
|
||||
# Sets busy to true or false.
|
||||
def busy(b) when is_boolean b do
|
||||
GenServer.cast(__MODULE__, {:set_busy, b})
|
||||
end
|
||||
|
||||
# Gets busy
|
||||
def busy? do
|
||||
GenServer.call(__MODULE__, {:get_busy})
|
||||
end
|
||||
|
||||
# All three coords
|
||||
def set_pos(x,y,z)
|
||||
when is_integer x and
|
||||
is_integer y and
|
||||
is_integer z
|
||||
do
|
||||
GenServer.cast(__MODULE__, {:set_pos, x,y,z})
|
||||
end
|
||||
|
||||
# If we only have one coord, get the current pos of the others first.
|
||||
def set_pos({:x, x}) when is_integer(x) do
|
||||
[_x,y,z] = get_current_pos
|
||||
set_pos(x,y,z)
|
||||
end
|
||||
|
||||
def set_pos({:y, y}) when is_integer(y) do
|
||||
[x,_y,z] = get_current_pos
|
||||
set_pos(x,y,z)
|
||||
end
|
||||
|
||||
def set_pos({:z, z}) when is_integer(z) do
|
||||
[x,y,_z] = get_current_pos
|
||||
set_pos(x,y,z)
|
||||
end
|
||||
|
||||
def set_end_stop({stop, value}) do
|
||||
#TODO
|
||||
GenServer.cast(__MODULE__, {:set_end_stop, stop, value})
|
||||
end
|
||||
|
||||
# Get current coords
|
||||
def get_current_pos do
|
||||
Enum.map([:x,:y,:z], fn(coord) ->
|
||||
Map.get(get_status, coord)
|
||||
end)
|
||||
end
|
||||
|
||||
def get_current_version do
|
||||
GenServer.call(__MODULE__, :get_controller_version)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the current value of a param
|
||||
"""
|
||||
def get_param(param) when is_integer(param) do
|
||||
cur_status = BotStatus.get_status
|
||||
this_param = Gcode.parse_param(param) |> Atom.to_string |> String.Casing.downcase
|
||||
Map.get(cur_status, this_param)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,48 @@
|
|||
defmodule BotSync do
|
||||
use GenServer
|
||||
require Logger
|
||||
def init(_args) do
|
||||
{:ok, %{token: token, server: server, sequences: [] }}
|
||||
end
|
||||
|
||||
def start_link(args) do
|
||||
GenServer.start_link(__MODULE__, args, name: __MODULE__)
|
||||
end
|
||||
|
||||
def handle_cast(:sync, state) do
|
||||
resp = HTTPotion.get "#{server}/api/sequences",
|
||||
[headers: ["Content-Type": "application/json",
|
||||
"Authorization": "Bearer" <> Map.get(state.token, "encoded")]]
|
||||
case resp do
|
||||
%HTTPotion.ErrorResponse{message: reason} ->
|
||||
Logger.debug("Error Fetching sequences: #{inspect reason}")
|
||||
{:noreply, Map.update(state, :sequences, [], fn _cur -> [] end)}
|
||||
_ ->
|
||||
sequences = Poison.decode!(resp.body)
|
||||
Logger.debug("Successfully Fetched Sequences")
|
||||
{:noreply, Map.update(state, :sequences, [], fn _cur -> sequences end)}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_call(:sequences, _from, state) do
|
||||
{:reply, Map.get(state, :sequences), state}
|
||||
end
|
||||
|
||||
def sync do
|
||||
GenServer.cast(__MODULE__, :sync)
|
||||
end
|
||||
|
||||
def sequences do
|
||||
GenServer.call(__MODULE__, :sequences)
|
||||
end
|
||||
|
||||
defp token do
|
||||
Auth.fetch_token
|
||||
end
|
||||
|
||||
defp server do
|
||||
Map.get(token, "unencoded")
|
||||
|> Map.get("iss")
|
||||
end
|
||||
|
||||
end
|
|
@ -1,33 +0,0 @@
|
|||
require_relative 'schedule_chore'
|
||||
module FBPi
|
||||
# A recurring task that happens every ::INTERVAL seconds. Typically used for
|
||||
# pulling scheduled sequences out of the database and executing them when the
|
||||
# time is due. Gets fired off by an EventMachine::PeriodicTimer.
|
||||
class ChoreRunner
|
||||
INTERVAL = 60 # seconds
|
||||
attr_accessor :schedules, :bot
|
||||
|
||||
def initialize(bot)
|
||||
@bot = bot
|
||||
end
|
||||
|
||||
def run
|
||||
schedules.present? ? something : nothing
|
||||
end
|
||||
|
||||
def something
|
||||
schedules.each { |s| ScheduleChore.run(s, bot) }
|
||||
end
|
||||
|
||||
def nothing
|
||||
bot.log "Nothing to run this cycle. Waiting an additional #{INTERVAL}"\
|
||||
" secs."
|
||||
end
|
||||
|
||||
def schedules
|
||||
# TODO re-write this as an actual SQL query. This does extra SQL queries.
|
||||
@schedules ||= ( Schedule.where(next_time: nil).to_a + # New
|
||||
Schedule.where("next_time < ?", Time.now).to_a )# Overdue
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,48 +0,0 @@
|
|||
require 'ice_cube'
|
||||
module FBPi
|
||||
# Command object that uses a schedule, bot and time object to determine if a
|
||||
# schedule should be:
|
||||
# 1. performed 2. destroyed (if no occurences left) 3. Re-scheduled (if
|
||||
# there are still occurences left on the schedule)
|
||||
class ScheduleChore
|
||||
attr_accessor :schedule, :bot, :now, :next_time
|
||||
|
||||
def initialize(sched, bot, now = Time.now)
|
||||
@schedule, @bot, @now = sched, bot, now
|
||||
end
|
||||
|
||||
def self.run(sched, bot)
|
||||
self.new(sched, bot).run
|
||||
end
|
||||
|
||||
def run
|
||||
if expired?
|
||||
schedule.destroy!
|
||||
else
|
||||
perform_steps
|
||||
bump_execution_time
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def perform_steps
|
||||
bot.log "Running #{schedule.sequence.name}"
|
||||
schedule.sequence.steps.each { |s| s.execute(bot) }
|
||||
end
|
||||
|
||||
def bump_execution_time
|
||||
schedule.update_attributes(next_time: next_time)
|
||||
end
|
||||
|
||||
def expired?
|
||||
schedule.end_time < now
|
||||
end
|
||||
|
||||
def next_time
|
||||
@next_time ||= (
|
||||
rule = IceCube::Rule.send(schedule.time_unit, schedule.repeat)
|
||||
sched = IceCube::Schedule.new(now) { |o| o.add_recurrence_rule(rule) }
|
||||
sched.next_occurrence.to_time )
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,45 +0,0 @@
|
|||
|
||||
require 'mutations'
|
||||
require 'securerandom'
|
||||
|
||||
module FBPi
|
||||
# Builds a message suitable for transmission over MeshBlu messaging system.
|
||||
# Message objects are mostly compatible with the JSONRPC protocol, except that
|
||||
# they have additional information added to the JSON object related to MeshBlu
|
||||
# such as "fromUuid". SEE:
|
||||
# https://github.com/octoblu/meshblu MeshBlu IoT Gateway
|
||||
# http://json-rpc.org/wiki/specification JSONRPC v 1.0
|
||||
class BuildMeshMessage < Mutations::Command
|
||||
required do
|
||||
duck :message, methods: [:payload]
|
||||
end
|
||||
|
||||
def validate
|
||||
parse_message
|
||||
type_check "params", Hash
|
||||
type_check "method", String
|
||||
message["id"] ||= SecureRandom.uuid # Just incase...
|
||||
end
|
||||
|
||||
def execute
|
||||
MeshMessage.new(message.symbolize_keys)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse_message
|
||||
inputs["message"] = JSON.parse(inputs["message"].payload)
|
||||
rescue JSON::ParserError => e
|
||||
add_error :payload, :parse_error, "Message was probably not JSON"
|
||||
end
|
||||
|
||||
def type_check(key, klass)
|
||||
if (message[key].is_a?(klass))
|
||||
return true
|
||||
else
|
||||
add_error :payload, :key, "Expected #{key} to be a #{klass}"
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,60 +0,0 @@
|
|||
require 'mutations'
|
||||
require_relative 'fetch_bot_status'
|
||||
module FBPi
|
||||
# The Arduino does not have persistent storage on board. There are a
|
||||
# number of settings that must be set every time the device starts (eg:
|
||||
# calibration settings). This object is responsible for bootstrapping bot
|
||||
# settings when the device is powered up.
|
||||
class ColdStart < Mutations::Command
|
||||
DEFAULT_PARAMS = { # Default settings for hardware.
|
||||
MOVEMENT_AXIS_NR_STEPS_X: 222,
|
||||
MOVEMENT_AXIS_NR_STEPS_Y: 222,
|
||||
MOVEMENT_AXIS_NR_STEPS_Z: 222 }
|
||||
|
||||
required do
|
||||
duck :bot, methods: [:emit_changes, :log, :mesh, :onchange, :onclose,
|
||||
:onmessage, :ready, :status, :status_storage]
|
||||
end
|
||||
|
||||
def execute
|
||||
pull_up_stored_parameters_from_disk
|
||||
FetchBotStatus.run!(bot: bot)
|
||||
set_event_handlers
|
||||
bot
|
||||
end
|
||||
|
||||
def set_event_handlers
|
||||
# bot.onmessage { |msg| /# not needed anymore, except for debugging #/ }
|
||||
bot.onchange { |msg| diffmessage(msg) }
|
||||
bot.onclose { |msg| close(msg) }
|
||||
end
|
||||
|
||||
def pull_up_stored_parameters_from_disk
|
||||
hash = DEFAULT_PARAMS.merge(bot.status_storage.to_h(:bot))
|
||||
param_names = FB::Gcode::PARAMETER_DICTIONARY.invert
|
||||
bot.status.transaction do |s|
|
||||
hash.each { |k,v|
|
||||
param_number = param_names.fetch(k, k)
|
||||
bot.commands.write_parameter(param_number, v) }
|
||||
end
|
||||
end
|
||||
|
||||
def diffmessage(diff)
|
||||
bot.status_storage.update_attributes(:bot, diff)
|
||||
if (diff.keys != [:BUSY])
|
||||
# Broadcasting busy status changes result in too much network 'noise'.
|
||||
# We could broadcast bot's busy status, but why?
|
||||
bot.emit_changes
|
||||
end
|
||||
end
|
||||
|
||||
def close()
|
||||
# Offload all persistent variables to file on shutdown.
|
||||
[:bot, :pi].each do |namespace|
|
||||
bot.status_storage.update_attributes(namespace, bot.status.to_h)
|
||||
end
|
||||
bot.log "Bot offline at #{Time.now}", "high"
|
||||
EM.stop
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,19 +0,0 @@
|
|||
require 'active_record'
|
||||
require 'mutations'
|
||||
require 'ostruct'
|
||||
|
||||
require_relative 'build_mesh_message'
|
||||
require_relative 'create_step'
|
||||
require_relative 'create_sequence'
|
||||
require_relative 'create_schedule'
|
||||
require_relative 'create_step'
|
||||
require_relative 'create_plant'
|
||||
require_relative 'report_bot_status'
|
||||
require_relative 'fetch_bot_status'
|
||||
require_relative 'if_statement'
|
||||
require_relative 'resolve_controller'
|
||||
require_relative 'send_mesh_response'
|
||||
require_relative 'sync_bot'
|
||||
require_relative 'send_message'
|
||||
require_relative 'read_pin'
|
||||
require_relative 'cold_start'
|
|
@ -1,21 +0,0 @@
|
|||
require_relative '../models/plant'
|
||||
|
||||
module FBPi
|
||||
# Download all Plant objects (JSON) off of Farmbot Web API, convert them to
|
||||
# Ruby objects, save them in SQLite.
|
||||
class CreatePlant < Mutations::Command
|
||||
required do
|
||||
string :id
|
||||
integer :x
|
||||
integer :y
|
||||
end
|
||||
|
||||
def validate
|
||||
inputs[:id_on_web_app] = inputs.delete(:id)
|
||||
end
|
||||
|
||||
def execute
|
||||
Plant.create!(inputs)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,33 +0,0 @@
|
|||
require_relative '../models/schedule'
|
||||
require_relative '../models/sequence'
|
||||
|
||||
module FBPi
|
||||
# Acts as a factory for schedule objects. Used mostly when the RPi is fetching
|
||||
# JSON schedule objects off of the web app's API (format 1) and needs to
|
||||
# transform the schedules into a format that is usable by the RPi and storable
|
||||
# in SQLite (via ActiveRecord)
|
||||
class CreateSchedule < Mutations::Command
|
||||
required do
|
||||
string :start_time
|
||||
string :id
|
||||
string :end_time
|
||||
float :repeat
|
||||
string :time_unit, in: Schedule::UNITS_OF_TIME
|
||||
string :sequence_id
|
||||
end
|
||||
|
||||
optional do
|
||||
string :next_time
|
||||
end
|
||||
|
||||
def validate
|
||||
inputs[:id_on_web_app] = inputs.delete(:id)
|
||||
inputs[:sequence] = Sequence.find_by!(id_on_web_app:
|
||||
inputs.delete(:sequence_id))
|
||||
end
|
||||
|
||||
def execute
|
||||
Schedule.create!(inputs)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,17 +0,0 @@
|
|||
require_relative '../models/sequence'
|
||||
|
||||
module FBPi
|
||||
# Builds a validated sequence (a collection of steps that the bot will
|
||||
# execute, eg: "Plant tomato seeds")
|
||||
class CreateSequence < Mutations::Command
|
||||
required do
|
||||
string :name
|
||||
string :id
|
||||
array(:steps) { model :step, builder: CreateStep, new_records: true }
|
||||
end
|
||||
|
||||
def execute
|
||||
Sequence.create!(inputs.merge!(id_on_web_app: inputs.delete(:id)))
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,30 +0,0 @@
|
|||
require_relative '../models/step'
|
||||
module FBPi
|
||||
# Factory for new step objects. Mostly used when pulling down steps from the
|
||||
# Web App's API and transforming that JSON data into SQLite/ActiveRecord
|
||||
# objects
|
||||
class CreateStep < Mutations::Command
|
||||
required do
|
||||
string :message_type, in: Step::COMMANDS
|
||||
integer :position
|
||||
hash :command do
|
||||
optional do
|
||||
integer :x, default: nil
|
||||
integer :y, default: nil
|
||||
integer :z, default: nil
|
||||
integer :speed, default: nil
|
||||
integer :pin, default: nil
|
||||
integer :mode, default: nil
|
||||
string :variable, default: nil
|
||||
string :operator, default: nil
|
||||
string :sub_sequence_id, default: nil
|
||||
string :value, default: nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def execute
|
||||
Step.new(inputs.merge(inputs["command"]))
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,36 +0,0 @@
|
|||
require 'mutations'
|
||||
require 'securerandom'
|
||||
|
||||
module FBPi
|
||||
# Occasionally, the bot will receive trash messages that are not formatted in
|
||||
# a coherent manner. This object will attempt to recover from failure by
|
||||
# building an error response and avoiding a crash or unsafe behavior. This
|
||||
# object operates in a very uncertain environment, so approach with caution.
|
||||
class DisposeTrashMessage < Mutations::Command
|
||||
# Nothing is certain, so all inputs are optional with safe defaults.
|
||||
optional do
|
||||
duck :id
|
||||
duck :params
|
||||
duck :method
|
||||
end
|
||||
|
||||
def execute
|
||||
be_careful(id, String, :to_s, SecureRandom.uuid)
|
||||
be_careful(method, String, :to_s, 'unknown')
|
||||
be_careful(params, Hash, :to_h, {})
|
||||
|
||||
MeshMessage.new(method: method,
|
||||
params: params,
|
||||
id: id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# The most cowardly of all methods. Coerces an object to a class, or cowers
|
||||
# in fear of the consequences.
|
||||
def be_careful(object, klass, coersion, default)
|
||||
object.send(coersion) if object.respond_to?(coersion) # Attempt coercion
|
||||
object.is_a?(klass) ? object : default # Return coerced value or default.
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,76 +0,0 @@
|
|||
require 'mutations'
|
||||
|
||||
module FBPi
|
||||
# Excutes a single step in "The Real World(tm)". This command will be called
|
||||
# multiple times by a single schedule object when it is executed at a
|
||||
# prescribed time.
|
||||
class ExecStep < Mutations::Command
|
||||
class UnsafeCommand < Exception; end
|
||||
|
||||
required do
|
||||
duck :step, methods: [:message_type, :x, :y, :z, :pin, :value, :mode]
|
||||
duck :bot, methods: [:commands, :current_position]
|
||||
end
|
||||
|
||||
def execute
|
||||
start = Time.now
|
||||
self.send(message_key)
|
||||
finish = Time.now
|
||||
puts "Executed #{message_key} after #{(finish - start) * 1000.0} ms"
|
||||
rescue UnsafeCommand; unknown
|
||||
end
|
||||
|
||||
def message_key
|
||||
msg = step.message_type.to_s
|
||||
raise UnsafeCommand,
|
||||
"Unknown sequence step '#{msg}'" if !Step::COMMANDS.include?(msg)
|
||||
raise UnsafeCommand,
|
||||
"Step '#{msg}' is allowed, but not yet implemented" if !respond_to?(msg)
|
||||
msg
|
||||
end
|
||||
|
||||
def move_relative
|
||||
bot.commands.move_relative(x: (step.x || 0),
|
||||
y: (step.y || 0),
|
||||
z: (step.z || 0))
|
||||
end
|
||||
|
||||
def move_absolute
|
||||
bot.commands.move_absolute(x: step.x || bot.current_position.x,
|
||||
y: step.y || bot.current_position.y,
|
||||
z: step.z || bot.current_position.z)
|
||||
end
|
||||
|
||||
def pin_write
|
||||
bot.commands.write_pin(pin: step.pin, value: step.value, mode: step.mode)
|
||||
end
|
||||
|
||||
|
||||
def send_message
|
||||
SendMessage.run! message: step.value, bot: bot
|
||||
end
|
||||
|
||||
def if_statement
|
||||
params = {
|
||||
lhs: bot.template_variables[step.variable].to_s,
|
||||
rhs: step.value.to_s,
|
||||
operator: step.operator.to_s,
|
||||
bot: bot,
|
||||
sequence: Sequence.find_by(id_on_web_app: step.sub_sequence_id)
|
||||
}
|
||||
IfStatement.run!(params)
|
||||
end
|
||||
|
||||
def unknown
|
||||
bot.log("Unknown message #{step.message_type}")
|
||||
end
|
||||
|
||||
def read_pin
|
||||
ReadPin.run!(bot: bot, pin: step.pin)
|
||||
end
|
||||
|
||||
def wait
|
||||
bot.commands.wait(step.value.to_f.round.to_i)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,14 +0,0 @@
|
|||
require 'mutations'
|
||||
|
||||
module FBPi
|
||||
# Grabs a new auth token from the API.
|
||||
class FetchAuthToken < Mutations::Command
|
||||
required do
|
||||
duck :bot, methods: [:rest_client]
|
||||
end
|
||||
|
||||
def execute
|
||||
raise "TODO : Build this feature for when tokens expire!"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,32 +0,0 @@
|
|||
require 'mutations'
|
||||
|
||||
module FBPi
|
||||
# Typically, the pi does not need to poll the Arduino to know its status
|
||||
# because updates are broadcast over the serial line when statuses change. The
|
||||
# exception to this rule is device startup, when there is no data to perform a
|
||||
# diff on. When this is the case, you can run FetchBotStatus, which polls the
|
||||
# device for all of its status registers. This command sends as many as 22
|
||||
# requests down the serial line in one action, so hopefully it can be
|
||||
# refactored later on. SEE ALSO: ReportBotStatus
|
||||
class FetchBotStatus < Mutations::Command
|
||||
RELEVANT_PARAMETERS = [0,11,12,13,21,22,23,31,32,33,41,42,43,51,52,53,
|
||||
61,62,63,71,72,73]
|
||||
required do
|
||||
duck :bot, methods: [:status, :commands]
|
||||
end
|
||||
|
||||
def execute
|
||||
read_pins
|
||||
read_parameters
|
||||
FBPi::ReportBotStatus.run!(bot: bot)
|
||||
end
|
||||
|
||||
def read_pins
|
||||
0.upto(13) { |pin| bot.commands.read_pin(pin) }
|
||||
end
|
||||
|
||||
def read_parameters
|
||||
RELEVANT_PARAMETERS.each { |code| bot.commands.read_parameter(code) }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,32 +0,0 @@
|
|||
module FBPi
|
||||
# an if_statement is one of the many `message_type`s that a step can have.
|
||||
# This command uses relevant information in an if statement to execute a
|
||||
# sequence, but only if the if statement evaluates to true, using the left
|
||||
# hand side, right hand side and a set of allowable operators (eg: <, >, !=..)
|
||||
# Used in the execution of steps.
|
||||
# Conditionally executes a sequence if `operator(lhs, rhs)` evals to true.
|
||||
class IfStatement < Mutations::Command
|
||||
required do
|
||||
string :lhs
|
||||
string :rhs
|
||||
string :operator, in: %w(> < != ==)
|
||||
model :sequence
|
||||
duck :bot
|
||||
end
|
||||
|
||||
def execute
|
||||
sequence.exec(bot) if evaluates_to_true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def evaluates_to_true
|
||||
# String eval via liquid templates for safety / sandboxing.
|
||||
expression = "#{lhs} #{operator} #{rhs}"
|
||||
test = "{% if #{expression} %}true{% else %}false{% endif %}"
|
||||
outcome = Liquid::Template.parse(test).render({}).strip == "true"
|
||||
puts outcome ? "WILL EXECUTE THIS SEQUENCE" : "WONT EXECUTE THIS SEQUENCE"
|
||||
return outcome
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,20 +0,0 @@
|
|||
require 'mutations'
|
||||
require_relative 'send_message'
|
||||
|
||||
module FBPi
|
||||
# Typically used in the performance of a single step within a sequence whose
|
||||
# message_type == "read_pin". This method only reads the value of pins that
|
||||
# were REPORTED to the Pi over the serial line. If the pin value has not yet
|
||||
# been reported by the arduino, the value will be :unknown
|
||||
class ReadPin < Mutations::Command
|
||||
required do
|
||||
integer :pin, min: 0, max: 13
|
||||
duck :bot, methods: [:commands]
|
||||
end
|
||||
|
||||
def execute
|
||||
SendMessage.run!(message: "Pin #{pin} is #{bot.status.get_pin(pin).to_s}",
|
||||
bot: bot)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,46 +0,0 @@
|
|||
require 'mutations'
|
||||
|
||||
module FBPi
|
||||
# This class reads bot status information that has been cached by the Pi. This
|
||||
# should not be confused with FetchBotStatus, which actively requests status
|
||||
# updates from the bot. This command will return a Hash that has all relevant
|
||||
# information known about the bot. It's like a serializer, sort of.
|
||||
class ReportBotStatus < Mutations::Command
|
||||
required do
|
||||
duck :bot, methods: [:status, :commands]
|
||||
end
|
||||
|
||||
def execute
|
||||
{}
|
||||
.merge(bot_info)
|
||||
.merge(pin_info)
|
||||
.merge(pi_info)
|
||||
.deep_symbolize_keys
|
||||
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def bot_info
|
||||
bot
|
||||
.status
|
||||
.to_h
|
||||
.except(:PINS)
|
||||
.delete_if { |k, v| k.to_s.start_with?("UNKNOWN") }
|
||||
.map { |k, v| [k.to_s.downcase, v] }
|
||||
.to_h
|
||||
end
|
||||
|
||||
def pi_info
|
||||
{ last_sync: bot.status_storage.fetch(:pi, :LAST_SYNC) }
|
||||
end
|
||||
|
||||
def pin_info
|
||||
[*0..13].inject({}) do |hsh, pin|
|
||||
val = bot.status.get_pin(pin)
|
||||
hsh["pin#{pin}".to_sym] = val
|
||||
hsh
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,26 +0,0 @@
|
|||
Dir['lib/controllers/**/*.rb'].each { |file| load(file) }
|
||||
|
||||
module FBPi
|
||||
# Uses a string such as 'single_command.MOVE RELATIVE' and resolves it to a
|
||||
# particular controller, or returns UnknownController. Be sure to add new
|
||||
# controllers and RPC commands to the ROUTES constant.
|
||||
class ResolveController < Mutations::Command
|
||||
|
||||
ROUTES = {
|
||||
'single_command' => SingleCommandController,
|
||||
'read_status' => ReadStatusController,
|
||||
'exec_sequence' => ExecSequenceController,
|
||||
'sync_sequence' => SyncSequenceController,
|
||||
'update_calibration' => UpdateCalibrationController,
|
||||
'unknown' => UnknownController,
|
||||
}
|
||||
|
||||
optional do
|
||||
string :method, default: ''
|
||||
end
|
||||
|
||||
def execute
|
||||
ROUTES[method.to_s.split('.').first] || UnknownController
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,32 +0,0 @@
|
|||
require 'mutations'
|
||||
require 'securerandom'
|
||||
|
||||
module FBPi
|
||||
# This class emits a JSONRPC compliant response object using a provided mesh
|
||||
# object.
|
||||
# For more info see: http://json-rpc.org/wiki/specification#a1.2Response
|
||||
class SendMeshResponse < Mutations::Command
|
||||
required do
|
||||
duck :original_message, methods: [:id]
|
||||
duck :mesh, methods: [:emit]
|
||||
string :method
|
||||
end
|
||||
|
||||
optional do
|
||||
hash(:result, default: {}) do
|
||||
duck :*, methods: [:to_json]
|
||||
end
|
||||
end
|
||||
|
||||
def execute
|
||||
mesh.emit "bot/CHANGE_THIS_PLEASE", output
|
||||
end
|
||||
|
||||
def output
|
||||
hsh = {id: original_message.id, error: nil, result: nil}
|
||||
key = (method == "error") ? :error : :result
|
||||
hsh[key] = result.merge(method: method)
|
||||
hsh.deep_symbolize_keys # :(
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,28 +0,0 @@
|
|||
require 'mutations'
|
||||
require 'liquid'
|
||||
require_relative '../telemetry_message'
|
||||
|
||||
module FBPi
|
||||
# Sends a message to MeshBlu with optional Liquid Templating. Usually this is
|
||||
# required to report an event, completion of a schedule or system failures.
|
||||
class SendMessage < Mutations::Command
|
||||
Liquid::Template.error_mode = :lax
|
||||
|
||||
required do
|
||||
string :message
|
||||
duck :bot, methods: [:status, :mesh, :template_variables]
|
||||
end
|
||||
|
||||
def execute
|
||||
TelemetryMessage.build(template).publish(bot.mesh)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def template
|
||||
@template ||= Liquid::Template
|
||||
.parse(message)
|
||||
.render(bot.template_variables)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,34 +0,0 @@
|
|||
module FBPi
|
||||
# This Object is responsible for contacting the Farmbot Web App via REST calls
|
||||
# in order to fetch the latest command execution schedules. This is a
|
||||
# destructive action that will download all Sequences, Schedules and Steps via
|
||||
# REST. After fetching the resource, it will destroy all of the local Schedule
|
||||
# , Step and Sequence objects and assume that the Web App always has the
|
||||
# latests and "most correct" version of Schedule information.
|
||||
class SyncBot < Mutations::Command
|
||||
required { duck :bot, methods: [:rest_client] }
|
||||
|
||||
def execute
|
||||
puts "Attempting to sync now..."
|
||||
api = bot.rest_client
|
||||
ActiveRecord::Base.transaction do
|
||||
[Schedule, Sequence, Step].map(&:destroy_all)
|
||||
{sequences: api.sequences.fetch.map { |s| CreateSequence.run!(s) }.count,
|
||||
schedules: api.schedules.fetch.map { |s| CreateSchedule.run!(s) }.count,
|
||||
plants: api.plants.fetch.map { |s| CreatePlant.run!(s) }.count,
|
||||
steps: Step.count }.tap { |d| after_sync(d) }
|
||||
end
|
||||
puts "Done with sync..."
|
||||
rescue FbResource::FetchError => e
|
||||
add_error :web_server, :fetch_error, e.message
|
||||
rescue => e
|
||||
add_error :sync_issues, :fetch_error, e.message
|
||||
end
|
||||
|
||||
def after_sync(data)
|
||||
["Sync completed at #{Time.now}", data].map { |d| bot.log(d) }
|
||||
bot.status_storage.update_attributes(:pi, LAST_SYNC: Time.now)
|
||||
bot.emit_changes
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,49 +0,0 @@
|
|||
require 'mutations'
|
||||
module FBPi
|
||||
# Responsible for setting calibration settings for a specific axis. Required
|
||||
# when changing settings like speed, accelerations, steps_per_mm, etc.
|
||||
class UpdateCalibration < Mutations::Command
|
||||
PARAMETER_DICTIONARY = FB::Gcode::PARAMETER_DICTIONARY.invert
|
||||
|
||||
required do
|
||||
duck :bot, methods: [:commands]
|
||||
hash :settings do
|
||||
optional do
|
||||
# Possible values:
|
||||
# param_version, movement_timeout_x, movement_timeout_y, movement_timeout_z,
|
||||
# movement_invert_endpoints_x, movement_invert_endpoints_y, movement_invert_endpoints_z,
|
||||
# movement_invert_motor_x, movement_invert_motor_y, movement_invert_motor_z,
|
||||
# movement_steps_acc_dec_x, movement_steps_acc_dec_y, movement_steps_acc_dec_z,
|
||||
# movement_home_up_x, movement_home_up_y, movement_home_up_z, movement_min_spd_x,
|
||||
# movement_min_spd_y, movement_min_spd_z, movement_max_spd_x, movement_max_spd_y, movement_max_spd_z,
|
||||
# encoder_enabled_x, encoder_enabled_y, encoder_enabled_z, encoder_missed_steps_max_x,
|
||||
# encoder_missed_steps_max_y, encoder_missed_steps_max_z, encoder_missed_steps_decay_x,
|
||||
# encoder_missed_steps_decay_y, encoder_missed_steps_decay_z, movement_axis_nr_steps_x,
|
||||
# movement_axis_nr_steps_y, movement_axis_nr_steps_z, pin_guard_1_pin_nr, pin_guard_1_time_out,
|
||||
# pin_guard_1_active_state, pin_guard_2_pin_nr, pin_guard_2_time_out, pin_guard_2_active_state,
|
||||
# pin_guard_3_pin_nr, pin_guard_3_time_out, pin_guard_3_active_state, pin_guard_4_pin_nr,
|
||||
# pin_guard_4_time_out, pin_guard_4_active_state, pin_guard_5_pin_nr, pin_guard_5_time_out,
|
||||
# pin_guard_5_active_state
|
||||
PARAMETER_DICTIONARY.keys.map(&:downcase).each { |p| integer(p) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def execute
|
||||
settings.each do |key, param_value|
|
||||
key = key.upcase
|
||||
param_number = PARAMETER_DICTIONARY[key.to_sym]
|
||||
|
||||
if bot.status.to_h[key.to_sym] != param_value
|
||||
# TODO: This belongs inside of write_parameter
|
||||
# TODO: Move this into farmbot-serial and send PR.
|
||||
bot.status.transaction { |i| i[key] = param_value }
|
||||
bot.commands.write_parameter(param_number, param_value)
|
||||
end
|
||||
end
|
||||
|
||||
ReportBotStatus.run!(bot: bot)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
defmodule Controller do
|
||||
# use Application
|
||||
require Logger
|
||||
use Supervisor
|
||||
|
||||
def start_link(_args) do
|
||||
import Supervisor.Spec, warn: false
|
||||
Logger.debug("Starting Controller")
|
||||
|
||||
children = [
|
||||
worker(Auth, [[]]),
|
||||
supervisor(RPCSupervisor, [[]], restart: :permanent ),
|
||||
supervisor(BotCommandSupervisor, [[]], restart: :permanent),
|
||||
worker(BotStatus, [[]] , restart: :permanent ),
|
||||
supervisor(SerialSupervisor, [[]], restart: :permanent ),
|
||||
supervisor(MqttSupervisor, [[]], restart: :permanent ),
|
||||
supervisor(SequenceSupervisor, [[]], restart: :permanent )
|
||||
# worker(BotSync, [[]] , restart: :permanent )
|
||||
]
|
||||
opts = [strategy: :one_for_all, name: Controller.Supervisor]
|
||||
Supervisor.start_link(children, opts)
|
||||
end
|
||||
end
|
|
@ -1,28 +0,0 @@
|
|||
require_relative '../command_objects/send_mesh_response'
|
||||
# TODO: SimpleCov is not registering hits to this controller, despite the fact
|
||||
# that tests exist. Investigate later. PRs welcome.
|
||||
# :nocov:
|
||||
module FBPi
|
||||
# The base controller from which all other controllers inherit. Takes in the
|
||||
# current bot instance, an FBPi::MeshMessage and the current MeshBlu
|
||||
# connection and routes the JSONRPC request to wherever it needs to go in the
|
||||
# app.
|
||||
class AbstractController
|
||||
attr_reader :message, :bot, :mesh
|
||||
|
||||
def initialize(message, bot, mesh)
|
||||
@message, @bot, @mesh = message, bot, mesh
|
||||
end
|
||||
|
||||
def call
|
||||
raise "A child of AbstractController is expected to implement #call()"
|
||||
end
|
||||
|
||||
def reply(method, reslt = {})
|
||||
SendMeshResponse.run!(original_message: message,
|
||||
mesh: mesh,
|
||||
method: method,
|
||||
result: reslt)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,24 +0,0 @@
|
|||
require_relative 'abstract_controller'
|
||||
require_relative '../command_objects/commands'
|
||||
|
||||
module FBPi
|
||||
# Controller that will execute a single Schedule of operations immediately (as
|
||||
# opposed to scheduling it for execution at a later time).
|
||||
class ExecSequenceController < AbstractController
|
||||
def call
|
||||
sequence.try_sync(bot)
|
||||
sequence.exec(bot)
|
||||
reply "exec_sequence", params
|
||||
rescue Mutations::ValidationException => error
|
||||
reply "error", error: error.message
|
||||
end
|
||||
|
||||
def params
|
||||
@params ||= (@message.params || {})
|
||||
end
|
||||
|
||||
def sequence
|
||||
@sequence ||= CreateSequence.run!(params)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,11 +0,0 @@
|
|||
require_relative 'abstract_controller'
|
||||
require_relative '../command_objects/report_bot_status'
|
||||
module FBPi
|
||||
# Responsible for reporting the status of the Bot when it is requested by a
|
||||
# user
|
||||
class ReadStatusController < AbstractController
|
||||
def call
|
||||
reply "read_status", ReportBotStatus.run!(bot: bot)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,76 +0,0 @@
|
|||
require_relative 'abstract_controller'
|
||||
|
||||
module FBPi
|
||||
# This controller will safely execute a subset of whitelisted commands when
|
||||
# requested. This is similar to ExecSequenceController, except that rather
|
||||
# than execute an entire sequence of steps, it executes one single command,
|
||||
# such as moving a single axis to a specific coord.
|
||||
class SingleCommandController < AbstractController
|
||||
# TODO: Reduce duplication in this controller.
|
||||
attr_reader :cmd, :key
|
||||
|
||||
AVAILABLE_ACTIONS = { "move relative" => :move_relative,
|
||||
"move absolute" => :move_absolute,
|
||||
"unknown" => :unknown,
|
||||
"home x" => :home_x,
|
||||
"home y" => :home_y,
|
||||
"home z" => :home_z,
|
||||
"home all" => :home_all,
|
||||
"pin write" => :pin_write,
|
||||
"emergency stop" => :emergency_stop, }
|
||||
|
||||
def call
|
||||
# "single_command.MOVE RELATIVE" ==> "move relative"
|
||||
@key = message.method.to_s.split('.').last.downcase.gsub("_", " ")
|
||||
action = AVAILABLE_ACTIONS.fetch(key, :unknown).to_sym
|
||||
@cmd = message.params
|
||||
send(action)
|
||||
reply 'single_command', ReportBotStatus.run!(bot: bot)
|
||||
end
|
||||
|
||||
def move_relative
|
||||
bot.commands.move_relative x: cmd['x'], y: cmd['y'], z: cmd['z']
|
||||
end
|
||||
|
||||
def move_absolute
|
||||
bot.commands.move_absolute x: cmd['x'], y: cmd['y'], z: cmd['z']
|
||||
end
|
||||
|
||||
def home_x
|
||||
bot.commands.home_x
|
||||
end
|
||||
|
||||
def home_y
|
||||
bot.commands.home_y
|
||||
end
|
||||
|
||||
def home_z
|
||||
bot.commands.home_z
|
||||
end
|
||||
|
||||
def home_all
|
||||
bot.commands.home_all
|
||||
end
|
||||
|
||||
def pin_write
|
||||
# write_pin(pin: 13, value: 1, mode: 0)
|
||||
bot.commands.write_pin(pin: cmd['pin'],
|
||||
value: cmd['value1'],
|
||||
mode:cmd.fetch('mode', 0))
|
||||
end
|
||||
|
||||
def emergency_stop
|
||||
bot.commands.emergency_stop
|
||||
end
|
||||
|
||||
def unknown
|
||||
case key
|
||||
when 'single command', nil then msg = 'NULL'
|
||||
else; msg = key
|
||||
end
|
||||
raise "Unknown message '#{ msg }'. Most likely, the "\
|
||||
"command has not been implemented or does not exist. Try: "\
|
||||
"#{AVAILABLE_ACTIONS.keys.join(', ')}"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,17 +0,0 @@
|
|||
require_relative 'abstract_controller'
|
||||
require 'farmbot-resource'
|
||||
require_relative '../command_objects/commands'
|
||||
|
||||
module FBPi
|
||||
# This controller will destroy all Schedules, Sequences and Steps on the bot
|
||||
# and replace them with the most recent version that exists remotely on the
|
||||
# Web App.
|
||||
class SyncSequenceController < AbstractController
|
||||
def call
|
||||
SyncBot.run!(bot: bot)
|
||||
reply "sync_sequence"
|
||||
rescue FbResource::FetchError => error
|
||||
reply "error", error: error.message, hint: "The Webapp is having issues"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,14 +0,0 @@
|
|||
require_relative 'abstract_controller'
|
||||
|
||||
module FBPi
|
||||
# Just a controller that gets called when a user tries to access a nonexistant
|
||||
# controller name.
|
||||
class UnknownController < AbstractController
|
||||
def call
|
||||
really_long_message = "You tried to send a message with a `method` " +
|
||||
"property of `#{message.method || 'null'}`, but " +
|
||||
"that's not a valid options."
|
||||
reply "error", error: really_long_message
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,13 +0,0 @@
|
|||
require_relative 'abstract_controller'
|
||||
require_relative '../command_objects/update_calibration'
|
||||
|
||||
module FBPi
|
||||
# Used to remotely set one of the many possible "parameter" variables inside
|
||||
# of the Arduino. Useful for calibration. SEE: FBPi::UpdateCalibration
|
||||
class UpdateCalibrationController < AbstractController
|
||||
def call
|
||||
reply "calibrate_axis",
|
||||
UpdateCalibration.run!(bot: bot, settings: message.params)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,44 @@
|
|||
defmodule Downloader do
|
||||
|
||||
def download_and_install_update(url) do
|
||||
RPCMessageHandler.log("Downloading an Update!")
|
||||
run(url, "/tmp/update.fw") |> Nerves.Firmware.upgrade_and_finalize
|
||||
RPCMessageHandler.log("Going down for update. See you soon!")
|
||||
Nerves.Firmware.reboot
|
||||
end
|
||||
|
||||
def run(url, dl_file) when is_bitstring url do
|
||||
HTTPotion.get url, stream_to: self, timeout: :infinity
|
||||
receive_data(total_bytes: :unknown, data: "", dl_path: dl_file)
|
||||
end
|
||||
|
||||
defp receive_data(total_bytes: total_bytes, data: data, dl_path: path) do
|
||||
receive do
|
||||
%HTTPotion.AsyncHeaders{headers: h} ->
|
||||
|
||||
{total_bytes, _} = h[:"Content-Length"] |> Integer.parse
|
||||
IO.puts "Let's download #{mb total_bytes}…"
|
||||
receive_data(total_bytes: total_bytes, data: data, dl_path: path)
|
||||
|
||||
%HTTPotion.AsyncChunk{chunk: new_data} ->
|
||||
|
||||
accumulated_data = data <> new_data
|
||||
accumulated_bytes = byte_size(accumulated_data)
|
||||
percent = accumulated_bytes / total_bytes * 100 |> Float.round(2)
|
||||
IO.puts "#{percent}% (#{mb accumulated_bytes})"
|
||||
receive_data(total_bytes: total_bytes, data: accumulated_data, dl_path: path)
|
||||
|
||||
%HTTPotion.AsyncEnd{} ->
|
||||
|
||||
File.write!(path, data)
|
||||
IO.puts "All downloaded! See: #{path}"
|
||||
path
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
defp mb(bytes) do
|
||||
number = bytes / 1_048_576 |> Float.round(2)
|
||||
"#{number} MB"
|
||||
end
|
||||
end
|
|
@ -1,48 +0,0 @@
|
|||
require 'farmbot-serial'
|
||||
require_relative 'messaging/mqtt'
|
||||
require_relative 'messaging/message_handler'
|
||||
require_relative 'chores/chore_runner'
|
||||
require_relative 'models/status_storage.rb'
|
||||
require_relative 'bot_decorator'
|
||||
require_relative 'settings'
|
||||
require_relative 'rest_client'
|
||||
|
||||
ActiveRecord::Base.establish_connection(
|
||||
:adapter => 'sqlite3',
|
||||
:database => './db/db.sqlite3'
|
||||
)
|
||||
|
||||
class FarmBotPi
|
||||
attr_accessor :mqtt, :bot, :handler, :runner, :status_storage
|
||||
WEBAPP_URL = FBPi::Settings.webapp_url
|
||||
|
||||
def initialize
|
||||
@rest_client = FBPi::RPiRestClient.new(WEBAPP_URL)
|
||||
uuid = @rest_client.device.current["uuid"]
|
||||
token = @rest_client.config.token
|
||||
@mqtt = MQTTAdapter.new(uuid, token)
|
||||
@status_storage = FBPi::StatusStorage.new("db/bot_status.pstore")
|
||||
@bot = FBPi::BotDecorator.build(@status_storage, @mqtt, @rest_client)
|
||||
end
|
||||
|
||||
def start
|
||||
EM.run do
|
||||
bot.bootstrap
|
||||
mqtt.connect { |msg| mqttmessage(msg) }
|
||||
FB::ArduinoEventMachine.connect(bot)
|
||||
start_chore_runner
|
||||
end
|
||||
end
|
||||
|
||||
def mqttmessage(msg)
|
||||
puts msg.payload
|
||||
FBPi::MessageHandler.call(msg, bot, mqtt)
|
||||
end
|
||||
|
||||
def start_chore_runner
|
||||
EventMachine::PeriodicTimer.new(FBPi::ChoreRunner::INTERVAL) do
|
||||
# TODO: Add chore to check validity of session token / refresh as needed.
|
||||
FBPi::ChoreRunner.new(bot).run
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,33 @@
|
|||
defmodule Fw do
|
||||
require Logger
|
||||
use Supervisor
|
||||
@target System.get_env("NERVES_TARGET") || "rpi3"
|
||||
@version Path.join(__DIR__ <> "/..", "VERSION")
|
||||
|> File.read!
|
||||
|> String.strip
|
||||
|
||||
def init(_args) do
|
||||
children = [
|
||||
Plug.Adapters.Cowboy.child_spec(:http, MyRouter, [], [port: 4000]),
|
||||
supervisor(NetworkSupervisor, [[]], restart: :permanent),
|
||||
supervisor(Controller, [[]], restart: :permanent)
|
||||
]
|
||||
opts = [strategy: :one_for_all, name: Fw]
|
||||
supervise(children, opts)
|
||||
end
|
||||
|
||||
def start(_type, args) do
|
||||
Logger.debug("Starting Firmware on Target: #{@target}")
|
||||
Supervisor.start_link(__MODULE__, args)
|
||||
end
|
||||
|
||||
def version do
|
||||
@version
|
||||
end
|
||||
|
||||
def factory_reset do
|
||||
File.rm("/root/secretes.txt")
|
||||
File.rm("/root/network.config")
|
||||
Nerves.Firmware.reboot
|
||||
end
|
||||
end
|
|
@ -0,0 +1,224 @@
|
|||
alias Experimental.{GenStage}
|
||||
defmodule RPCMessageHandler do
|
||||
use GenStage
|
||||
require Logger
|
||||
@transport Application.get_env(:json_rpc, :transport)
|
||||
@update_server Application.get_env(:fb, :update_server)
|
||||
@doc """
|
||||
This is where all JSON RPC messages come in.
|
||||
Currently only from Mqtt, but is technically transport agnostic.
|
||||
Right now we set @transport to MqttHandler, but it could technically be
|
||||
In config and set to anything that can emit and recieve JSON RPC messages.
|
||||
"""
|
||||
|
||||
def start_link() do
|
||||
GenStage.start_link(__MODULE__, :ok)
|
||||
end
|
||||
|
||||
def init(_args) do
|
||||
{:consumer, :ok, subscribe_to: [RPCMessageManager]}
|
||||
end
|
||||
|
||||
def handle_events(events, _from, state) do
|
||||
for event <- events do
|
||||
handle_rpc(event)
|
||||
end
|
||||
{:noreply, [], state}
|
||||
end
|
||||
|
||||
# JSON RPC RESPONSE
|
||||
def ack_msg(id) when is_bitstring(id) do
|
||||
Poison.encode!(
|
||||
%{id: id,
|
||||
error: nil,
|
||||
result: %{"OK" => "OK"} })
|
||||
end
|
||||
|
||||
# JSON RPC RESPONSE ERROR
|
||||
def ack_msg(id, {name, message}) when is_bitstring(id) and is_bitstring(name) and is_bitstring(message) do
|
||||
IO.inspect({name, message})
|
||||
Poison.encode!(
|
||||
%{id: id,
|
||||
error: %{name: name,
|
||||
message: message },
|
||||
result: nil})
|
||||
end
|
||||
|
||||
def log_msg(message) do
|
||||
Poison.encode!(
|
||||
%{ id: nil,
|
||||
method: "log_message",
|
||||
params: [%{status: BotStatus.get_status,
|
||||
time: :os.system_time(:seconds),
|
||||
message: message}] })
|
||||
end
|
||||
|
||||
def personality_msg(message) when is_bitstring(message) do
|
||||
Poison.encode!(
|
||||
%{ id: nil,
|
||||
method: "personality_message",
|
||||
params: [%{message: message}] })
|
||||
end
|
||||
|
||||
def handle_rpc(%{"method" => method, "params" => params, "id" => id})
|
||||
when is_list(params) and
|
||||
is_bitstring(method) and
|
||||
is_bitstring(id)
|
||||
do
|
||||
case do_handle(method, params) do
|
||||
:ok -> @transport.emit(ack_msg(id))
|
||||
{:error, name, message} -> @transport.emit(ack_msg(id, {name, message}))
|
||||
unknown_error -> @transport.emit(ack_msg(id, {"unknown_error", "#{inspect unknown_error}"}))
|
||||
end
|
||||
end
|
||||
|
||||
def handle_rpc(broken_rpc) do
|
||||
Logger.debug("Got a broken RPC message!!")
|
||||
IO.inspect broken_rpc
|
||||
end
|
||||
|
||||
# E STOP
|
||||
def do_handle("emergency_stop", _) do
|
||||
Command.e_stop
|
||||
end
|
||||
|
||||
# Home All
|
||||
def do_handle("home_all", [ %{"speed" => s} ]) when is_integer s do
|
||||
Command.home_all(s)
|
||||
end
|
||||
|
||||
def do_handle("home_all", params) do
|
||||
Logger.debug("bad params for home_all")
|
||||
IO.inspect(params)
|
||||
{:error, "BAD_PARAMS",
|
||||
Poison.encode!(%{"speed" => "number"})}
|
||||
end
|
||||
|
||||
# WRITE_PIN
|
||||
def do_handle("write_pin", [ %{"pin_mode" => 1, "pin_number" => p, "pin_value" => v} ])
|
||||
when is_integer p and
|
||||
is_integer v
|
||||
do
|
||||
Command.write_pin(p,v,1)
|
||||
end
|
||||
|
||||
def do_handle("write_pin", [ %{"pin_mode" => 0, "pin_number" => p, "pin_value" => v} ])
|
||||
when is_integer p and
|
||||
is_integer v
|
||||
do
|
||||
Command.write_pin(p,v,0)
|
||||
end
|
||||
|
||||
def do_handle("write_pin", _) do
|
||||
{:error, "BAD_PARAMS",
|
||||
Poison.encode!(%{"pin_mode" => "1 or 2", "pin_number" => "number", "pin_value" => "number"})}
|
||||
end
|
||||
|
||||
# Move to a specific coord
|
||||
def do_handle("move_absolute", [%{"speed" => s, "x" => x, "y" => y, "z" => z}])
|
||||
when is_integer(x) and
|
||||
is_integer(y) and
|
||||
is_integer(z) and
|
||||
is_integer(s)
|
||||
do
|
||||
Command.move_absolute(x,y,z,s)
|
||||
end
|
||||
|
||||
def do_handle("move_absolute", params) do
|
||||
Logger.debug("bad params for Move Absolute")
|
||||
IO.inspect params
|
||||
{:error, "BAD_PARAMS",
|
||||
Poison.encode!(%{"x" => "number", "y" => "number", "z" => "number", "speed" => "number"})}
|
||||
end
|
||||
|
||||
# Move relative to current x position
|
||||
def do_handle("move_relative", [%{"speed" => speed,
|
||||
"x" => x_move_by,
|
||||
"y" => y_move_by,
|
||||
"z" => z_move_by}])
|
||||
when is_integer(speed) and
|
||||
is_integer(x_move_by) and
|
||||
is_integer(y_move_by) and
|
||||
is_integer(z_move_by)
|
||||
do
|
||||
Command.move_relative(%{x: x_move_by, y: y_move_by, z: z_move_by, speed: speed})
|
||||
end
|
||||
|
||||
def do_handle("move_relative", _) do
|
||||
{:error, "BAD_PARAMS",
|
||||
Poison.encode!(%{"x or y or z" => "number to move by", "speed" => "number"})}
|
||||
end
|
||||
|
||||
# Read status
|
||||
def do_handle("read_status", _) do
|
||||
Logger.debug("Reporting Current Status")
|
||||
send_status
|
||||
end
|
||||
|
||||
def do_handle("check_updates", _) do
|
||||
resp = HTTPotion.get(@update_server<>"/version.json")
|
||||
current_version = Fw.version
|
||||
case resp do
|
||||
%HTTPotion.ErrorResponse{message: error} ->
|
||||
{:error, "Check Updates failed", error}
|
||||
_ ->
|
||||
json = Poison.decode!(resp.body)
|
||||
new_version = Map.get(json, "latest") |> Map.get("version")
|
||||
new_version_url = Map.get(json, "latest") |> Map.get("url")
|
||||
if(new_version != current_version) do
|
||||
spawn fn -> Downloader.download_and_install_update(new_version_url) end
|
||||
end
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
def do_handle("reboot", _ ) do
|
||||
log("Bot Going down for reboot.")
|
||||
Nerves.Firmware.reboot
|
||||
log("Something Weird happened...")
|
||||
end
|
||||
|
||||
def do_handle("power_off", _ ) do
|
||||
log("Bot Going down. Pls remeber me.")
|
||||
Nerves.Firmware.poweroff
|
||||
end
|
||||
|
||||
# "{\"update_calibration\", [%{\"movement_home_up_y\" => 0}]}"}
|
||||
def do_handle("update_calibration", [params]) when is_map(params) do
|
||||
case Enum.all?(params, fn({param, value}) ->
|
||||
param_int = Gcode.parse_param(param)
|
||||
Command.update_param(param_int, value)
|
||||
end)
|
||||
do
|
||||
true -> :ok
|
||||
false -> {:error, "update_calibration", "Something went wrong."}
|
||||
end
|
||||
end
|
||||
|
||||
# Unhandled event. Probably not implemented if it got this far.
|
||||
def do_handle(event, params) do
|
||||
Logger.debug("[RPC_HANDLER] got valid rpc, but event is not implemented.")
|
||||
{:error, "Unhandled method", "#{inspect {event, params}}"}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Shortcut for loggin to teh frontend. Pass it a string, watch it display
|
||||
"""
|
||||
def log(message) when is_bitstring(message) do
|
||||
@transport.emit(log_msg(message))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Shortcut for a personality message
|
||||
"""
|
||||
def pm(message) when is_bitstring(message) do
|
||||
@transport.emit(personality_msg(message))
|
||||
end
|
||||
|
||||
def send_status do
|
||||
m = %{id: nil,
|
||||
method: "status_update",
|
||||
params: [BotStatus.get_status] }
|
||||
@transport.emit(Poison.encode!(m))
|
||||
end
|
||||
end
|
|
@ -0,0 +1,34 @@
|
|||
alias Experimental.{GenStage}
|
||||
defmodule RPCMessageManager do
|
||||
use GenStage
|
||||
def start_link() do
|
||||
GenStage.start_link(__MODULE__, :ok, name: __MODULE__)
|
||||
end
|
||||
|
||||
def sync_notify(event, timeout\\5000) do
|
||||
GenStage.call(__MODULE__, {:notify, event}, timeout)
|
||||
end
|
||||
|
||||
def init(:ok) do
|
||||
{:producer, {:queue.new, 0}, dispatcher: GenStage.BroadcastDispatcher}
|
||||
end
|
||||
|
||||
def handle_call({:notify, event}, from, {queue, demand}) do
|
||||
dispatch_events(:queue.in({from, event}, queue), demand, [])
|
||||
end
|
||||
|
||||
def handle_demand(incoming_demand, {queue, demand}) do
|
||||
dispatch_events(queue, incoming_demand + demand, [])
|
||||
end
|
||||
|
||||
# This is some copy paste magic, not touching it.
|
||||
defp dispatch_events(queue, demand, events) do
|
||||
with d when d > 0 <- demand,
|
||||
{{:value, {from, event}}, queue} <- :queue.out(queue) do
|
||||
GenStage.reply(from, :ok)
|
||||
dispatch_events(queue, demand - 1, [event | events])
|
||||
else
|
||||
_ -> {:noreply, Enum.reverse(events), {queue, demand}}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,14 @@
|
|||
defmodule RPCSupervisor do
|
||||
def start_link(_) do
|
||||
import Supervisor.Spec
|
||||
children = [
|
||||
worker(RPCMessageManager, []),
|
||||
worker(RPCMessageHandler, [], id: 1)
|
||||
]
|
||||
Supervisor.start_link(children, strategy: :one_for_one)
|
||||
end
|
||||
|
||||
def init(_) do
|
||||
{:ok, %{}}
|
||||
end
|
||||
end
|
|
@ -1,11 +0,0 @@
|
|||
module FBPi
|
||||
# Represents a message as it enters the bot from the outside world. Mostly
|
||||
# compliant with JSONRPC specification. http://json-rpc.org/wiki/specification
|
||||
class MeshMessage
|
||||
attr_accessor :method, :params, :id
|
||||
|
||||
def initialize(method:, params: {}, id: '')
|
||||
@method, @params, @id = method, params, id
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,50 +0,0 @@
|
|||
require 'json'
|
||||
require 'time'
|
||||
require_relative 'mesh_message'
|
||||
require_relative '../command_objects/build_mesh_message'
|
||||
require_relative '../command_objects/dispose_trash_message'
|
||||
require_relative '../command_objects/resolve_controller'
|
||||
|
||||
module FBPi
|
||||
# Every time a message comes in from MeshBlu, a new MessageHandler is created.
|
||||
# Take a bot, message hash and MeshBlu connection and routes it to a specific
|
||||
# controller.
|
||||
class MessageHandler
|
||||
attr_accessor :message, :bot, :mesh
|
||||
|
||||
## general handling messages
|
||||
def initialize(mqtt_message, bot, mesh)
|
||||
@bot, @mesh = bot, mesh
|
||||
@message = BuildMeshMessage.run!(message: mqtt_message)
|
||||
rescue Mutations::ValidationException => e
|
||||
puts "BOT WAS UNABLE TO PARSE MALFORMED MESSAGE! DANGER IMMINENT!"
|
||||
@message = DisposeTrashMessage.run!(mqtt_message)
|
||||
end
|
||||
|
||||
def call
|
||||
controller_klass = ResolveController.run!(method: message.method)
|
||||
controller_klass.new(message, bot, mesh).call
|
||||
rescue Exception => e
|
||||
send_error(e)
|
||||
end
|
||||
|
||||
# Make a new instance and call() it.
|
||||
def self.call(message, bot, mesh)
|
||||
self.new(message, bot, mesh).call
|
||||
end
|
||||
|
||||
def send_error(error)
|
||||
msg = "#{error.message} @ #{error.backtrace.first}"
|
||||
bot.log msg
|
||||
reply 'error', message: error.message, backtrace: error.backtrace
|
||||
end
|
||||
|
||||
def reply(method, reslt = {})
|
||||
SendMeshResponse.run!(original_message: message,
|
||||
mesh: mesh,
|
||||
method: method,
|
||||
result: reslt)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,37 +0,0 @@
|
|||
require 'em/mqtt'
|
||||
|
||||
class MQTTAdapter
|
||||
def initialize(username,
|
||||
password,
|
||||
host = FBPi::Settings.mqtt_url,
|
||||
port = FBPi::Settings.mqtt_port)
|
||||
@username = username
|
||||
@password = password
|
||||
@host = host
|
||||
@port = port
|
||||
end
|
||||
|
||||
def connect(&blk)
|
||||
puts "Conencting to #{@host}:#{@port}"
|
||||
@client = EventMachine::MQTT::ClientConnection.connect(host: @host,
|
||||
port: @port,
|
||||
username: @username,
|
||||
password: @password)
|
||||
@client.subscribe("bot/#{@username}/request")
|
||||
@client.receive_callback(&blk)
|
||||
puts "Connected!"
|
||||
end
|
||||
|
||||
def data(payload)
|
||||
@client.publish("bot/#{username}/notification", payload.to_json)
|
||||
end
|
||||
|
||||
def emit(_channel, payload)
|
||||
@client.publish("bot/#{username}/response", payload.to_json)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :username, :password
|
||||
|
||||
end
|
|
@ -1,3 +0,0 @@
|
|||
# Stores information about a single organism under the FarmBot's care.
|
||||
class Plant < ActiveRecord::Base
|
||||
end
|
|
@ -1,7 +0,0 @@
|
|||
# A schedule will store information about when and how often a Sequence should
|
||||
# execute.
|
||||
class Schedule < ActiveRecord::Base
|
||||
UNITS_OF_TIME = %w(minutely hourly daily weekly monthly yearly)
|
||||
belongs_to :sequence, dependent: :destroy
|
||||
validates_presence_of :sequence
|
||||
end
|
|
@ -1,24 +0,0 @@
|
|||
require_relative "../command_objects/sync_bot"
|
||||
# References a collection of steps that the bot will execute, eg: "Plant tomato
|
||||
# seeds". Sequences are stored procedures that are executed in the real world at
|
||||
# a specified time (through the use of Schedule objects) or immediately.
|
||||
class Sequence < ActiveRecord::Base
|
||||
has_many :schedules
|
||||
has_many :steps, dependent: :destroy
|
||||
|
||||
def exec(bot)
|
||||
steps
|
||||
.sort{ |a, b| a.position <=> b.position}
|
||||
.map { |step| step.execute(bot) }
|
||||
end
|
||||
|
||||
def try_sync(bot)
|
||||
begin
|
||||
FBPi::SyncBot.run!(bot: bot)
|
||||
rescue => e
|
||||
puts 'WARN: Could not sync sequences.'
|
||||
puts e.class
|
||||
puts e.message
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,67 +0,0 @@
|
|||
require 'pstore'
|
||||
# Farmbot needs a way to persist some variables across reboots.
|
||||
# We have SQLite, but sometimes its overkill for these use cases.
|
||||
# One example is the bot's current X, Y, Z coords. We need to know the bot's
|
||||
# position when powering down, and saving it to SQL is a lot of work for simple
|
||||
# key/value pairs. This is where PStore (and the StatusStorage class) come in.
|
||||
# StatusStorage is child of Ruby's `PStore` class, which allows you to create
|
||||
# hash-like objects that are stored to disk (*.pstore). Farmbot uses that to
|
||||
# remember settings after reboots. For sanity, settings are put into namespaces.
|
||||
module FBPi
|
||||
class StatusStorage < PStore
|
||||
class InvalidNamespace < Exception; end
|
||||
|
||||
NAMESPACES = {
|
||||
bot: "Used for storage of Arduino specific settings, like the status "\
|
||||
"register object inside the Arduino.",
|
||||
pi: "Used for storage of Raspberry-Pi specific settings, such as the "\
|
||||
"time of :LAST_SYNC, etc.",
|
||||
misc: "Anything else."
|
||||
}
|
||||
|
||||
# Human readable explanation for use in exceptions
|
||||
NAMESPACE_EXPLANATIONS = NAMESPACES
|
||||
.inject("\n") { |a, (k, v)| a += "#{k.inspect} => #{v}\n" }
|
||||
|
||||
def initialize(*args)
|
||||
super
|
||||
transaction { |i| set_namespaces(i) }
|
||||
end
|
||||
|
||||
# Creates namespaces (hashes) in the store if it has not yet been set.
|
||||
def set_namespaces(info)
|
||||
NAMESPACES.keys.each { |k| if info[k] then nil else info[k] = {} end }
|
||||
end
|
||||
|
||||
|
||||
def update_attributes(namespace = :none, hash)
|
||||
validate_namespace(namespace)
|
||||
hash.each do |key, value|
|
||||
transaction { self[namespace].merge!(key => value) }
|
||||
end
|
||||
end
|
||||
|
||||
def fetch(namespace = :none, key)
|
||||
validate_namespace(namespace)
|
||||
transaction { self[namespace][key] }
|
||||
end
|
||||
|
||||
def to_h(namespace = :none)
|
||||
validate_namespace(namespace)
|
||||
transaction do
|
||||
self[namespace]
|
||||
.keys
|
||||
.reduce({}) { |hash, root| hash[root] = self[namespace][root]; hash }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate_namespace(namespace)
|
||||
return if NAMESPACES.keys.include?(namespace)
|
||||
raise InvalidNamespace, """You tried to access a status_storage namespace\
|
||||
of '#{namespace}' while accessing `StatusStorage`. Try one of these\
|
||||
instead:#{NAMESPACE_EXPLANATIONS}""".squeeze(" ")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,17 +0,0 @@
|
|||
require_relative '../command_objects/exec_step'
|
||||
# A step represents one piece of a sequence. For example:
|
||||
# "Sleep for 3 seconds", "perform an emergency stop", "Move X to 100"
|
||||
class Step < ActiveRecord::Base
|
||||
attr_accessor :command
|
||||
|
||||
COMMANDS = %w(emergency_stop home_all home_x home_y home_z move_absolute
|
||||
move_relative pin_write read_parameter read_status write_parameter wait
|
||||
send_message if_statement read_pin)
|
||||
|
||||
belongs_to :sequence
|
||||
|
||||
def execute(bot)
|
||||
FBPi::ExecStep.run!(bot: bot, step: self)
|
||||
rescue SystemStackError; bot.log("Endless loop in '#{sequence.name}'.")
|
||||
end
|
||||
end
|
|
@ -0,0 +1,51 @@
|
|||
defmodule Mqtt.Client do
|
||||
use Hulaaki.Client
|
||||
def on_connect(message: message, state: state) do
|
||||
GenServer.call(state.parent, {:connect, message})
|
||||
end
|
||||
def on_connect_ack(message: message, state: state) do
|
||||
GenServer.call(state.parent, {:connect_ack, message})
|
||||
end
|
||||
def on_publish(message: message, state: state) do
|
||||
GenServer.call(state.parent, {:publish, message})
|
||||
end
|
||||
def on_subscribed_publish(message: message, state: state) do
|
||||
GenServer.call(state.parent, {:subscribed_publish, message})
|
||||
end
|
||||
def on_subscribed_publish_ack(message: message, state: state) do
|
||||
GenServer.call(state.parent, {:subscribed_publish_ack, message})
|
||||
end
|
||||
def on_publish_receive(message: message, state: state) do
|
||||
GenServer.call(state.parent, {:publish_receive, message})
|
||||
end
|
||||
def on_publish_release(message: message, state: state) do
|
||||
GenServer.call(state.parent, {:publish_release, message})
|
||||
end
|
||||
def on_publish_complete(message: message, state: state) do
|
||||
GenServer.call(state.parent, {:publish_complete, message})
|
||||
end
|
||||
def on_publish_ack(message: message, state: state) do
|
||||
GenServer.call(state.parent, {:publish_ack, message})
|
||||
end
|
||||
def on_subscribe(message: message, state: state) do
|
||||
GenServer.call(state.parent, {:subscribe, message})
|
||||
end
|
||||
def on_subscribe_ack(message: message, state: state) do
|
||||
GenServer.call(state.parent, {:subscribe_ack, message})
|
||||
end
|
||||
def on_unsubscribe(message: message, state: state) do
|
||||
GenServer.call(state.parent, {:unsubscribe, message})
|
||||
end
|
||||
def on_unsubscribe_ack(message: message, state: state) do
|
||||
GenServer.call(state.parent, {:unsubscribe_ack, message})
|
||||
end
|
||||
def on_ping(message: message, state: state) do
|
||||
GenServer.call(state.parent, {:ping, message})
|
||||
end
|
||||
def on_pong(message: message, state: state) do
|
||||
GenServer.call(state.parent, {:pong, message})
|
||||
end
|
||||
def on_disconnect(message: message, state: state) do
|
||||
GenServer.call(state.parent, {:disconnect, message})
|
||||
end
|
||||
end
|
|
@ -0,0 +1,174 @@
|
|||
defmodule MqttHandler do
|
||||
require GenServer
|
||||
require Logger
|
||||
|
||||
defp build_last_will_message do
|
||||
RPCMessageHandler.log_msg("Something TERRIBLE Happened. Bot going offline.")
|
||||
end
|
||||
|
||||
@doc """
|
||||
"tries to log into mqtt."
|
||||
"""
|
||||
def log_in(err_wait_time\\ 10000) do
|
||||
mqtt_host = Map.get(token, "unencoded") |> Map.get("mqtt")
|
||||
mqtt_user = Map.get(token, "unencoded") |> Map.get("bot")
|
||||
mqtt_pass = Map.get(token, "encoded")
|
||||
options = [client_id: mqtt_user,
|
||||
username: mqtt_user,
|
||||
password: mqtt_pass,
|
||||
host: mqtt_host,
|
||||
port: 1883,
|
||||
timeout: 5000,
|
||||
keep_alive: 500,
|
||||
will_topic: "bot/#{bot}/from_device",
|
||||
will_message: build_last_will_message,
|
||||
will_qos: 0,
|
||||
will_retain: 0]
|
||||
case GenServer.call(MqttHandler, {:log_in, options}) do
|
||||
{:error, reason} -> Logger.debug("Error connecting. #{inspect reason}")
|
||||
Process.sleep(err_wait_time)
|
||||
log_in(err_wait_time + 10000) # increment the sleep time for teh lawls
|
||||
_ -> :ok
|
||||
end
|
||||
end
|
||||
|
||||
def init(_args) do
|
||||
Mqtt.Client.start_link(%{parent: __MODULE__})
|
||||
end
|
||||
|
||||
def start_link(args) do
|
||||
handler = GenServer.start_link(__MODULE__, args, name: __MODULE__)
|
||||
log_in
|
||||
Logger.debug("MQTT ONLINE")
|
||||
handler
|
||||
end
|
||||
|
||||
def handle_call({:connect, _message}, _from, client) do
|
||||
{:reply, :ok, client}
|
||||
end
|
||||
|
||||
def handle_call({:connect_ack, _message}, from, client) do
|
||||
options = [id: 24756, topics: ["bot/#{bot}/from_clients"], qoses: [0]]
|
||||
spawn fn ->
|
||||
NetworkSupervisor.set_time # Should NOT be here
|
||||
Mqtt.Client.subscribe(client, options)
|
||||
handle_call({:emit, RPCMessageHandler.log_msg("Bot Bootstrapping")}, from, client)
|
||||
Command.read_all_pins # I'm truly sorry these are here
|
||||
Command.read_all_params
|
||||
BotSync.sync
|
||||
end
|
||||
keep_connection_alive
|
||||
{:reply, :ok, client}
|
||||
end
|
||||
|
||||
def handle_call({:publish, _message}, _from, client) do
|
||||
{:reply, :ok, client}
|
||||
end
|
||||
|
||||
def handle_call({:subscribed_publish, message}, _from, client) do
|
||||
Map.get(message, :message) |> Poison.decode! |>
|
||||
RPCMessageManager.sync_notify
|
||||
{:reply, :ok, client}
|
||||
end
|
||||
|
||||
def handle_call({:subscribed_publish_ack, _message}, _from, client) do
|
||||
{:reply, :ok, client}
|
||||
end
|
||||
|
||||
def handle_call({:publish_receive, _message}, _from, client) do
|
||||
{:reply, :ok, client}
|
||||
end
|
||||
|
||||
def handle_call({:publish_release, _message}, _from, client) do
|
||||
{:reply, :ok, client}
|
||||
end
|
||||
|
||||
def handle_call({:publish_complete, _message}, _from, client) do
|
||||
{:reply, :ok, client}
|
||||
end
|
||||
|
||||
def handle_call({:publish_ack, _message}, _from, client) do
|
||||
{:reply, :ok, client}
|
||||
end
|
||||
|
||||
def handle_call({:subscribe, _message}, _from, client) do
|
||||
{:reply, :ok, client}
|
||||
end
|
||||
|
||||
def handle_call({:subscribe_ack, _message}, from, client) do
|
||||
Logger.debug("Subscribed.")
|
||||
handle_call({:emit, RPCMessageHandler.log_msg("Bot Online")}, from, client)
|
||||
end
|
||||
|
||||
def handle_call({:unsubscribe, _message}, _from, client) do
|
||||
{:reply, :ok, client}
|
||||
end
|
||||
|
||||
def handle_call({:unsubscribe_ack, _message}, _from, client) do
|
||||
{:reply, :ok, client}
|
||||
end
|
||||
|
||||
def handle_call({:ping, _message}, _from, client) do
|
||||
{:reply, :ok, client}
|
||||
end
|
||||
|
||||
def handle_call({:disconnect, _message}, _from, client) do
|
||||
{:reply, :ok, client}
|
||||
end
|
||||
|
||||
def handle_call({:pong, _message}, _from, client) do
|
||||
{:reply, :ok, client}
|
||||
end
|
||||
|
||||
def handle_call({:log_in, options}, _from, client) do
|
||||
case Mqtt.Client.connect(client, options) do
|
||||
{:error, reason} -> {:reply, {:error, reason}, client}
|
||||
_ -> {:reply, :ok, client}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_call({:emit, message}, _from, client) when is_bitstring(message) do
|
||||
options = [ id: 1234,
|
||||
topic: "bot/#{bot}/from_device",
|
||||
message: message,
|
||||
dup: 1, qos: 1, retain: 0]
|
||||
spawn fn -> Mqtt.Client.publish(client, options) end
|
||||
{:reply, :ok, client}
|
||||
end
|
||||
|
||||
def handle_call(thing, _from, client) do
|
||||
Logger.debug("Unhandled Thing #{inspect thing}")
|
||||
{:reply, :ok, client}
|
||||
end
|
||||
|
||||
def handle_cast(event, state) do
|
||||
Logger.debug("#{inspect event}")
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info({:keep_alive}, client) do
|
||||
Mqtt.Client.ping(client)
|
||||
keep_connection_alive
|
||||
{:noreply, client}
|
||||
end
|
||||
|
||||
def emit(message) when is_bitstring(message) do
|
||||
GenServer.call(__MODULE__, {:emit, message})
|
||||
end
|
||||
|
||||
defp bot do
|
||||
Map.get(token, "unencoded") |> Map.get("bot")
|
||||
end
|
||||
|
||||
defp token do
|
||||
Auth.fetch_token
|
||||
end
|
||||
|
||||
defp keep_connection_alive do
|
||||
Process.send_after(__MODULE__, {:keep_alive}, 15000)
|
||||
end
|
||||
|
||||
def terminate(reason, _state) do
|
||||
Logger.debug("MqttHandler died. #{inspect reason}")
|
||||
end
|
||||
end
|
|
@ -0,0 +1,14 @@
|
|||
defmodule MqttSupervisor do
|
||||
require Logger
|
||||
use Supervisor
|
||||
|
||||
def init(_args) do
|
||||
children = [worker(MqttHandler, [[]], restart: :permanent)]
|
||||
supervise(children, strategy: :one_for_one, name: __MODULE__)
|
||||
end
|
||||
|
||||
def start_link(args) do
|
||||
Logger.debug("MQTT INIT")
|
||||
Supervisor.start_link(__MODULE__, args)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
defmodule Network.EventManager do
|
||||
use GenEvent
|
||||
require Logger
|
||||
|
||||
def handle_event({:udhcpc, _, :bound, %{ipv4_address: address}}, state) do
|
||||
on_ip(address)
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
def handle_event(_event, state) do
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
def on_ip(address) do
|
||||
Logger.debug("WE ARE CONNECTED")
|
||||
Wifi.set_connected(true)
|
||||
Node.stop
|
||||
full_node_name = "farmbot@#{address}" |> String.to_atom
|
||||
{:ok, _pid} = Node.start(full_node_name)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,34 @@
|
|||
defmodule NetworkSupervisor do
|
||||
require Logger
|
||||
use Supervisor
|
||||
@env Mix.env
|
||||
def start_link(_args) do
|
||||
Logger.debug("Starting Network")
|
||||
Nerves.Networking.setup(:eth0) # eh
|
||||
children = [ worker(Wifi, [[]]) ]
|
||||
opts = [strategy: :one_for_all, name: NetworkSupervisor]
|
||||
Supervisor.start_link(children, opts)
|
||||
end
|
||||
|
||||
def set_time do
|
||||
case @env do
|
||||
:prod ->
|
||||
Logger.debug("Setting time. If it seems to hang here, reboot. Need a better ntp pool.")
|
||||
System.cmd("ntpd", ["-q",
|
||||
"-p", "0.pool.ntp.org",
|
||||
"-p", "1.pool.ntp.org",
|
||||
"-p", "2.pool.ntp.org",
|
||||
"-p", "3.pool.ntp.org"])
|
||||
check_time_set
|
||||
Logger.debug("Time set.")
|
||||
:ok
|
||||
_ -> :ok
|
||||
end
|
||||
end
|
||||
|
||||
def check_time_set do
|
||||
if :os.system_time(:seconds) < 1474929 do
|
||||
check_time_set # wait until time is set
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,141 @@
|
|||
defmodule Wifi do
|
||||
@path Application.get_env(:fb, :ro_path)
|
||||
@env Mix.env
|
||||
use GenServer
|
||||
require Logger
|
||||
defp load do
|
||||
case File.read("#{@path}/network.config") do
|
||||
{:ok, contents} -> {:wpa_supplicant, :erlang.binary_to_term(contents)}
|
||||
_ -> :nope
|
||||
end
|
||||
end
|
||||
|
||||
defp save({ssid, password}) do
|
||||
File.write("#{@path}/network.config", :erlang.term_to_binary({ssid, password}))
|
||||
end
|
||||
|
||||
def init(_args) do
|
||||
System.cmd("epmd", ["-daemon"])
|
||||
GenEvent.add_handler(Nerves.NetworkInterface.event_manager(), Network.EventManager, [])
|
||||
initial_state = load
|
||||
case initial_state do
|
||||
{:wpa_supplicant, {ssid, pass}} -> start_wifi_client(ssid, pass)
|
||||
{:ok, {:wpa, connected: false}}
|
||||
_ -> start_hostapd_deps(@env) # These only need to be started onece per boot
|
||||
start_hostapd(@env)
|
||||
{:ok, :hostapd}
|
||||
end
|
||||
end
|
||||
|
||||
def start_link(args) do
|
||||
GenServer.start_link(__MODULE__, args, name: __MODULE__)
|
||||
end
|
||||
|
||||
defp start_wifi_client(ssid, pass) when is_bitstring(ssid) and is_bitstring(pass) do
|
||||
spawn_link fn -> Nerves.InterimWiFi.setup "wlan0", ssid: ssid, key_mgmt: :"WPA-PSK", psk: pass end
|
||||
end
|
||||
|
||||
# Blatently ripped off from @joelbyler
|
||||
# https://github.com/joelbyler/elixir_conf_chores/blob/f13298f9185b850fdfaad0448f03a03b3067a85c/apps/firmware/lib/firmware.ex
|
||||
defp start_hostapd_deps(:prod) do
|
||||
System.cmd("httpd",["-p", "80", "-h", "/www"]) |> print_cmd_result
|
||||
System.cmd("ip", ["link", "set", "wlan0", "up"]) |> print_cmd_result
|
||||
System.cmd("ip", ["addr", "add", "192.168.24.1/24", "dev", "wlan0"]) |> print_cmd_result
|
||||
System.cmd("dnsmasq", ["--dhcp-lease", "/root/dnsmasq.lease"]) |> print_cmd_result
|
||||
end
|
||||
|
||||
defp start_hostapd_deps(_) do
|
||||
nil
|
||||
end
|
||||
|
||||
defp start_hostapd(:prod) do
|
||||
System.cmd("hostapd", ["-B", "-d", "/etc/hostapd/hostapd.conf"]) |> print_cmd_result
|
||||
end
|
||||
|
||||
defp start_hostapd(_) do
|
||||
nil
|
||||
end
|
||||
|
||||
defp print_cmd_result({_message, 0}) do
|
||||
# IO.puts message
|
||||
nil
|
||||
end
|
||||
|
||||
defp print_cmd_result({message, err_no}) do
|
||||
IO.puts "ERROR (#{err_no}): #{message}"
|
||||
end
|
||||
|
||||
def connect(ssid, pass) do
|
||||
save({ssid, pass})
|
||||
GenServer.cast(__MODULE__, {:connect, ssid, pass})
|
||||
end
|
||||
|
||||
def set_connected(con) when is_boolean(con) do
|
||||
GenServer.cast(__MODULE__, {:connected, con})
|
||||
end
|
||||
|
||||
def scan do
|
||||
GenServer.call(__MODULE__, :scan, 10000)
|
||||
end
|
||||
|
||||
def connected? do
|
||||
GenServer.call(__MODULE__, :am_i_connected)
|
||||
end
|
||||
|
||||
def handle_cast({:connect, ssid, pass}, :hostapd) do
|
||||
Logger.debug("trying to switch from hostapd to wpa_supplicant ")
|
||||
System.cmd("sh", ["-c", "killall hostapd"]) |> print_cmd_result
|
||||
System.cmd("ip", ["link", "set", "wlan0", "down"]) |> print_cmd_result
|
||||
System.cmd("ip", ["addr", "del", "192.168.24.1/24", "dev", "wlan0"]) |> print_cmd_result
|
||||
System.cmd("ip", ["link", "set", "wlan0", "up"]) |> print_cmd_result
|
||||
start_wifi_client(ssid, pass)
|
||||
{:noreply, :hostapd}
|
||||
end
|
||||
|
||||
def handle_cast({:connect, ssid, pass}, {:wpa, connected: _con}) do
|
||||
start_wifi_client(ssid, pass)
|
||||
{:noreply,{ssid, pass, connected: false}}
|
||||
end
|
||||
|
||||
def handle_cast({:connected, con}, {:wpa, connected: _old}) do
|
||||
{:noreply, {:wpa, connected: con} }
|
||||
end
|
||||
|
||||
def handle_cast({:connected, con}, :hostapd) do
|
||||
{:noreply, {:wpa, connected: con} }
|
||||
end
|
||||
|
||||
def handle_call(:scan, _from, {:wpa, connected: are_connected} ) do
|
||||
{hc, 0} = System.cmd("iw", ["wlan0", "scan", "ap-force"])
|
||||
{:reply, hc |> clean_ssid, {:wpa, connected: are_connected}}
|
||||
end
|
||||
|
||||
def handle_call(:scan, _from, :hostapd ) do
|
||||
{hc, 0} = System.cmd("iw", ["wlan0", "scan", "ap-force"])
|
||||
{:reply, hc |> clean_ssid, :hostapd}
|
||||
end
|
||||
|
||||
# IF hostapd always no.
|
||||
def handle_call(:am_i_connected, _from, :hostapd) do
|
||||
case @env do
|
||||
:prod -> {:reply, false, :hostapd}
|
||||
_ -> {:reply, true, :hostapd}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_call(:am_i_connected, _from, {:wpa, connected: are_connected}) do
|
||||
{:reply, are_connected, {:wpa, connected: are_connected}}
|
||||
end
|
||||
|
||||
def handle_call(:get_state, _crom, state) do
|
||||
{:reply, state, state}
|
||||
end
|
||||
|
||||
defp clean_ssid(hc) do
|
||||
hc
|
||||
|> String.replace("\t", "")
|
||||
|> String.split("\n")
|
||||
|> Enum.filter(fn(s) -> String.contains?(s, "SSID") end)
|
||||
|> Enum.map(fn(z) -> String.replace(z, "SSID: ", "") end)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,40 @@
|
|||
defmodule Farmbot do
|
||||
use GenServer
|
||||
|
||||
def init(args) do
|
||||
resp = HTTPotion.get(args)
|
||||
Process.send_after(__MODULE__, :emit_message, 10000)
|
||||
case resp do
|
||||
%HTTPotion.ErrorResponse{message: _} -> {:ok, %{}}
|
||||
_ -> {:ok, Poison.decode!(resp.body)}
|
||||
end
|
||||
end
|
||||
|
||||
def start_link(args) do
|
||||
GenServer.start_link(__MODULE__, args, name: __MODULE__)
|
||||
end
|
||||
|
||||
def get_state do
|
||||
GenServer.call(__MODULE__, :get_state)
|
||||
end
|
||||
|
||||
def download_personality(url) do
|
||||
resp = HTTPotion.get(url)
|
||||
case resp do
|
||||
%HTTPotion.ErrorResponse{message: _} -> {:error, :could_not_get_personality}
|
||||
_ -> GenServer.call(__MODULE__, {:update_personality, Poison.decode!(resp.body) })
|
||||
end
|
||||
end
|
||||
|
||||
def handle_call(:get_state, _from, state) do
|
||||
{:reply, state, state}
|
||||
end
|
||||
|
||||
def handle_call({:update_personality, new_personality_map}, _from, _old_personality_map) do
|
||||
{:reply, :ok, new_personality_map}
|
||||
end
|
||||
|
||||
def handle_info(:emit_message,personality_map) do
|
||||
{:noreply, personality_map}
|
||||
end
|
||||
end
|
|
@ -1,36 +0,0 @@
|
|||
# Wraps around the Farmbot REST client gem to provide RPi specific functions.
|
||||
require 'farmbot-resource'
|
||||
require_relative 'secret_file'
|
||||
|
||||
module FBPi
|
||||
class RPiRestClient < FbResource::Client
|
||||
CREDENTIALS_FILE = 'db/secrets.txt'
|
||||
TRANSLATIONS = {
|
||||
"sub"=> "email",
|
||||
"iat"=> "token_issued_at",
|
||||
"jti"=> "token_id",
|
||||
"iss"=> "webapp_url",
|
||||
"exp"=> "token_expiration",
|
||||
"mqtt"=>"mqtt_url",
|
||||
"bot"=> "device_uuid"
|
||||
}
|
||||
def initialize(url)
|
||||
public_key = FBPi::RPiRestClient.public_key(url)
|
||||
credentials = SecretFile.new(public_key, CREDENTIALS_FILE).cipher_text
|
||||
token = FbResource::Client.get_token(credentials: credentials, url: url)
|
||||
unpack_token_settings(token)
|
||||
super() { |c| config.token = token }
|
||||
end
|
||||
|
||||
# To prevent adding a bunch of setup steps, we pull most config out of the
|
||||
# API token. That way users don't need to worry about pointing to the right
|
||||
# servers or updating settings.
|
||||
def unpack_token_settings(token)
|
||||
settings = JSON.parse(Base64.decode64(token.split(".")[1]))
|
||||
TRANSLATIONS.each do |key, val|
|
||||
FBPi::Settings[val] = settings[key] || "#{key} not set."
|
||||
end
|
||||
FBPi::Settings.save
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,41 +0,0 @@
|
|||
# a "Write only" file dump for sharing secrets. The web server has the private
|
||||
# key and is the only party able to read the file.
|
||||
require 'securerandom'
|
||||
require 'base64'
|
||||
|
||||
class SecretFile
|
||||
attr_reader :public_key, :file_path, :cipher_text
|
||||
|
||||
def initialize(public_key, file_path)
|
||||
@public_key, @file_path = public_key, file_path
|
||||
end
|
||||
|
||||
def self.save_password(public_key, email, password)
|
||||
self
|
||||
.new(public_key, "db/secrets.txt")
|
||||
.write({email: email,
|
||||
password: password})
|
||||
end
|
||||
|
||||
def write(payload_obj)
|
||||
# Add a UUID for comparison / debugging purposes
|
||||
text = payload_obj.merge!(id: SecureRandom.uuid, version: 1).to_json
|
||||
self.cipher_text = text
|
||||
cipher_text
|
||||
end
|
||||
|
||||
def cipher_text=(text)
|
||||
text = Base64.encode64(public_key.public_encrypt(text))
|
||||
File.open(file_path, 'w') { |f| f.write(text) }
|
||||
@cipher_text = text
|
||||
end
|
||||
|
||||
def cipher_text
|
||||
if @cipher_text || File.file?(file_path)
|
||||
return @cipher_text ||= File.read(file_path)
|
||||
else
|
||||
raise "Attempted to access bot credentials, but none were found. " \
|
||||
"Did you run `ruby setup.rb`?"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,95 @@
|
|||
defmodule Sequence do
|
||||
@moduledoc """
|
||||
LOL
|
||||
"""
|
||||
use GenServer
|
||||
require Logger
|
||||
require Kernel
|
||||
def init(_) do
|
||||
{:ok, %{}}
|
||||
end
|
||||
|
||||
def start_link(_) do
|
||||
GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
|
||||
end
|
||||
|
||||
def handle_cast({:add_step, {position, function}}, steps) do
|
||||
{:noreply, Map.update(steps, position, function, fn _x -> function end)}
|
||||
end
|
||||
|
||||
def handle_call({:get_steps}, _from, steps) do
|
||||
{:reply, steps, steps}
|
||||
end
|
||||
|
||||
def handle_call({:clean_steps}, _from, steps) do
|
||||
{:reply, steps, %{}}
|
||||
end
|
||||
|
||||
def handle_call({:execute, _id}, _from, steps) do
|
||||
Logger.debug("#{inspect steps}")
|
||||
ordered_steps = Enum.sort(steps)
|
||||
ordered_list = Enum.map(ordered_steps, fn({_pos, step}) -> step end)
|
||||
Logger.debug("#{inspect ordered_list}")
|
||||
pid = spawn fn -> Enum.each(ordered_list, fn step -> Kernel.apply(step, []) end) end
|
||||
{:reply, pid, %{}}
|
||||
end
|
||||
|
||||
def execute(id \\nil) do
|
||||
GenServer.call(__MODULE__, {:execute, id})
|
||||
end
|
||||
|
||||
# Pattern match available commands
|
||||
def add_step(step,id \\ nil)
|
||||
def add_step(%{"command" => command, "message_type" => "move_absolute",
|
||||
"position" => position}, _id) do
|
||||
#TODO: i think this would allow negative numbers
|
||||
xpos = String.to_integer(Map.get(command, "x", nil))
|
||||
ypos = String.to_integer(Map.get(command, "y", nil))
|
||||
zpos = String.to_integer(Map.get(command, "z", nil))
|
||||
speed = String.to_integer(Map.get(command, "speed", nil))
|
||||
GenServer.cast(__MODULE__, {:add_step, {position, fn -> Command.move_absolute(xpos,ypos,zpos,speed) end}})
|
||||
end
|
||||
|
||||
def add_step(%{"command" => %{"speed" => speed,
|
||||
"x" => x, "y" => y, "z" => z},
|
||||
"id" => _istep_d,
|
||||
"message_type" => "move_relative",
|
||||
"position" => position, "sequence_id" => _seq_id}, _id) do
|
||||
GenServer.cast(__MODULE__,
|
||||
{:add_step, {position,
|
||||
fn -> Command.move_relative(%{x: String.to_integer(x), y: String.to_integer(y), z: String.to_integer(z), speed: speed}) end}})
|
||||
end
|
||||
|
||||
# Write pin (MODE IS NOT WORKING?)
|
||||
def add_step(%{"command" => %{"mode" => _mode, "pin" => pin, "value" => value}, "message_type" => "pin_write", "position" => position}, _id) do
|
||||
GenServer.cast(__MODULE__,
|
||||
{:add_step, {position, fn -> Command.write_pin(String.to_integer(pin), String.to_integer(value),0) end}})
|
||||
end
|
||||
|
||||
# Process.sleep seems to be off by a couple seconds?
|
||||
def add_step(%{"command" => %{"value" => milis}, "message_type" => "wait", "position" => position}, _id) do
|
||||
GenServer.cast(__MODULE__,
|
||||
{:add_step, {position, fn -> Process.sleep(String.to_integer(milis)) end}})
|
||||
end
|
||||
|
||||
def add_step(%{"command" => command, "message_type" => "read_pin", "position" => position}, _id) do
|
||||
# I dont know what this is supposed to do ???
|
||||
Logger.debug("add_step: COMMAND: #{inspect command}, POSITION: #{inspect position}")
|
||||
end
|
||||
|
||||
def add_step(step, _id) do
|
||||
Logger.debug("Unable to add step: #{inspect step}")
|
||||
end
|
||||
|
||||
def add_steps(steps, id) when is_list(steps) do
|
||||
Enum.each(steps, fn step -> add_step(step, id) end)
|
||||
end
|
||||
|
||||
def get_steps do
|
||||
GenServer.call(__MODULE__, {:get_steps})
|
||||
end
|
||||
|
||||
def clean_steps do
|
||||
GenServer.call(__MODULE__, {:clean_steps})
|
||||
end
|
||||
end
|
|
@ -0,0 +1,33 @@
|
|||
alias Experimental.{GenStage}
|
||||
defmodule SequenceHandler do
|
||||
require IEx
|
||||
use GenStage
|
||||
require Logger
|
||||
|
||||
def start_link() do
|
||||
GenStage.start_link(__MODULE__, :ok)
|
||||
end
|
||||
|
||||
def init(:ok) do
|
||||
# Bootstarp the bot here.
|
||||
{:consumer, :ok, subscribe_to: [SequenceManager]}
|
||||
end
|
||||
|
||||
def handle_events(events, _from, state) do
|
||||
for event <- events do
|
||||
do_handle(event)
|
||||
end
|
||||
{:noreply, [], state}
|
||||
end
|
||||
|
||||
def do_handle({:exec_sequence, steps, id}) when is_list(steps) do
|
||||
Sequence.clean_steps
|
||||
Sequence.add_steps(steps, id)
|
||||
Sequence.execute(id)
|
||||
end
|
||||
|
||||
# Unhandled event. Probably not implemented if it got this far.
|
||||
def do_handle(event) do
|
||||
Logger.debug("[SequenceHandler] (Probably not implemented) Unhandled Event: #{inspect event}")
|
||||
end
|
||||
end
|
|
@ -0,0 +1,34 @@
|
|||
alias Experimental.{GenStage}
|
||||
defmodule SequenceManager do
|
||||
use GenStage
|
||||
def start_link() do
|
||||
GenStage.start_link(__MODULE__, :ok, name: __MODULE__)
|
||||
end
|
||||
|
||||
def sync_notify(event, timeout \\ 5000) do
|
||||
GenStage.call(__MODULE__, {:notify, event}, timeout)
|
||||
end
|
||||
|
||||
def init(:ok) do
|
||||
{:producer, {:queue.new, 0}, dispatcher: GenStage.BroadcastDispatcher}
|
||||
end
|
||||
|
||||
def handle_call({:notify, event}, from, {queue, demand}) do
|
||||
dispatch_events(:queue.in({from, event}, queue), demand, [])
|
||||
end
|
||||
|
||||
def handle_demand(incoming_demand, {queue, demand}) do
|
||||
dispatch_events(queue, incoming_demand + demand, [])
|
||||
end
|
||||
|
||||
# This is some copy paste magic, not touching it.
|
||||
defp dispatch_events(queue, demand, events) do
|
||||
with d when d > 0 <- demand,
|
||||
{{:value, {from, event}}, queue} <- :queue.out(queue) do
|
||||
GenStage.reply(from, :ok)
|
||||
dispatch_events(queue, demand - 1, [event | events])
|
||||
else
|
||||
_ -> {:noreply, Enum.reverse(events), {queue, demand}}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
defmodule SequenceSupervisor do
|
||||
def start_link(_args) do
|
||||
import Supervisor.Spec
|
||||
children = [
|
||||
worker(SequenceManager, []),
|
||||
worker(SequenceHandler, [], id: 1),
|
||||
worker(Sequence, [[]])
|
||||
]
|
||||
Supervisor.start_link(children, strategy: :one_for_one)
|
||||
end
|
||||
|
||||
def init(_) do
|
||||
{:ok, %{}}
|
||||
end
|
||||
end
|
|
@ -0,0 +1,160 @@
|
|||
defmodule Gcode do
|
||||
@moduledoc """
|
||||
Im lazy and didn't want to parse yaml or write macros
|
||||
"""
|
||||
|
||||
def parse_code("G0") do { :move_to_location_at_given_speed_for_axis } end
|
||||
def parse_code("G1" )do { :move_to_location_on_a_straight_line } end
|
||||
def parse_code("G28") do { :move_home_all_axis } end
|
||||
def parse_code("F1" ) do { :dose_amount_of_water_using_time_in_millisecond } end
|
||||
def parse_code("F2" ) do { :dose_amount_of_water_using_flow_meter_that_measures_pulses } end
|
||||
def parse_code("F11") do { :home_x_axis } end
|
||||
def parse_code("F12") do { :home_y_axis } end
|
||||
def parse_code("F13") do { :home_z_axis } end
|
||||
def parse_code("F14") do { :calibrate_x_axis } end
|
||||
def parse_code("F15") do { :calibrate_y_axis } end
|
||||
def parse_code("F16") do { :calibrate_z_axis } end
|
||||
def parse_code("F21") do { :read_parameter } end
|
||||
def parse_code("F22") do { :write_parameter } end
|
||||
def parse_code("F23") do { :update_parameter_during_calibration } end
|
||||
def parse_code("F31") do { :read_status } end
|
||||
def parse_code("F32") do { :write_status } end
|
||||
def parse_code("F41") do { :set_a_value_on_an_arduino_pin } end
|
||||
def parse_code("F42") do { :read_a_value_from_an_arduino_pin } end
|
||||
def parse_code("F43") do { :set_the_mode_of_a_pin_in_arduino } end
|
||||
def parse_code("F44") do { :set_the_value_v_on_an_arduino_pin } end
|
||||
def parse_code("F51") do { :set_a_value_on_the_tool_mount_with_i2c } end
|
||||
def parse_code("F52") do { :read_value_from_the_tool_mount_with_i2c } end
|
||||
def parse_code("F61") do { :set_the_servo_on_the_pin_to_the_requested_angle } end
|
||||
def parse_code("F81") do { :report_end_stop } end
|
||||
def parse_code("F82") do { :report_current_position } end
|
||||
def parse_code("F83") do { :report_software_version } end
|
||||
def parse_code("E" ) do { :emergency_stop } end
|
||||
def parse_code("R0" ) do { :idle } end
|
||||
def parse_code("R1" ) do { :received } end
|
||||
def parse_code("R2" ) do { :done } end
|
||||
def parse_code("R3" ) do { :error } end
|
||||
def parse_code("R4" ) do { :busy } end
|
||||
def parse_code("R00") do { :idle } end
|
||||
def parse_code("R01") do { :received } end
|
||||
def parse_code("R02") do { :done } end
|
||||
def parse_code("R03") do { :error } end
|
||||
def parse_code("R04") do { :busy } end
|
||||
def parse_code("R21 " <> params) do { :report_parameter_value, params } end
|
||||
def parse_code("R31") do { :report_status_value } end
|
||||
def parse_code("R41 " <> params) do { :report_pin_value, params } end
|
||||
def parse_code("R81 " <> params ) do { :reporting_end_stops, params } end
|
||||
def parse_code("R82 " <> position) do { :report_current_position, position } end
|
||||
def parse_code("R83") do { :report_software_version } end
|
||||
def parse_code("R99 " <> message) do { :debug_message, message } end
|
||||
def parse_code(code) do {:unhandled_gcode, code} end
|
||||
|
||||
def parse_param("0" ) do :PARAM_VERSION end
|
||||
def parse_param("11" ) do :MOVEMENT_TIMEOUT_X end
|
||||
def parse_param("12" ) do :MOVEMENT_TIMEOUT_Y end
|
||||
def parse_param("13" ) do :MOVEMENT_TIMEOUT_Z end
|
||||
def parse_param("21" ) do :MOVEMENT_INVERT_ENDPOINTS_X end
|
||||
def parse_param("22" ) do :MOVEMENT_INVERT_ENDPOINTS_Y end
|
||||
def parse_param("23" ) do :MOVEMENT_INVERT_ENDPOINTS_Z end
|
||||
def parse_param("31" ) do :MOVEMENT_INVERT_MOTOR_X end
|
||||
def parse_param("32" ) do :MOVEMENT_INVERT_MOTOR_Y end
|
||||
def parse_param("33" ) do :MOVEMENT_INVERT_MOTOR_Z end
|
||||
def parse_param("41" ) do :MOVEMENT_STEPS_ACC_DEC_X end
|
||||
def parse_param("42" ) do :MOVEMENT_STEPS_ACC_DEC_Y end
|
||||
def parse_param("43" ) do :MOVEMENT_STEPS_ACC_DEC_Z end
|
||||
def parse_param("51" ) do :MOVEMENT_HOME_UP_X end
|
||||
def parse_param("52" ) do :MOVEMENT_HOME_UP_Y end
|
||||
def parse_param("53" ) do :MOVEMENT_HOME_UP_Z end
|
||||
def parse_param("61" ) do :MOVEMENT_MIN_SPD_X end
|
||||
def parse_param("62" ) do :MOVEMENT_MIN_SPD_Y end
|
||||
def parse_param("63" ) do :MOVEMENT_MIN_SPD_Z end
|
||||
def parse_param("71" ) do :MOVEMENT_MAX_SPD_X end
|
||||
def parse_param("72" ) do :MOVEMENT_MAX_SPD_Y end
|
||||
def parse_param("73" ) do :MOVEMENT_MAX_SPD_Z end
|
||||
def parse_param("101") do :ENCODER_ENABLED_X end
|
||||
def parse_param("102") do :ENCODER_ENABLED_Y end
|
||||
def parse_param("103") do :ENCODER_ENABLED_Z end
|
||||
def parse_param("111") do :ENCODER_MISSED_STEPS_MAX_X end
|
||||
def parse_param("112") do :ENCODER_MISSED_STEPS_MAX_Y end
|
||||
def parse_param("113") do :ENCODER_MISSED_STEPS_MAX_Z end
|
||||
def parse_param("121") do :ENCODER_MISSED_STEPS_DECAY_X end
|
||||
def parse_param("122") do :ENCODER_MISSED_STEPS_DECAY_Y end
|
||||
def parse_param("123") do :ENCODER_MISSED_STEPS_DECAY_Z end
|
||||
def parse_param("141") do :MOVEMENT_AXIS_NR_STEPS_X end
|
||||
def parse_param("142") do :MOVEMENT_AXIS_NR_STEPS_Y end
|
||||
def parse_param("143") do :MOVEMENT_AXIS_NR_STEPS_Z end
|
||||
def parse_param("201") do :PIN_GUARD_1_PIN_NR end
|
||||
def parse_param("202") do :PIN_GUARD_1_TIME_OUT end
|
||||
def parse_param("203") do :PIN_GUARD_1_ACTIVE_STATE end
|
||||
def parse_param("205") do :PIN_GUARD_2_PIN_NR end
|
||||
def parse_param("206") do :PIN_GUARD_2_TIME_OUT end
|
||||
def parse_param("207") do :PIN_GUARD_2_ACTIVE_STATE end
|
||||
def parse_param("211") do :PIN_GUARD_3_PIN_NR end
|
||||
def parse_param("212") do :PIN_GUARD_3_TIME_OUT end
|
||||
def parse_param("213") do :PIN_GUARD_3_ACTIVE_STATE end
|
||||
def parse_param("215") do :PIN_GUARD_4_PIN_NR end
|
||||
def parse_param("216") do :PIN_GUARD_4_TIME_OUT end
|
||||
def parse_param("217") do :PIN_GUARD_4_ACTIVE_STATE end
|
||||
def parse_param("221") do :PIN_GUARD_5_PIN_NR end
|
||||
def parse_param("222") do :PIN_GUARD_5_TIME_OUT end
|
||||
def parse_param("223") do :PIN_GUARD_5_ACTIVE_STATE end
|
||||
def parse_param(param) when is_integer(param) do
|
||||
parse_param("#{param}")
|
||||
end
|
||||
|
||||
def parse_param(:PARAM_VERSION) do 0 end
|
||||
def parse_param(:MOVEMENT_TIMEOUT_X) do 11 end
|
||||
def parse_param(:MOVEMENT_TIMEOUT_Y) do 12 end
|
||||
def parse_param(:MOVEMENT_TIMEOUT_Z) do 13 end
|
||||
def parse_param(:MOVEMENT_INVERT_ENDPOINTS_X) do 21 end
|
||||
def parse_param(:MOVEMENT_INVERT_ENDPOINTS_Y) do 22 end
|
||||
def parse_param(:MOVEMENT_INVERT_ENDPOINTS_Z) do 23 end
|
||||
def parse_param(:MOVEMENT_INVERT_MOTOR_X) do 31 end
|
||||
def parse_param(:MOVEMENT_INVERT_MOTOR_Y) do 32 end
|
||||
def parse_param(:MOVEMENT_INVERT_MOTOR_Z) do 33 end
|
||||
def parse_param(:MOVEMENT_STEPS_ACC_DEC_X) do 41 end
|
||||
def parse_param(:MOVEMENT_STEPS_ACC_DEC_Y) do 42 end
|
||||
def parse_param(:MOVEMENT_STEPS_ACC_DEC_Z) do 43 end
|
||||
def parse_param(:MOVEMENT_HOME_UP_X) do 51end
|
||||
def parse_param(:MOVEMENT_HOME_UP_Y) do 52end
|
||||
def parse_param(:MOVEMENT_HOME_UP_Z) do 53end
|
||||
def parse_param(:MOVEMENT_MIN_SPD_X) do 61end
|
||||
def parse_param(:MOVEMENT_MIN_SPD_Y) do 62end
|
||||
def parse_param(:MOVEMENT_MIN_SPD_Z) do 63end
|
||||
def parse_param(:MOVEMENT_MAX_SPD_X) do 71end
|
||||
def parse_param(:MOVEMENT_MAX_SPD_Y) do 72end
|
||||
def parse_param(:MOVEMENT_MAX_SPD_Z) do 73end
|
||||
def parse_param(:ENCODER_ENABLED_X) do 101 end
|
||||
def parse_param(:ENCODER_ENABLED_Y) do 102 end
|
||||
def parse_param(:ENCODER_ENABLED_Z) do 103 end
|
||||
def parse_param(:ENCODER_MISSED_STEPS_MAX_X) do 111 end
|
||||
def parse_param(:ENCODER_MISSED_STEPS_MAX_Y) do 112 end
|
||||
def parse_param(:ENCODER_MISSED_STEPS_MAX_Z) do 113 end
|
||||
def parse_param(:ENCODER_MISSED_STEPS_DECAY_X) do 121 end
|
||||
def parse_param(:ENCODER_MISSED_STEPS_DECAY_Y) do 122 end
|
||||
def parse_param(:ENCODER_MISSED_STEPS_DECAY_Z) do 123 end
|
||||
def parse_param(:MOVEMENT_AXIS_NR_STEPS_X) do 141 end
|
||||
def parse_param(:MOVEMENT_AXIS_NR_STEPS_Y) do 142 end
|
||||
def parse_param(:MOVEMENT_AXIS_NR_STEPS_Z) do 143 end
|
||||
def parse_param(:PIN_GUARD_1_PIN_NR) do 201 end
|
||||
def parse_param(:PIN_GUARD_1_TIME_OUT) do 202 end
|
||||
def parse_param(:PIN_GUARD_1_ACTIVE_STATE) do 203 end
|
||||
def parse_param(:PIN_GUARD_2_PIN_NR) do 205 end
|
||||
def parse_param(:PIN_GUARD_2_TIME_OUT) do 206 end
|
||||
def parse_param(:PIN_GUARD_2_ACTIVE_STATE) do 207 end
|
||||
def parse_param(:PIN_GUARD_3_PIN_NR) do 211 end
|
||||
def parse_param(:PIN_GUARD_3_TIME_OUT) do 212 end
|
||||
def parse_param(:PIN_GUARD_3_ACTIVE_STATE) do 213 end
|
||||
def parse_param(:PIN_GUARD_4_PIN_NR) do 215 end
|
||||
def parse_param(:PIN_GUARD_4_TIME_OUT) do 216 end
|
||||
def parse_param(:PIN_GUARD_4_ACTIVE_STATE) do 217 end
|
||||
def parse_param(:PIN_GUARD_5_PIN_NR) do 221 end
|
||||
def parse_param(:PIN_GUARD_5_TIME_OUT) do 222 end
|
||||
def parse_param(:PIN_GUARD_5_ACTIVE_STATE) do 223 end
|
||||
def parse_param(param) when is_bitstring(param) do
|
||||
String.Casing.upcase(param)
|
||||
|> String.to_atom
|
||||
|> parse_param
|
||||
end
|
||||
def parse_param(_) do nil end
|
||||
end
|
|
@ -0,0 +1,133 @@
|
|||
alias Experimental.{GenStage}
|
||||
defmodule GcodeMessageHandler do
|
||||
use GenStage
|
||||
require Logger
|
||||
|
||||
def start_link() do
|
||||
GenStage.start_link(__MODULE__, :ok)
|
||||
end
|
||||
|
||||
def init(:ok) do
|
||||
{:consumer, :ok, subscribe_to: [SerialMessageManager]}
|
||||
end
|
||||
|
||||
def handle_events(events, _from, state) do
|
||||
for event <- events do
|
||||
do_handle(event)
|
||||
end
|
||||
{:noreply, [], state}
|
||||
end
|
||||
|
||||
# I think this is supposed to be somewhere else.
|
||||
def do_handle({:send, str}) do
|
||||
BotStatus.busy true
|
||||
GenServer.cast(UartHandler, {:send, str})
|
||||
end
|
||||
|
||||
# This is the heartbeat messge.
|
||||
def do_handle({:gcode, {:idle} }) do
|
||||
BotStatus.busy false
|
||||
end
|
||||
|
||||
# The opposite of the below command?
|
||||
def do_handle({:gcode, {:done } }) do
|
||||
BotStatus.busy false
|
||||
end
|
||||
|
||||
# I'm not entirely sure what this is.
|
||||
def do_handle({:gcode, {:received } }) do
|
||||
BotStatus.busy true
|
||||
end
|
||||
|
||||
def do_handle({:gcode, { :report_pin_value, params }}) do
|
||||
["P"<>pin, "V"<>value] = String.split(params, " ")
|
||||
Logger.debug("pin#{pin}: #{value}")
|
||||
BotStatus.set_pin(String.to_integer(pin), String.to_integer(value))
|
||||
end
|
||||
|
||||
# TODO report end stops
|
||||
def do_handle({:gcode, {:reporting_end_stops, stop_values }}) do
|
||||
# Logger.debug("[gcode_handler] {:reporting_end_stops} stub: #{stop_values}")
|
||||
stop_values
|
||||
|> parse_stop_values
|
||||
|> Enum.each(&BotStatus.set_end_stop/1)
|
||||
end
|
||||
|
||||
def do_handle({:gcode, { :report_current_position, position }}) do
|
||||
[x, y, z] = parse_coords(position)
|
||||
BotStatus.set_pos(x,y,z)
|
||||
end
|
||||
|
||||
def do_handle({:gcode, {:report_parameter_value, param }}) do
|
||||
[p, v] = String.split(param, " ")
|
||||
[_, real_p] = String.split(p, "P")
|
||||
[_, real_v] = String.split(v, "V")
|
||||
Logger.debug("Param: #{real_p}, Value: #{real_v}")
|
||||
real_p
|
||||
|> Gcode.parse_param
|
||||
|> Atom.to_string
|
||||
|> String.downcase
|
||||
|> BotStatus.set_param(real_v)
|
||||
end
|
||||
|
||||
def do_handle({:gcode, {:busy}}) do
|
||||
BotStatus.busy true
|
||||
end
|
||||
|
||||
def do_handle({:gcode, {:debug_message, "stopped"}} ) do
|
||||
BotStatus.busy false
|
||||
end
|
||||
|
||||
# Serial sending a debug message. Print it.
|
||||
def do_handle({:gcode, {:debug_message, message}} ) do
|
||||
Logger.debug("Debug message from arduino: #{message}")
|
||||
end
|
||||
|
||||
# Unhandled gcode message
|
||||
def do_handle({:gcode, {:unhandled_gcode, code}}) do
|
||||
Logger.debug("[gcode_handler] Broken code? : #{inspect code}")
|
||||
end
|
||||
|
||||
# Catch all for serial messages
|
||||
def do_handle({:gcode, message}) do
|
||||
Logger.debug("[gcode_handler] Unhandled Serial Gcode: #{inspect message}")
|
||||
end
|
||||
|
||||
@doc """
|
||||
Example:
|
||||
iex> GcodeMessageHandler.parse_coords("X34 Y756 Z23")
|
||||
[34, 756, 23]
|
||||
"""
|
||||
def parse_coords(position) when position |> is_binary do
|
||||
position
|
||||
|> String.split(" ")
|
||||
|> parse_coords
|
||||
end
|
||||
def parse_coords(["X" <> x,"Y" <> y, "Z" <> z]) do
|
||||
[x,y,z]
|
||||
|> Enum.map(&String.to_integer/1)
|
||||
end
|
||||
|
||||
|
||||
@doc """
|
||||
Example:
|
||||
iex> GcodeMessageHandler.parse_stop_values("XA0 XB0 YA0 YB0 ZA0 ZB0")
|
||||
[{"XA", "0"}, {"XB", "0"}, {"YA", "0"}, {"YB", "0"}, {"ZA", "0"}, {"ZB", "0"}]
|
||||
"""
|
||||
def parse_stop_values(stop_values) when stop_values |> is_binary do
|
||||
# same thing here as parse_coords
|
||||
stop_values
|
||||
|> String.split(" ")
|
||||
|> parse_stop_values
|
||||
end
|
||||
def parse_stop_values(["XA"<>xa, "XB"<>xb,
|
||||
"YA"<>ya, "YB"<>yb,
|
||||
"ZA"<>za, "ZB"<>zb]) do
|
||||
[
|
||||
{"XA", xa}, {"XB", xb},
|
||||
{"YA", ya}, {"YB", yb},
|
||||
{"ZA", za}, {"ZB", zb},
|
||||
]
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,39 @@
|
|||
alias Experimental.{GenStage}
|
||||
|
||||
defmodule SerialMessageManager do
|
||||
use GenStage
|
||||
def start_link() do
|
||||
GenStage.start_link(__MODULE__, :ok, name: __MODULE__)
|
||||
end
|
||||
|
||||
@doc """
|
||||
New Serial message
|
||||
"""
|
||||
def sync_notify(event, timeout \\ 5000) do
|
||||
GenStage.call(__MODULE__, {:notify, event}, timeout)
|
||||
end
|
||||
|
||||
def init(:ok) do
|
||||
{:producer, {:queue.new, 0}, dispatcher: GenStage.BroadcastDispatcher}
|
||||
end
|
||||
|
||||
def handle_call({:notify, event}, from, {queue, demand}) do
|
||||
dispatch_events(:queue.in({from, event}, queue), demand, [])
|
||||
end
|
||||
|
||||
def handle_demand(incoming_demand, {queue, demand}) do
|
||||
dispatch_events(queue, incoming_demand + demand, [])
|
||||
end
|
||||
|
||||
# This is some copy paste magic, not touching it.
|
||||
defp dispatch_events(queue, demand, events) do
|
||||
with d when d > 0 <- demand,
|
||||
{{:value, {from, event}}, queue} <- :queue.out(queue),
|
||||
_ <- GenStage.reply(from, :ok)
|
||||
do
|
||||
dispatch_events(queue, demand - 1, [event | events])
|
||||
else
|
||||
_ -> {:noreply, Enum.reverse(events), {queue, demand}}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
defmodule SerialSupervisor do
|
||||
def start_link(_args) do
|
||||
import Supervisor.Spec
|
||||
children = [
|
||||
worker(SerialMessageManager, []),
|
||||
worker(GcodeMessageHandler, [], id: 1), # Consumer
|
||||
worker(UartHandler, [[]])
|
||||
]
|
||||
Supervisor.start_link(children, strategy: :one_for_one)
|
||||
end
|
||||
|
||||
def init(_) do
|
||||
{:ok, %{}}
|
||||
end
|
||||
end
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue