1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

Compare commits

...

73 commits

Author SHA1 Message Date
winlin
e801174ab2 Fix #2606: Memory leak for RTMP client, pick from 4.0. v3.0.170 2021-10-08 07:19:25 +08:00
winlin
b4220ee7c1 Refine Dockerfile 2021-09-05 15:03:03 +08:00
winlin
21be9daba4 Update README 2021-08-15 21:49:24 +08:00
winlin
9eadebdde7 Fix version query bug. 2021-08-15 21:43:16 +08:00
winlin
2431e0b140 Acitons: Support pullrequest CI 2021-08-15 17:49:03 +08:00
winlin
b3516b88dc Acitons: Support pullrequest CI 2021-08-15 17:45:43 +08:00
winlin
1d79ef87a5 Acitons: Support pullrequest CI 2021-08-15 17:41:15 +08:00
winlin
8d44b982ee Actions: Add CI for utest and coverage 2021-08-15 17:22:07 +08:00
winlin
b02c814251 Expose live streaming ports for SRS 3.0 2021-08-14 21:09:44 +08:00
winlin
c2b133bd07 Support Github Actions 2021-08-14 20:56:00 +08:00
winlin
b8177ff2fb Release v3.0-r8, 3.0.168 2021-08-14 09:26:04 +08:00
winlin
34117027fd Update README 2021-08-13 14:53:54 +08:00
winlin
d21dadb285 Update README 2021-08-13 14:52:10 +08:00
winlin
6543aa8635 Update README 2021-08-13 14:44:22 +08:00
winlin
d81cba4d16 Update README 2021-08-13 14:43:17 +08:00
winlin
5c97da28c6 API: For #2508, query feature docker and packager. v3.0.166 2021-08-07 16:24:02 +08:00
winlin
5c6bb63bf2 API: Use libuuid to generate uuid. v3.0.165 2021-08-06 21:37:12 +08:00
winlin
ea064166b7 Add role(srs) to query versions 2021-07-06 11:20:52 +08:00
winlin
d4c1bdf9d0 Update README for v3.0-r7 2021-07-04 14:27:22 +08:00
winlin
d72d05294d For #2424, use srandom/random to generate. 3.0.164 2021-07-04 14:21:32 +08:00
winlin
e7435a6237 Fix bug for v3.0-r6 2021-06-27 10:00:38 +08:00
winlin
2be8589a8a For #2424, query the latest available version. 3.0.163 2021-06-26 09:51:15 +08:00
winlin
cf34ebc32f Fix link in README.md 2021-05-31 13:13:02 +08:00
winlin
e7e05c434e Refine README.md 2021-05-24 10:15:54 +08:00
winlin
9f9bc6a17f For SRS3.0, correct the protocols it supports 2021-05-24 08:58:28 +08:00
Xiaoniu
a1f5382240
fix bug: memory leak in SrsStatisticClient (#2352) 2021-05-13 21:07:37 +08:00
winlin
e076300cd8 For #2311, Refine the update to update_auth 2021-05-12 21:26:04 +08:00
winlin
71dda68f62 Fix #2311, Copy the request for stat client. 3.0.162 2021-05-12 21:17:57 +08:00
winlin
b7af726ffc Doc: Link source flv 2021-04-30 16:42:47 +08:00
winlin
61a5e22af8 Update gitee repository url 2021-04-28 16:58:36 +08:00
winlin
a3fddbe8b0 Update README 2021-04-28 16:45:36 +08:00
winlin
c95c53cfe3 Update script 2021-04-28 15:30:06 +08:00
winlin
9d9d81ef17 Upgrade players. 3.0.161 2021-04-28 15:18:10 +08:00
winlin
cf6fa98e36 Update README 2021-04-28 08:19:22 +08:00
winlin
42e910bb32 Update README 2021-04-28 08:13:48 +08:00
winlin
0c5b5106d0 Update README 2021-04-28 08:12:21 +08:00
winlin
8547118622 Update README 2021-04-28 08:10:50 +08:00
winlin
f847b0d1c0 Add crossdomain.xml for install script 2021-04-26 13:47:15 +08:00
winlin
7367780605 For #2304: Deprecate pull RTSP 2021-04-25 08:53:14 +08:00
winlin
c7d45c20b5 Update README 2021-04-24 22:35:57 +08:00
winlin
e6897a9959 Update README 2021-04-24 22:27:42 +08:00
winlin
e80245c66b Update README 2021-04-24 22:22:36 +08:00
winlin
077c45cea0 Update README 2021-04-24 22:21:46 +08:00
winlin
bee9d181e7 Update README 2021-04-24 22:20:44 +08:00
winlin
cea3f43cba Update README 2021-04-24 22:18:03 +08:00
winlin
75059531c2 Release v3.0-r4 2021-04-24 18:30:00 +08:00
winlin
4cdaa15a76 Remove make install-api 2021-04-24 18:28:46 +08:00
winlin
08500fe162 Package players and console to zip and docker. 3.0.160 2021-04-24 18:28:31 +08:00
winlin
44965da9af Add srs-console to research/console. 3.0.159 2021-04-24 18:07:49 +08:00
winlin
90b2e08c4b Package srs-console to zip and docker. 3.0.159 2021-04-24 14:44:06 +08:00
winlin
f8e3be644d Update description 2021-04-16 11:11:40 +08:00
winlin
0702c27d88 Update description 2021-04-16 11:05:55 +08:00
stone
bf58f915ef
Update srs_app_config.cpp (#2275)
bugfix: misleading error log information for transcode engine config param.
2021-04-09 07:16:00 +08:00
winlin
47a7a5f825 Update README for docker 2021-03-05 09:46:17 +08:00
winlin
5eaf3b7cdb Update README for docker 2021-03-05 09:44:09 +08:00
winlin
7a134f5352 Refine usage to docker by default. 3.0.158 2021-03-05 09:04:59 +08:00
winlin
f5ff646c0c Add docker for FFmpeg publish 2021-03-05 09:04:10 +08:00
winlin
33a94de13d Add docker for FFmpeg publish 2021-03-05 09:02:04 +08:00
winlin
ba5b2f6a60 Add docker for FFmpeg publish 2021-03-05 08:58:29 +08:00
winlin
a44833a3b9 Add docker for FFmpeg publish 2021-03-05 08:57:15 +08:00
winlin
aa0520e07e Update README for docker 2021-03-05 00:11:29 +08:00
winlin
10746d25c1 Refine README for docker 2021-03-05 00:05:31 +08:00
winlin
407bad5c9d Refine README for docker 2021-03-05 00:03:46 +08:00
winlin
4465cf3df1 Refine README for docker 2021-03-05 00:03:21 +08:00
winlin
da7251f628 Refine README for docker 2021-03-04 23:56:37 +08:00
winlin
0a6ad8b821 Refine README for docker 2021-03-04 23:55:09 +08:00
winlin
48a9498a20 Refine README for docker 2021-03-04 23:51:59 +08:00
winlin
a3c9f57f8a Refine README for docker 2021-03-04 23:48:41 +08:00
winlin
3c602269bb Update usage, docker first. 2021-03-04 23:29:09 +08:00
winlin
ccbc8b25c6 Update usage, docker first. 2021-03-04 23:28:16 +08:00
winlin
48ac0a01c2 Update usage, docker first. 2021-03-04 23:27:18 +08:00
winlin
a4f9447380 Update usage, docker first. 2021-03-04 23:24:51 +08:00
winlin
de5b86aab9 Docker: Change log from file to console, add conf/docker.conf 2021-03-04 21:57:00 +08:00
111 changed files with 48868 additions and 1639 deletions

75
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,75 @@
name: "Release"
on:
push:
tags:
- v3*
jobs:
k8s:
name: actions-release-k8s
runs-on: ubuntu-20.04
steps:
- name: Checkout repository
uses: actions/checkout@v2
# The github.ref is, for example, refs/tags/v3.0.145 or refs/tags/v3.0-r8
# Generate variables like:
# SRS_TAG=v3.0.145
# SRS_TAG=v3.0-r8
# SRS_MAJOR=3
# @see https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable
- name: Generate varaiables
shell: bash
run: |
SRS_TAG=$(echo ${{ github.ref }}| awk -F '/' '{print $3}')
echo "SRS_TAG=$SRS_TAG" >> $GITHUB_ENV
SRS_MAJOR=$(echo $SRS_TAG| cut -c 2)
echo "SRS_MAJOR=$SRS_MAJOR" >> $GITHUB_ENV
- name: Build SRS
shell: bash
run: |
echo "Release ossrs/srs:$SRS_TAG"
docker build --tag ossrs/srs:$SRS_TAG -f trunk/Dockerfile .
- name: Login docker hub
uses: docker/login-action@v1
with:
username: "${{ secrets.DOCKER_USERNAME }}"
password: "${{ secrets.DOCKER_PASSWORD }}"
- name: Push to docker hub
shell: bash
run: |
docker push ossrs/srs:$SRS_TAG
docker tag ossrs/srs:$SRS_TAG ossrs/srs:$SRS_MAJOR
docker push ossrs/srs:$SRS_MAJOR
- name: Login Aliyun docker hub
uses: aliyun/acr-login@v1
with:
login-server: https://registry.cn-hangzhou.aliyuncs.com
username: "${{ secrets.ACR_USERNAME }}"
password: "${{ secrets.ACR_PASSWORD }}"
- name: Push to Aliyun docker hub
shell: bash
run: |
docker tag ossrs/srs:$SRS_TAG registry.cn-hangzhou.aliyuncs.com/ossrs/srs:$SRS_TAG
docker push registry.cn-hangzhou.aliyuncs.com/ossrs/srs:$SRS_TAG
docker tag ossrs/srs:$SRS_TAG registry.cn-hangzhou.aliyuncs.com/ossrs/srs:$SRS_MAJOR
docker push registry.cn-hangzhou.aliyuncs.com/ossrs/srs:$SRS_MAJOR
- name: Setup KUBCONFIG for Aliyun ACK
shell: bash
run: |-
KUBECONFIG=$RUNNER_TEMP/kubeconfig_$(date +%s)
echo "${{ secrets.KUBCONFIG }}" > $KUBECONFIG
echo "KUBECONFIG=$KUBECONFIG" >> $GITHUB_ENV
- name: Release SRS 3.0 to Aliyun ACK
shell: bash
if: ${{ startsWith(github.ref, 'refs/tags/v3') }}
run: |-
kubectl set image deploy/srs3-deploy srs=registry.cn-hangzhou.aliyuncs.com/ossrs/srs:$SRS_TAG
kubectl describe deploy/srs3-deploy

47
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,47 @@
name: "Test"
on: [push, pull_request]
jobs:
utest:
name: actions-test-utest
runs-on: ubuntu-20.04
steps:
- name: Checkout repository
uses: actions/checkout@v2
################################################################
# Tests
- name: Build test image
run: docker build --tag srs:test -f trunk/Dockerfile.test .
# For utest
- name: Run SRS utest
run: docker run --rm srs:test bash -c 'make && ./objs/srs_utest'
coverage:
name: actions-test-coverage
runs-on: ubuntu-20.04
steps:
- name: Checkout repository
uses: actions/checkout@v2
################################################################
# Tests
- name: Build coverage image
run: docker build --tag srs:cov -f trunk/Dockerfile.cov .
# For coverage
- name: Run SRS covergae
if: ${{ startsWith(github.ref, 'refs/heads/') || startsWith(github.ref, 'refs/pull/') }}
run: |
# The hash of commit.
SRS_SHA=${{ github.sha }}
# Note that the root of SRS, must contains .git, for report fixing.
SRS_PROJECT=/srs
# The github.ref is, for example, refs/heads/3.0release
SRS_BRANCH=$(echo ${{ github.ref }}| awk -F 'refs/heads/' '{print $2}'| awk -F '/' '{print $1}')
# The github.ref is, for example, refs/pull/2536/merge
SRS_PR=$(echo ${{ github.ref }}| awk -F 'refs/pull/' '{print $2}'| awk -F '/' '{print $1}')
echo "For ref=${{ github.ref }}, sha=${{ github.sha }}, SRS_BRANCH=$SRS_BRANCH, SRS_PR=$SRS_PR, SRS_SHA=$SRS_SHA, SRS_PROJECT=$SRS_PROJECT"
docker run --rm --env SRS_BRANCH=$SRS_BRANCH --env SRS_PR=$SRS_PR --env SRS_SHA=$SRS_SHA --env SRS_PROJECT=$SRS_PROJECT \
srs:cov bash -c 'make && ./objs/srs_utest && bash auto/coverage.sh'

1
.gitignore vendored
View file

@ -22,7 +22,6 @@
/trunk/research/librtmp/objs
/trunk/3rdparty/ccache/ccache-3.1.9
/trunk/3rdparty/gprof/graphviz-2.36.0
/trunk/research/api-server/static-dir/crossdomain.xml
/trunk/research/api-server/static-dir/forward
/trunk/research/api-server/static-dir/live
/trunk/research/api-server/static-dir/players

138
README.md
View file

@ -1,82 +1,66 @@
# SRS(Simple Realtime Server)
![](http://ossrs.net/gif/v1/sls.gif?site=github.com&path=/srs/srs3)
[![](https://circleci.com/gh/ossrs/srs/tree/3.0release.svg?style=svg&circle-token=1ef1d5b5b0cde6c8c282ed856a18199f9e8f85a9)](https://circleci.com/gh/ossrs/srs/tree/3.0release)
[![](https://github.com/ossrs/srs/actions/workflows/test.yml/badge.svg?branch=3.0release)](https://github.com/ossrs/srs/actions?query=workflow%3ATest+branch%3A3.0release)
[![](https://github.com/ossrs/srs/actions/workflows/release.yml/badge.svg)](https://github.com/ossrs/srs/actions?query=workflow%3ARelease)
[![](https://codecov.io/gh/ossrs/srs/branch/3.0release/graph/badge.svg)](https://codecov.io/gh/ossrs/srs/branch/3.0release)
[![](https://cloud.githubusercontent.com/assets/2777660/22814959/c51cbe72-ef92-11e6-81cc-32b657b285d5.png)](https://github.com/ossrs/srs/wiki/v1_CN_Contact#wechat)
SRS/3.0[OuXuli][release3]是一个流媒体集群支持RTMP/HLS/FLV高效、稳定、易用简单而快乐。<br/>
SRS is a RTMP/HLS/FLV streaming cluster, high efficiency, stable and simple.
SRS/3.0[OuXuli][release3]是一个简单高效的实时视频服务器支持RTMP/HLS/HTTP-FLV。
SRS is a simple, high efficiency and realtime video server, supports RTMP/HLS/HTTP-FLV.
> Remark: Although SRS is licenced under [MIT][LICENSE], but there are some depended libraries which are distributed using their own licenses, please read [License Mixing][LicenseMixing].
<a name="product"></a>
## Usage
**>>> Step 1:** Get SRS.
Build SRS from source, please read **Wiki: Gettting Started( [EN](https://github.com/ossrs/srs/wiki/v3_EN_Home#getting-started) / [CN](https://github.com/ossrs/srs/wiki/v3_CN_Home#getting-started) )**:
```
git clone https://gitee.com/winlinvip/srs.oschina.git srs &&
cd srs/trunk && git remote set-url origin https://github.com/ossrs/srs.git &&
git checkout 3.0release && git pull
git clone -b 3.0release https://gitee.com/ossrs/srs.git &&
cd srs/trunk && ./configure && make && ./objs/srs -c conf/srs.conf
```
> Note: We use [mirrors(gitee)](#mirrors) here, but it's also ok to directly clone by `git clone https://github.com/ossrs/srs.git && cd srs/trunk`
Open [http://localhost:8080/](http://localhost:8080/) to check it, then publish
by [FFmpeg](https://ffmpeg.org/download.html) or [OBS](https://obsproject.com/download) as:
**>>> Step 2:** Build SRS.
```
./configure && make
```bash
ffmpeg -re -i ./doc/source.flv -c copy -f flv -y rtmp://localhost/live/livestream
```
> Remark: Recommend to use Centos7 64bits, please read wiki([CN][v3_CN_Build],[EN][v3_EN_Build]).
Play the following streams by [players](https://ossrs.net):
> Note: You can also build SRS in docker, please read [docker][docker-dev].
**>>> Step 3:** Run SRS
```
./objs/srs -c conf/srs.conf
```
**>>> Whatever**, you can also directly run SRS in [docker][docker-srs3]:
```
docker run -p 1935:1935 -p 1985:1985 -p 8080:8080 \
registry.cn-hangzhou.aliyuncs.com/ossrs/srs:3
```
> Note: Again, we use [ACR](https://cr.console.aliyun.com/) here, you can directly run in docker hub by `docker run -p 1935:1935 -p 1985:1985 -p 8080:8080 ossrs/srs:3`
**>>> From here,** strongly recommend to read bellow wikis:
* Usage: How to delivery RTMP?([CN][v1_CN_SampleRTMP], [EN][v1_EN_SampleRTMP])
* Usage: How to delivery RTMP Edge Cluster?([CN][v3_CN_SampleRTMPCluster], [EN][v3_EN_SampleRTMPCluster])
* Usage: How to create a RTMP Origin Cluster?([CN][v3_CN_SampleOriginCluster], [EN][v3_EN_SampleOriginCluster])
* Usage: How to delivery HTTP FLV Live Streaming?([CN][v3_CN_SampleHttpFlv], [EN][v3_EN_SampleHttpFlv])
* Usage: How to delivery HTTP FLV Live Streaming Cluster?([CN][v3_CN_SampleHttpFlvCluster], [EN][v3_EN_SampleHttpFlvCluster])
* Usage: How to delivery HLS?([CN][v3_CN_SampleHLS], [EN][v3_EN_SampleHLS])
* Usage: How to transode RTMP stream by FFMPEG?([CN][v2_CN_SampleFFMPEG], [EN][v2_EN_SampleFFMPEG])
* Usage: How to forward stream to other servers?([CN][v3_CN_SampleForward], [EN][v3_EN_SampleForward])
* Usage: How to deploy in low lantency mode?([CN][v3_CN_SampleRealtime], [EN][v3_EN_SampleRealtime])
* Usage: How to ingest file/stream/device to RTMP?([CN][v1_CN_SampleIngest], [EN][v1_EN_SampleIngest])
* Usage: How to improve edge performance for multiple CPUs? ([CN][v3_CN_REUSEPORT], [EN][v3_EN_REUSEPORT])
* Usage: How to file a bug or contact us? ([CN][v1_CN_Contact], [EN][v1_EN_Contact])
* VLC(RTMP): rtmp://localhost/live/livestream
* H5(HTTP-FLV): [http://localhost:8080/live/livestream.flv](http://localhost:8080/players/srs_player.html?autostart=true&stream=livestream.flv&port=8080&schema=http)
* H5(HLS): [http://localhost:8080/live/livestream.m3u8](http://localhost:8080/players/srs_player.html?autostart=true&stream=livestream.m3u8&port=8080&schema=http)
<a name="srs-30-wiki"></a>
## Wiki
<a name="wiki"></a>
Please select according to languages:
From here, please read wikis:
* [SRS 3.0 English Wiki][v3_EN_Home]
* [SRS 3.0 Chinese Wiki][v3_CN_Home]
* [Getting Started](https://github.com/ossrs/srs/wiki/v3_EN_Home#getting-started), please read Wiki first.
* [中文文档:起步](https://github.com/ossrs/srs/wiki/v3_CN_Home#getting-started)不读Wiki一定扑街不读文档请不要提Issue不读文档请不要提问题任何文档中明确说过的疑问都不会解答。
For previous versions, please read:
Fast index for Wikis:
* [SRS 2.0 English Wiki][v2_EN_Home]
* [SRS 2.0 Chinese Wiki][v2_CN_Home]
* [SRS 1.0 English Wiki][v1_EN_Home]
* [SRS 1.0 Chinese Wiki][v1_CN_Home]
* Overview? ([CN][v3_CN_Home], [EN][v3_EN_Home])
* How to deliver RTMP streaming?([CN][v1_CN_SampleRTMP], [EN][v1_EN_SampleRTMP])
* How to build RTMP Edge-Cluster?([CN][v3_CN_SampleRTMPCluster], [EN][v3_EN_SampleRTMPCluster])
* How to build RTMP Origin-Cluster?([CN][v3_CN_SampleOriginCluster], [EN][v3_EN_SampleOriginCluster])
* How to deliver HTTP-FLV streaming?([CN][v3_CN_SampleHttpFlv], [EN][v3_EN_SampleHttpFlv])
* How to deliver HLS streaming?([CN][v3_CN_SampleHLS], [EN][v3_EN_SampleHLS])
* How to deliver low-latency streaming?([CN][v3_CN_SampleRealtime], [EN][v3_EN_SampleRealtime])
Other important wiki:
* Usage: How to transode RTMP stream by FFMPEG?([CN][v2_CN_SampleFFMPEG], [EN][v2_EN_SampleFFMPEG])
* Usage: How to delivery HTTP FLV Live Streaming Cluster?([CN][v3_CN_SampleHttpFlvCluster], [EN][v3_EN_SampleHttpFlvCluster])
* Usage: How to ingest file/stream/device to RTMP?([CN][v1_CN_SampleIngest], [EN][v1_EN_SampleIngest])
* Usage: How to forward stream to other servers?([CN][v3_CN_SampleForward], [EN][v3_EN_SampleForward])
* Usage: How to improve edge performance for multiple CPUs? ([CN][v3_CN_REUSEPORT], [EN][v3_EN_REUSEPORT])
* Usage: How to file a bug or contact us? ([CN][v1_CN_Contact], [EN][v1_EN_Contact])
## Features
@ -146,6 +130,19 @@ For previous versions, please read:
## V3 changes
* v3.0, 2021-10-08, Fix [#2606](https://github.com/ossrs/srs/issues/2606): Memory leak for RTMP client, pick from 4.0. v3.0.170
* <strong>v3.0, 2021-08-14, [3.0 release8(3.0.168)](https://github.com/ossrs/srs/releases/tag/v3.0-r8) released. 124469 lines.</strong>
* <strong>v3.0, 2021-07-04, [3.0 release7(3.0.164)](https://github.com/ossrs/srs/releases/tag/v3.0-r7) released. 123463 lines.</strong>
* v3.0, 2021-07-04, For [#2424](https://github.com/ossrs/srs/issues/2424), use srandom/random to generate. 3.0.164
* <strong>v3.0, 2021-06-26, [3.0 release6(3.0.163)](https://github.com/ossrs/srs/releases/tag/v3.0-r6) released. 123011 lines.</strong>
* v3.0, 2021-06-26, For [#2424](https://github.com/ossrs/srs/issues/2424), query the latest available version. 3.0.163
* v3.0, 2021-05-12, Fix [#2311][bug #2311], Copy the request for stat client. 3.0.162
* <strong>v3.0, 2021-04-28, [3.0 release5(3.0.161)][r3.0r5] released. 122750 lines.</strong>
* v3.0, 2021-04-28, Upgrade players. 3.0.161
* <strong>v3.0, 2021-04-24, [3.0 release4(3.0.160)][r3.0r4] released. 122750 lines.</strong>
* v3.0, 2021-04-24, Package players and console to zip and docker. 3.0.160
* v3.0, 2021-04-24, Add srs-console to research/console. 3.0.159
* v3.0, 2021-03-05, Refine usage to docker by default. 3.0.158
* v3.0, 2021-01-07, Change id from int to string for the statistics. 3.0.157
* <strong>v3.0, 2021-01-02, [3.0 release3(3.0.156)][r3.0r3] released. 122736 lines.</strong>
* v3.0, 2020-12-26, For RTMP edge/forward, pass vhost in tcUrl, not in stream. 3.0.156
@ -342,6 +339,11 @@ For previous versions, please read:
## V2 changes
* <strong>v2.0, 2021-08-14, [2.0 release11(2.0.276)](https://github.com/ossrs/srs/releases/tag/v2.0-r11) released. 89013 lines.</strong>
* <strong>v2.0, 2021-07-04, [2.0 release10(2.0.274)](https://github.com/ossrs/srs/releases/tag/v2.0-r10) released. 87575 lines.</strong>
* v2.0, 2021-07-04, For [#2424](https://github.com/ossrs/srs/issues/2424), use srandom/random to generate. 2.0.274
* <strong>v2.0, 2021-06-26, [2.0 release9(2.0.273)](https://github.com/ossrs/srs/releases/tag/v2.0-r9) released. 87552 lines.</strong>
* v2.0, 2021-06-25, For [#2424](https://github.com/ossrs/srs/issues/2424), query the latest available version. 2.0.273
* <strong>v2.0, 2020-01-25, [2.0 release8(2.0.272)][r2.0r8] released. 87292 lines.</strong>
* v2.0, 2020-01-08, Merge [#1554][bug #1554], support logrotate copytruncate. 2.0.272
* v2.0, 2020-01-05, Merge [#1551][bug #1551], fix memory leak in RTSP stack. 2.0.270
@ -780,6 +782,11 @@ For previous versions, please read:
## Releases
* 2021-08-14, [Release v3.0-r8](https://github.com/ossrs/srs/releases/tag/v3.0-r8), 3.0 release8, 3.0.168, 124469 lines.
* 2021-07-04, [Release v3.0-r7](https://github.com/ossrs/srs/releases/tag/v3.0-r7), 3.0 release7, 3.0.167, 123463 lines.
* 2021-06-26, [Release v3.0-r6](https://github.com/ossrs/srs/releases/tag/v3.0-r6), 3.0 release6, 3.0.163, 123011 lines.
* 2021-04-28, [Release v3.0-r5][r3.0r5], 3.0 release5, 3.0.161, 122750 lines.
* 2021-04-24, [Release v3.0-r4][r3.0r4], 3.0 release4, 3.0.160, 122750 lines.
* 2021-01-02, [Release v3.0-r3][r3.0r3], 3.0 release3, 3.0.156, 122736 lines.
* 2020-10-31, [Release v3.0-r2][r3.0r2], 3.0 release2, 3.0.153, 122663 lines.
* 2020-10-10, [Release v3.0-r1][r3.0r1], 3.0 release1, 3.0.144, 122674 lines.
@ -1150,10 +1157,10 @@ A big THANK YOU goes to:
## Mirrors
OSChina: [https://gitee.com/winlinvip/srs.oschina][oschina], the GIT usage([CN][v1_CN_Git], [EN][v1_EN_Git])
Gitee: [https://gitee.com/ossrs/srs][gitee], the GIT usage([CN][v1_CN_Git], [EN][v1_EN_Git])
```
git clone https://gitee.com/winlinvip/srs.oschina.git srs &&
git clone https://gitee.com/ossrs/srs.git &&
cd srs && git remote set-url origin https://github.com/ossrs/srs.git && git pull
```
@ -1174,12 +1181,12 @@ git clone https://github.com/ossrs/srs.git
| Branch | Cost | Size | CMD |
| --- | --- | --- | --- |
| 3.0release | 2m19.931s | 262MB | git clone -b 3.0release https://gitee.com/winlinvip/srs.oschina.git |
| 3.0release | 0m56.515s | 95MB | git clone -b 3.0release --depth=1 https://gitee.com/winlinvip/srs.oschina.git |
| develop | 2m22.430s | 234MB | git clone -b develop https://gitee.com/winlinvip/srs.oschina.git |
| develop | 0m46.421s | 42MB | git clone -b develop --depth=1 https://gitee.com/winlinvip/srs.oschina.git |
| min | 2m22.865s | 217MB | git clone -b min https://gitee.com/winlinvip/srs.oschina.git |
| min | 0m36.472s | 11MB | git clone -b min --depth=1 https://gitee.com/winlinvip/srs.oschina.git |
| 3.0release | 2m19.931s | 262MB | git clone -b 3.0release https://gitee.com/ossrs/srs.git |
| 3.0release | 0m56.515s | 95MB | git clone -b 3.0release --depth=1 https://gitee.com/ossrs/srs.git |
| develop | 2m22.430s | 234MB | git clone -b develop https://gitee.com/ossrs/srs.git |
| develop | 0m46.421s | 42MB | git clone -b develop --depth=1 https://gitee.com/ossrs/srs.git |
| min | 2m22.865s | 217MB | git clone -b min https://gitee.com/ossrs/srs.git |
| min | 0m36.472s | 11MB | git clone -b min --depth=1 https://gitee.com/ossrs/srs.git |
## System Requirements
@ -1230,7 +1237,7 @@ Winlin
[libx264]: http://www.videolan.org/
[srs]: https://github.com/ossrs/srs
[csdn]: https://code.csdn.net/winlinvip/srs-csdn
[oschina]: https://gitee.com/winlinvip/srs.oschina
[gitee]: https://gitee.com/ossrs/srs
[srs-dolphin]: https://github.com/ossrs/srs-dolphin
[oryx]: https://github.com/ossrs/go-oryx
[srs-bench]: https://github.com/ossrs/srs-bench
@ -1240,7 +1247,7 @@ Winlin
[console]: http://ossrs.net:1985/console
[player]: http://ossrs.net/players/srs_player.html
[modules]: https://github.com/ossrs/srs/blob/develop/trunk/modules/readme.txt
[docker-srs3]: https://github.com/ossrs/srs-docker#srs3
[docker-srs3]: https://github.com/ossrs/srs-docker/tree/v3#usage
[docker-dev]: https://github.com/ossrs/srs-docker/tree/dev#usage
[v1_CN_Git]: https://github.com/ossrs/srs/wiki/v1_CN_Git
@ -1716,10 +1723,13 @@ Winlin
[bug #1987]: https://github.com/ossrs/srs/issues/1987
[bug #1548]: https://github.com/ossrs/srs/issues/1548
[bug #1694]: https://github.com/ossrs/srs/issues/1694
[bug #2311]: https://github.com/ossrs/srs/issues/2311
[bug #yyyyyyyyyyyyy]: https://github.com/ossrs/srs/issues/yyyyyyyyyyyyy
[exo #828]: https://github.com/google/ExoPlayer/pull/828
[r3.0r5]: https://github.com/ossrs/srs/releases/tag/v3.0-r5
[r3.0r4]: https://github.com/ossrs/srs/releases/tag/v3.0-r4
[r3.0r3]: https://github.com/ossrs/srs/releases/tag/v3.0-r3
[r3.0r2]: https://github.com/ossrs/srs/releases/tag/v3.0-r2
[r3.0r1]: https://github.com/ossrs/srs/releases/tag/v3.0-r1
@ -1787,7 +1797,7 @@ Winlin
[more0]: http://winlinvip.github.io/srs.release/releases/
[more1]: http://ossrs.net/srs.release/releases/
[LICENSE]: https://github.com/ossrs/srs/blob/develop/LICENSE
[LICENSE]: https://github.com/ossrs/srs/blob/3.0release/LICENSE
[LicenseMixing]: https://github.com/ossrs/srs/wiki/LicenseMixing
[srs_CN]: https://github.com/ossrs/srs/wiki/v3_CN_Home

1
trunk/.gitignore vendored
View file

@ -13,6 +13,7 @@
/ide/srs_xcode/srs_xcode.xcodeproj/xcuserdata/
/research/aac/
/research/api-server/static-dir/mse
/research/api-server/static-dir/crossdomain.xml
/research/bat/
/research/big/
/research/bitch/

36
trunk/Dockerfile Normal file
View file

@ -0,0 +1,36 @@
FROM ossrs/srs:dev AS build
# Install depends tools.
RUN yum install -y gcc make gcc-c++ patch unzip perl git
# Build and install SRS.
COPY . /srs
WORKDIR /srs/trunk
RUN ./configure --jobs=2 && make -j2 && make install
# All config files for SRS.
RUN cp -R conf /usr/local/srs/conf
# The default index.html and srs-console.
RUN cp research/api-server/static-dir/index.html /usr/local/srs/objs/nginx/html/
RUN cp research/api-server/static-dir/favicon.ico /usr/local/srs/objs/nginx/html/
RUN cp research/players/crossdomain.xml /usr/local/srs/objs/nginx/html/
RUN cp -R research/console /usr/local/srs/objs/nginx/html/
RUN cp -R research/players /usr/local/srs/objs/nginx/html/
#RUN cp -R 3rdparty/signaling/www/demos /usr/local/srs/objs/nginx/html/
############################################################
# dist
############################################################
FROM centos:7 AS dist
# Expose ports for live streaming
EXPOSE 1935 1985 8080
# FFMPEG 4.1
COPY --from=build /usr/local/bin/ffmpeg /usr/local/srs/objs/ffmpeg/bin/ffmpeg
# SRS binary, config files and srs-console.
COPY --from=build /usr/local/srs /usr/local/srs
# Default workdir and command.
WORKDIR /usr/local/srs
CMD ["./objs/srs", "-c", "conf/srs.conf"]

9
trunk/Dockerfile.cov Normal file
View file

@ -0,0 +1,9 @@
FROM ossrs/srs:dev
# Install depends tools.
RUN yum install -y gcc make gcc-c++ patch unzip perl git
# Build and install SRS.
COPY . /srs
WORKDIR /srs/trunk
RUN ./configure --with-utest --gcov --jobs=2 && make -j2

9
trunk/Dockerfile.test Normal file
View file

@ -0,0 +1,9 @@
FROM ossrs/srs:dev
# Install depends tools.
RUN yum install -y gcc make gcc-c++ patch unzip perl git
# Build and install SRS.
COPY . /srs
WORKDIR /srs/trunk
RUN ./configure --with-utest --jobs=2 && make -j2

View file

@ -11,6 +11,7 @@ echo "#ifndef SRS_AUTO_HEADER_HPP" >> $SRS_AUTO_HEADERS_H
echo "#define SRS_AUTO_HEADER_HPP" >> $SRS_AUTO_HEADERS_H
echo "" >> $SRS_AUTO_HEADERS_H
echo "#define SRS_AUTO_PACKAGER \"${SRS_AUTO_PACKAGER}\"" >> $SRS_AUTO_HEADERS_H
echo "#define SRS_AUTO_BUILD_TS \"`date +%s`\"" >> $SRS_AUTO_HEADERS_H
echo "#define SRS_AUTO_BUILD_DATE \"`date \"+%Y-%m-%d %H:%M:%S\"`\"" >> $SRS_AUTO_HEADERS_H
echo "#define SRS_AUTO_UNAME \"`uname -a`\"" >> $SRS_AUTO_HEADERS_H

View file

@ -1,36 +1,35 @@
#!/bin/bash
# In .circleci/config.yml, generate *.gcno with
# ./configure --gcov --without-research --without-librtmp && make
# ./configure --gcov --with-utest --without-research --without-librtmp && make
# and generate *.gcda by
# ./objs/srs_utest
# Workdir is objs/cover.
workdir=`pwd`/objs/cover
# Tool git is required to map the right path.
git --version >/dev/null 2>&1
ret=$?; if [[ $ret -ne 0 ]]; then echo "Tool git is required, ret=$ret"; exit $ret; fi
# Create trunk under workdir.
mkdir -p $workdir && cd $workdir
ret=$?; if [[ $ret -ne 0 ]]; then echo "Enter workdir failed, ret=$ret"; exit $ret; fi
# Collect all *.gcno and *.gcda to objs/cover.
cd $workdir && (rm -rf src && cp -R ../../src . && cp -R ../src/* src/)
ret=$?; if [[ $ret -ne 0 ]]; then echo "Collect *.gcno and *.gcda failed, ret=$ret"; exit $ret; fi
# Generate *.gcov for coverage.
cd $workdir &&
for file in `find src -name "*.cpp"|grep -v utest`; do
gcov $file -o `dirname $file`
ret=$?; if [[ $ret -ne 0 ]]; then echo "Collect $file failed, ret=$ret"; exit $ret; fi
done
# Cook the gcov files.
cd $workdir &&
find . -name "*.gcov"|grep -v srs|xargs rm -f
ret=$?; if [[ $ret -ne 0 ]]; then echo "Cook gcov files failed, ret=$ret"; exit $ret; fi
CODECOV_ARGS=""
if [[ $SRS_PROJECT != '' ]]; then
# -R root dir Used when not in git/hg project to identify project root directory
# -p dir Project root directory. Also used when preparing gcov
CODECOV_ARGS="$CODECOV_ARGS -R $SRS_PROJECT -p $SRS_PROJECT"
fi
if [[ $SRS_BRANCH != '' ]]; then
# -B branch Specify the branch name
CODECOV_ARGS="$CODECOV_ARGS -B $SRS_BRANCH"
fi
if [[ $SRS_SHA != '' ]]; then
# -C sha Specify the commit sha
CODECOV_ARGS="$CODECOV_ARGS -C $SRS_SHA"
fi
if [[ $SRS_PR != '' ]]; then
# -P pr Specify the pull request number
CODECOV_ARGS="$CODECOV_ARGS -P $SRS_PR"
fi
# Upload report with *.gcov
# Remark: The file codecov.yml is not neccessary. It literally depends on git.
@ -41,5 +40,6 @@ ret=$?; if [[ $ret -ne 0 ]]; then echo "Cook gcov files failed, ret=$ret"; exit
# https://circleci.com/gh/ossrs/srs/tree/3.0release
cd $workdir &&
export CODECOV_TOKEN="493bba46-c468-4e73-8b45-8cdd8ff62d96" &&
bash <(curl -s https://codecov.io/bash) &&
bash <(curl -s https://codecov.io/bash) $CODECOV_ARGS &&
echo "Done" && exit 0

View file

@ -388,14 +388,20 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
# copy players to nginx html dir.
rm -rf ${SRS_OBJS}/nginx/html/players &&
ln -sf `pwd`/research/players ${SRS_OBJS}/nginx/html/players &&
rm -f ${SRS_OBJS}/nginx/crossdomain.xml &&
ln -sf `pwd`/research/players/crossdomain.xml ${SRS_OBJS}/nginx/html/crossdomain.xml
ln -sf `pwd`/research/players ${SRS_OBJS}/nginx/html/players
# For srs-console.
rm -rf ${SRS_OBJS}/nginx/html/console &&
ln -sf `pwd`/research/console ${SRS_OBJS}/nginx/html/console
# for favicon.ico
rm -rf ${SRS_OBJS}/nginx/html/favicon.ico &&
ln -sf `pwd`/research/api-server/static-dir/favicon.ico ${SRS_OBJS}/nginx/html/favicon.ico
# For home page index.html
rm -rf ${SRS_OBJS}/nginx/html/index.html &&
ln -sf `pwd`/research/api-server/static-dir/index.html ${SRS_OBJS}/nginx/html/index.html
# nginx.html to detect whether nginx is alive
echo "Nginx is ok." > ${SRS_OBJS}/nginx/html/nginx.html
fi

29
trunk/conf/docker.conf Normal file
View file

@ -0,0 +1,29 @@
# main config for srs.
# @see full.conf for detail config.
listen 1935;
max_connections 1000;
srs_log_tank console;
daemon off;
http_api {
enabled on;
listen 1985;
}
http_server {
enabled on;
listen 8080;
dir ./objs/nginx/html;
}
stats {
network 0;
disk sda sdb xvda xvdb;
}
vhost __defaultVhost__ {
hls {
enabled on;
}
http_remux {
enabled on;
mount [vhost]/[app]/[stream].flv;
}
}

View file

@ -105,6 +105,11 @@ inotify_auto_reload off;
# default: on
auto_reload_for_docker on;
# Query the latest available version of SRS, write a log to notice user to upgrade.
# @see https://github.com/ossrs/srs/issues/2424
# Default: on
query_latest_version on;
#############################################################################################
# heartbeat/stats sections
#############################################################################################

34
trunk/configure vendored
View file

@ -228,7 +228,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
"srs_app_heartbeat" "srs_app_empty" "srs_app_http_client" "srs_app_http_static"
"srs_app_recv_thread" "srs_app_security" "srs_app_statistic" "srs_app_hds"
"srs_app_mpegts_udp" "srs_app_rtsp" "srs_app_listener" "srs_app_async_call"
"srs_app_caster_flv" "srs_app_process" "srs_app_ng_exec"
"srs_app_caster_flv" "srs_app_latest_version" "srs_app_uuid" "srs_app_process" "srs_app_ng_exec"
"srs_app_hourglass" "srs_app_dash" "srs_app_fragment" "srs_app_dvr"
"srs_app_coworkers")
DEFINES=""
@ -346,7 +346,7 @@ mv ${SRS_WORKDIR}/${SRS_MAKEFILE} ${SRS_WORKDIR}/${SRS_MAKEFILE}.bk
# generate phony header
cat << END > ${SRS_WORKDIR}/${SRS_MAKEFILE}
.PHONY: default _default install install-api help clean destroy server srs_ingest_hls librtmp utest _prepare_dir $__mphonys
.PHONY: default _default install help clean destroy server srs_ingest_hls librtmp utest _prepare_dir $__mphonys
.PHONY: clean_srs clean_modules clean_st clean_openssl clean_ffmpeg clean_nginx clean_cherrypy
.PHONY: st
@ -367,7 +367,7 @@ _default: server srs_ingest_hls librtmp utest __modules $__mdefaults
@bash objs/_srs_build_summary.sh
help:
@echo "Usage: make <help>|<clean>|<destroy>|<server>|<librtmp>|<utest>|<install>|<install-api>|<uninstall>"
@echo "Usage: make <help>|<clean>|<destroy>|<server>|<librtmp>|<utest>|<install>|<uninstall>"
@echo " help display this help menu"
@echo " clean cleanup project and all depends"
@echo " destroy Cleanup all files for this platform in ${SRS_OBJS_DIR}/${SRS_PLATFORM}"
@ -375,7 +375,6 @@ help:
@echo " librtmp build the client publish/play library, and samples"
@echo " utest build the utest for srs"
@echo " install install srs to the prefix path"
@echo " install-api install srs and api-server to the prefix path"
@echo " uninstall uninstall srs from prefix path"
@echo "To clean special module:"
@echo " clean_st Clean depend st-srs in ${SRS_OBJS_DIR}/${SRS_PLATFORM}/st-srs"
@ -473,9 +472,6 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT != NO ]; then
uninstall:
@echo "Disable uninstall for srs-librtmp"
install-api:
@echo "Disable install-api for srs-librtmp"
install:
@echo "Disable install for srs-librtmp"
@ -486,30 +482,6 @@ uninstall:
@echo "rmdir \$(SRS_PREFIX)"
@rm -rf \$(SRS_PREFIX)
install-api: install
@echo "Now mkdir \$(__REAL_INSTALL)"
@mkdir -p \$(__REAL_INSTALL)
@echo "Now copy binary files"
@mkdir -p \$(__REAL_INSTALL)/research/api-server
@cp research/api-server/server.py \$(__REAL_INSTALL)/research/api-server
@mkdir -p \$(__REAL_INSTALL)/objs/ffmpeg/bin
@cp objs/ffmpeg/bin/ffmpeg \$(__REAL_INSTALL)/objs/ffmpeg/bin
@echo "Now copy html files"
@mkdir -p \$(__REAL_INSTALL)/research/api-server/static-dir/players
@cp research/api-server/static-dir/crossdomain.xml \$(__REAL_INSTALL)/research/api-server/static-dir
@cp research/api-server/static-dir/index.html \$(__REAL_INSTALL)/research/api-server/static-dir
@cp -r research/api-server/static-dir/players/* \$(__REAL_INSTALL)/research/api-server/static-dir/players
@echo "Now copy init.d script files"
@mkdir -p \$(__REAL_INSTALL)/etc/init.d
@cp etc/init.d/srs-api \$(__REAL_INSTALL)/etc/init.d
@sed -i "s|^ROOT=.*|ROOT=\"\$(SRS_PREFIX)\"|g" \$(__REAL_INSTALL)/etc/init.d/srs-api
@echo ""
@echo "The api installed, to link and start api:"
@echo " sudo ln -sf \$(SRS_PREFIX)/etc/init.d/srs-api /etc/init.d/srs-api"
@echo " /etc/init.d/srs-api start"
@echo " http://\$(shell bash auto/local_ip.sh):8085"
@echo "@see: https://github.com/ossrs/srs/wiki/v1_CN_LinuxService"
install:
@echo "Now mkdir \$(__REAL_INSTALL)"
@mkdir -p \$(__REAL_INSTALL)

1
trunk/doc/source.flv Symbolic link
View file

@ -0,0 +1 @@
source.200kbps.768x320.flv

View file

@ -1,50 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<title>SRS</title>
<title>SRS</title>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="players/css/bootstrap.min.css"/>
<script type="text/javascript" src="players/js/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="players/js/bootstrap.min.js"></script>
<script type="text/javascript" src="players/js/swfobject.js"></script>
<script type="text/javascript" src="players/js/srs.page.js"></script>
<style>
body{
padding-top: 55px;
}
</style>
<script type="text/javascript">
$(function(){
update_nav();
// direct to the default vhost for players.
window.location.href = "players/index.html" + window.location.search;
});
</script>
</head>
<body>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="index.html">SRS</a>
<div class="nav-collapse collapse">
<ul class="nav">
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
</ul>
</div>
</div>
</div>
</div>
<div class="container">
<hr>
<footer>
<p><a href="https://github.com/ossrs/srs">SRS Team &copy; 2013</a></p>
</footer>
</div>
<h3><a href="https://github.com/ossrs/srs">SRS</a> works!</h3>
<p>
Click <a id="en" href="#">here</a> to enter SRS console.<br/>
点击进入<a id="cn" href="#">SRS控制台</a>
</p>
<p>
Click <a href="players/">here</a> to start SRS player.<br/>
点击进入<a href="players/">SRS播放器</a>
</p>
<p><a href="https://github.com/ossrs/srs">SRS Team &copy; 2021</a></p>
<script type="text/javascript">
// http://localhost:8080/console/ng_index.html#/connect?port=1985
// http://localhost:8080/console/ng_index.html#/summaries?port=1985
var en_url = window.location.protocol + "//" + window.location.host + "/console/en_index.html#/summaries?port=1985";
var cn_url = window.location.protocol + "//" + window.location.host + "/console/ng_index.html#/summaries?port=1985";
document.getElementById("en").setAttribute('href', en_url);
document.getElementById("cn").setAttribute('href', cn_url);
</script>
</body>

View file

@ -0,0 +1,68 @@
<!DOCTYPE html>
<html ng-app="scApp">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>SRS控制台</title>
<link rel="stylesheet" type="text/css" href="js/3rdparty/bootstrap.min.css"/>
<link rel="stylesheet" type="text/css" href="js/srs.console.css"/>
<style>
body {
padding-top: 50px;
}
</style>
<script type="text/javascript" src="js/3rdparty/angular.min.js"></script>
<script type="text/javascript" src="js/3rdparty/angular-route.min.js"></script>
<script type="text/javascript" src="js/3rdparty/angular-resource.min.js"></script>
<script type="text/javascript" src="js/winlin.utility.js"></script>
<script type="text/javascript" src="js/bravo_alert/alert.js"></script>
<script type="text/javascript" src="js/bravo_popover/popover.js"></script>
<script type="text/javascript" src="js/srs.en.js"></script>
<script type="text/javascript" src="js/srs.console.js"></script>
</head>
<body ng-controller="CSCMain">
<img src='https://ossrs.net:8443/gif/v1/sls.gif?site=ossrs.net&path=/console/enindex'/>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="https://github.com/ossrs/srs" target="_blank">SRS</a>
<ul class="nav">
<li class="{{'/console'| sc_filter_nav_active}}"><a href="javascript:void(0)" ng-click="gogogo('/connect')">ConnectSRS</a></li>
<li class="{{'/summaries'| sc_filter_nav_active}}"><a href="javascript:void(0)" ng-click="gogogo('/summaries')">Overview</a></li>
<li class="{{'/vhosts'| sc_filter_nav_active}}"><a href="javascript:void(0)" ng-click="gogogo('/vhosts')">Vhosts</a></li>
<li class="{{'/streams'| sc_filter_nav_active}}"><a href="javascript:void(0)" ng-click="gogogo('/streams')">Streams</a></li>
<li class="{{'/clients'| sc_filter_nav_active}}"><a href="javascript:void(0)" ng-click="gogogo('/clients')">Clients</a></li>
<li class="{{'/configs'| sc_filter_nav_active}}"><a href="javascript:void(0)" ng-click="gogogo('/configs')">Config</a></li>
<li class="{{'/dvr'| sc_filter_nav_active}}"><a href="javascript:void(0)" ng-click="gogogo('/dvr')">DVR</a></li>
<li><a href="javascript:void(0)" ng-click="redirect('en_index.html', 'ng_index.html')">Chinese</a></li>
<li>
<a href="https://github.com/ossrs/srs-console">
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/ossrs/srs-console?style=social">
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="container-fluid">
<div ng-view></div>
<div bravo-alert ng-repeat="log in logs" class="alert fade in {{log.level|sc_filter_log_level}}">
<button type="button" class="close" data-dismiss="alert">×</button>
<strong>{{log.level}}:</strong> {{log.msg}}
</div>
</div>
<footer class="footer">
<div class="container">
<ul class="footer-links">
<li>&copy; SRS 2020</li>
<li class="muted">|</li>
<li><a href="http://ossrs.net/" target="_blank">Releases</a></li>
<li class="muted">|</li>
<li><a href="https://github.com/ossrs/srs" target="_blank">SRS</a></li>
<li class="muted">|</li>
<li><a href="https://github.com/ossrs/srs-console" target="_blank">Github</a></li>
</ul>
</div>
</footer>
</body>
</html>

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>SrsConsole</title>
<script type="text/javascript">
window.location.href = "ng_index.html#/summaries?port=1985";
</script>
</head>
<body>
</body>
</html>

View file

@ -0,0 +1,610 @@
/**
* @license AngularJS v1.2.17
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {'use strict';
var $resourceMinErr = angular.$$minErr('$resource');
// Helper functions and regex to lookup a dotted path on an object
// stopping at undefined/null. The path must be composed of ASCII
// identifiers (just like $parse)
var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$][0-9a-zA-Z_$]*)+$/;
function isValidDottedPath(path) {
return (path != null && path !== '' && path !== 'hasOwnProperty' &&
MEMBER_NAME_REGEX.test('.' + path));
}
function lookupDottedPath(obj, path) {
if (!isValidDottedPath(path)) {
throw $resourceMinErr('badmember', 'Dotted member path "@{0}" is invalid.', path);
}
var keys = path.split('.');
for (var i = 0, ii = keys.length; i < ii && obj !== undefined; i++) {
var key = keys[i];
obj = (obj !== null) ? obj[key] : undefined;
}
return obj;
}
/**
* Create a shallow copy of an object and clear other fields from the destination
*/
function shallowClearAndCopy(src, dst) {
dst = dst || {};
angular.forEach(dst, function(value, key){
delete dst[key];
});
for (var key in src) {
if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
dst[key] = src[key];
}
}
return dst;
}
/**
* @ngdoc module
* @name ngResource
* @description
*
* # ngResource
*
* The `ngResource` module provides interaction support with RESTful services
* via the $resource service.
*
*
* <div doc-module-components="ngResource"></div>
*
* See {@link ngResource.$resource `$resource`} for usage.
*/
/**
* @ngdoc service
* @name $resource
* @requires $http
*
* @description
* A factory which creates a resource object that lets you interact with
* [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources.
*
* The returned resource object has action methods which provide high-level behaviors without
* the need to interact with the low level {@link ng.$http $http} service.
*
* Requires the {@link ngResource `ngResource`} module to be installed.
*
* @param {string} url A parametrized URL template with parameters prefixed by `:` as in
* `/user/:username`. If you are using a URL with a port number (e.g.
* `http://example.com:8080/api`), it will be respected.
*
* If you are using a url with a suffix, just add the suffix, like this:
* `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')`
* or even `$resource('http://example.com/resource/:resource_id.:format')`
* If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be
* collapsed down to a single `.`. If you need this sequence to appear and not collapse then you
* can escape it with `/\.`.
*
* @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
* `actions` methods. If any of the parameter value is a function, it will be executed every time
* when a param value needs to be obtained for a request (unless the param was overridden).
*
* Each key value in the parameter object is first bound to url template if present and then any
* excess keys are appended to the url search query after the `?`.
*
* Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
* URL `/path/greet?salutation=Hello`.
*
* If the parameter value is prefixed with `@` then the value of that parameter will be taken
* from the corresponding key on the data object (useful for non-GET operations).
*
* @param {Object.<Object>=} actions Hash with declaration of custom action that should extend
* the default set of resource actions. The declaration should be created in the format of {@link
* ng.$http#usage_parameters $http.config}:
*
* {action1: {method:?, params:?, isArray:?, headers:?, ...},
* action2: {method:?, params:?, isArray:?, headers:?, ...},
* ...}
*
* Where:
*
* - **`action`** {string} The name of action. This name becomes the name of the method on
* your resource object.
* - **`method`** {string} HTTP request method. Valid methods are: `GET`, `POST`, `PUT`,
* `DELETE`, and `JSONP`.
* - **`params`** {Object=} Optional set of pre-bound parameters for this action. If any of
* the parameter value is a function, it will be executed every time when a param value needs to
* be obtained for a request (unless the param was overridden).
* - **`url`** {string} action specific `url` override. The url templating is supported just
* like for the resource-level urls.
* - **`isArray`** {boolean=} If true then the returned object for this action is an array,
* see `returns` section.
* - **`transformRequest`**
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}`
* transform function or an array of such functions. The transform function takes the http
* request body and headers and returns its transformed (typically serialized) version.
* - **`transformResponse`**
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}`
* transform function or an array of such functions. The transform function takes the http
* response body and headers and returns its transformed (typically deserialized) version.
* - **`cache`** `{boolean|Cache}` If true, a default $http cache will be used to cache the
* GET request, otherwise if a cache instance built with
* {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
* caching.
* - **`timeout`** `{number|Promise}` timeout in milliseconds, or {@link ng.$q promise} that
* should abort the request when resolved.
* - **`withCredentials`** - `{boolean}` - whether to set the `withCredentials` flag on the
* XHR object. See
* [requests with credentials](https://developer.mozilla.org/en/http_access_control#section_5)
* for more information.
* - **`responseType`** - `{string}` - see
* [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
* - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods -
* `response` and `responseError`. Both `response` and `responseError` interceptors get called
* with `http response` object. See {@link ng.$http $http interceptors}.
*
* @returns {Object} A resource "class" object with methods for the default set of resource actions
* optionally extended with custom `actions`. The default set contains these actions:
* ```js
* { 'get': {method:'GET'},
* 'save': {method:'POST'},
* 'query': {method:'GET', isArray:true},
* 'remove': {method:'DELETE'},
* 'delete': {method:'DELETE'} };
* ```
*
* Calling these methods invoke an {@link ng.$http} with the specified http method,
* destination and parameters. When the data is returned from the server then the object is an
* instance of the resource class. The actions `save`, `remove` and `delete` are available on it
* as methods with the `$` prefix. This allows you to easily perform CRUD operations (create,
* read, update, delete) on server-side data like this:
* ```js
* var User = $resource('/user/:userId', {userId:'@id'});
* var user = User.get({userId:123}, function() {
* user.abc = true;
* user.$save();
* });
* ```
*
* It is important to realize that invoking a $resource object method immediately returns an
* empty reference (object or array depending on `isArray`). Once the data is returned from the
* server the existing reference is populated with the actual data. This is a useful trick since
* usually the resource is assigned to a model which is then rendered by the view. Having an empty
* object results in no rendering, once the data arrives from the server then the object is
* populated with the data and the view automatically re-renders itself showing the new data. This
* means that in most cases one never has to write a callback function for the action methods.
*
* The action methods on the class object or instance object can be invoked with the following
* parameters:
*
* - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])`
* - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])`
* - non-GET instance actions: `instance.$action([parameters], [success], [error])`
*
* Success callback is called with (value, responseHeaders) arguments. Error callback is called
* with (httpResponse) argument.
*
* Class actions return empty instance (with additional properties below).
* Instance actions return promise of the action.
*
* The Resource instances and collection have these additional properties:
*
* - `$promise`: the {@link ng.$q promise} of the original server interaction that created this
* instance or collection.
*
* On success, the promise is resolved with the same resource instance or collection object,
* updated with data from server. This makes it easy to use in
* {@link ngRoute.$routeProvider resolve section of $routeProvider.when()} to defer view
* rendering until the resource(s) are loaded.
*
* On failure, the promise is resolved with the {@link ng.$http http response} object, without
* the `resource` property.
*
* If an interceptor object was provided, the promise will instead be resolved with the value
* returned by the interceptor.
*
* - `$resolved`: `true` after first server interaction is completed (either with success or
* rejection), `false` before that. Knowing if the Resource has been resolved is useful in
* data-binding.
*
* @example
*
* # Credit card resource
*
* ```js
// Define CreditCard class
var CreditCard = $resource('/user/:userId/card/:cardId',
{userId:123, cardId:'@id'}, {
charge: {method:'POST', params:{charge:true}}
});
// We can retrieve a collection from the server
var cards = CreditCard.query(function() {
// GET: /user/123/card
// server returns: [ {id:456, number:'1234', name:'Smith'} ];
var card = cards[0];
// each item is an instance of CreditCard
expect(card instanceof CreditCard).toEqual(true);
card.name = "J. Smith";
// non GET methods are mapped onto the instances
card.$save();
// POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'}
// server returns: {id:456, number:'1234', name: 'J. Smith'};
// our custom method is mapped as well.
card.$charge({amount:9.99});
// POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'}
});
// we can create an instance as well
var newCard = new CreditCard({number:'0123'});
newCard.name = "Mike Smith";
newCard.$save();
// POST: /user/123/card {number:'0123', name:'Mike Smith'}
// server returns: {id:789, number:'0123', name: 'Mike Smith'};
expect(newCard.id).toEqual(789);
* ```
*
* The object returned from this function execution is a resource "class" which has "static" method
* for each action in the definition.
*
* Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and
* `headers`.
* When the data is returned from the server then the object is an instance of the resource type and
* all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
* operations (create, read, update, delete) on server-side data.
```js
var User = $resource('/user/:userId', {userId:'@id'});
User.get({userId:123}, function(user) {
user.abc = true;
user.$save();
});
```
*
* It's worth noting that the success callback for `get`, `query` and other methods gets passed
* in the response that came from the server as well as $http header getter function, so one
* could rewrite the above example and get access to http headers as:
*
```js
var User = $resource('/user/:userId', {userId:'@id'});
User.get({userId:123}, function(u, getResponseHeaders){
u.abc = true;
u.$save(function(u, putResponseHeaders) {
//u => saved user object
//putResponseHeaders => $http header getter
});
});
```
*
* You can also access the raw `$http` promise via the `$promise` property on the object returned
*
```
var User = $resource('/user/:userId', {userId:'@id'});
User.get({userId:123})
.$promise.then(function(user) {
$scope.user = user;
});
```
* # Creating a custom 'PUT' request
* In this example we create a custom method on our resource to make a PUT request
* ```js
* var app = angular.module('app', ['ngResource', 'ngRoute']);
*
* // Some APIs expect a PUT request in the format URL/object/ID
* // Here we are creating an 'update' method
* app.factory('Notes', ['$resource', function($resource) {
* return $resource('/notes/:id', null,
* {
* 'update': { method:'PUT' }
* });
* }]);
*
* // In our controller we get the ID from the URL using ngRoute and $routeParams
* // We pass in $routeParams and our Notes factory along with $scope
* app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes',
function($scope, $routeParams, Notes) {
* // First get a note object from the factory
* var note = Notes.get({ id:$routeParams.id });
* $id = note.id;
*
* // Now call update passing in the ID first then the object you are updating
* Notes.update({ id:$id }, note);
*
* // This will PUT /notes/ID with the note object in the request payload
* }]);
* ```
*/
angular.module('ngResource', ['ng']).
factory('$resource', ['$http', '$q', function($http, $q) {
var DEFAULT_ACTIONS = {
'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'}
};
var noop = angular.noop,
forEach = angular.forEach,
extend = angular.extend,
copy = angular.copy,
isFunction = angular.isFunction;
/**
* We need our custom method because encodeURIComponent is too aggressive and doesn't follow
* http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
* segments:
* segment = *pchar
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
* pct-encoded = "%" HEXDIG HEXDIG
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
* / "*" / "+" / "," / ";" / "="
*/
function encodeUriSegment(val) {
return encodeUriQuery(val, true).
replace(/%26/gi, '&').
replace(/%3D/gi, '=').
replace(/%2B/gi, '+');
}
/**
* This method is intended for encoding *key* or *value* parts of query component. We need a
* custom method because encodeURIComponent is too aggressive and encodes stuff that doesn't
* have to be encoded per http://tools.ietf.org/html/rfc3986:
* query = *( pchar / "/" / "?" )
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* pct-encoded = "%" HEXDIG HEXDIG
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
* / "*" / "+" / "," / ";" / "="
*/
function encodeUriQuery(val, pctEncodeSpaces) {
return encodeURIComponent(val).
replace(/%40/gi, '@').
replace(/%3A/gi, ':').
replace(/%24/g, '$').
replace(/%2C/gi, ',').
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
}
function Route(template, defaults) {
this.template = template;
this.defaults = defaults || {};
this.urlParams = {};
}
Route.prototype = {
setUrlParams: function(config, params, actionUrl) {
var self = this,
url = actionUrl || self.template,
val,
encodedVal;
var urlParams = self.urlParams = {};
forEach(url.split(/\W/), function(param){
if (param === 'hasOwnProperty') {
throw $resourceMinErr('badname', "hasOwnProperty is not a valid parameter name.");
}
if (!(new RegExp("^\\d+$").test(param)) && param &&
(new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) {
urlParams[param] = true;
}
});
url = url.replace(/\\:/g, ':');
params = params || {};
forEach(self.urlParams, function(_, urlParam){
val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
if (angular.isDefined(val) && val !== null) {
encodedVal = encodeUriSegment(val);
url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), function(match, p1) {
return encodedVal + p1;
});
} else {
url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match,
leadingSlashes, tail) {
if (tail.charAt(0) == '/') {
return tail;
} else {
return leadingSlashes + tail;
}
});
}
});
// strip trailing slashes and set the url
url = url.replace(/\/+$/, '') || '/';
// then replace collapse `/.` if found in the last URL path segment before the query
// E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
url = url.replace(/\/\.(?=\w+($|\?))/, '.');
// replace escaped `/\.` with `/.`
config.url = url.replace(/\/\\\./, '/.');
// set params - delegate param encoding to $http
forEach(params, function(value, key){
if (!self.urlParams[key]) {
config.params = config.params || {};
config.params[key] = value;
}
});
}
};
function resourceFactory(url, paramDefaults, actions) {
var route = new Route(url);
actions = extend({}, DEFAULT_ACTIONS, actions);
function extractParams(data, actionParams){
var ids = {};
actionParams = extend({}, paramDefaults, actionParams);
forEach(actionParams, function(value, key){
if (isFunction(value)) { value = value(); }
ids[key] = value && value.charAt && value.charAt(0) == '@' ?
lookupDottedPath(data, value.substr(1)) : value;
});
return ids;
}
function defaultResponseInterceptor(response) {
return response.resource;
}
function Resource(value){
shallowClearAndCopy(value || {}, this);
}
forEach(actions, function(action, name) {
var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
Resource[name] = function(a1, a2, a3, a4) {
var params = {}, data, success, error;
/* jshint -W086 */ /* (purposefully fall through case statements) */
switch(arguments.length) {
case 4:
error = a4;
success = a3;
//fallthrough
case 3:
case 2:
if (isFunction(a2)) {
if (isFunction(a1)) {
success = a1;
error = a2;
break;
}
success = a2;
error = a3;
//fallthrough
} else {
params = a1;
data = a2;
success = a3;
break;
}
case 1:
if (isFunction(a1)) success = a1;
else if (hasBody) data = a1;
else params = a1;
break;
case 0: break;
default:
throw $resourceMinErr('badargs',
"Expected up to 4 arguments [params, data, success, error], got {0} arguments",
arguments.length);
}
/* jshint +W086 */ /* (purposefully fall through case statements) */
var isInstanceCall = this instanceof Resource;
var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data));
var httpConfig = {};
var responseInterceptor = action.interceptor && action.interceptor.response ||
defaultResponseInterceptor;
var responseErrorInterceptor = action.interceptor && action.interceptor.responseError ||
undefined;
forEach(action, function(value, key) {
if (key != 'params' && key != 'isArray' && key != 'interceptor') {
httpConfig[key] = copy(value);
}
});
if (hasBody) httpConfig.data = data;
route.setUrlParams(httpConfig,
extend({}, extractParams(data, action.params || {}), params),
action.url);
var promise = $http(httpConfig).then(function(response) {
var data = response.data,
promise = value.$promise;
if (data) {
// Need to convert action.isArray to boolean in case it is undefined
// jshint -W018
if (angular.isArray(data) !== (!!action.isArray)) {
throw $resourceMinErr('badcfg', 'Error in resource configuration. Expected ' +
'response to contain an {0} but got an {1}',
action.isArray?'array':'object', angular.isArray(data)?'array':'object');
}
// jshint +W018
if (action.isArray) {
value.length = 0;
forEach(data, function(item) {
value.push(new Resource(item));
});
} else {
shallowClearAndCopy(data, value);
value.$promise = promise;
}
}
value.$resolved = true;
response.resource = value;
return response;
}, function(response) {
value.$resolved = true;
(error||noop)(response);
return $q.reject(response);
});
promise = promise.then(
function(response) {
var value = responseInterceptor(response);
(success||noop)(value, response.headers);
return value;
},
responseErrorInterceptor);
if (!isInstanceCall) {
// we are creating instance / collection
// - set the initial promise
// - return the instance / collection
value.$promise = promise;
value.$resolved = false;
return value;
}
// instance call
return promise;
};
Resource.prototype['$' + name] = function(params, success, error) {
if (isFunction(params)) {
error = success; success = params; params = {};
}
var result = Resource[name].call(this, params, this, success, error);
return result.$promise || result;
};
});
Resource.bind = function(additionalParamDefaults){
return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
};
return Resource;
}
return resourceFactory;
}]);
})(window, window.angular);

View file

@ -0,0 +1,13 @@
/*
AngularJS v1.2.17
(c) 2010-2014 Google, Inc. http://angularjs.org
License: MIT
*/
(function(H,a,A){'use strict';function D(p,g){g=g||{};a.forEach(g,function(a,c){delete g[c]});for(var c in p)!p.hasOwnProperty(c)||"$"===c.charAt(0)&&"$"===c.charAt(1)||(g[c]=p[c]);return g}var v=a.$$minErr("$resource"),C=/^(\.[a-zA-Z_$][0-9a-zA-Z_$]*)+$/;a.module("ngResource",["ng"]).factory("$resource",["$http","$q",function(p,g){function c(a,c){this.template=a;this.defaults=c||{};this.urlParams={}}function t(n,w,l){function r(h,d){var e={};d=x({},w,d);s(d,function(b,d){u(b)&&(b=b());var k;if(b&&
b.charAt&&"@"==b.charAt(0)){k=h;var a=b.substr(1);if(null==a||""===a||"hasOwnProperty"===a||!C.test("."+a))throw v("badmember",a);for(var a=a.split("."),f=0,c=a.length;f<c&&k!==A;f++){var g=a[f];k=null!==k?k[g]:A}}else k=b;e[d]=k});return e}function e(a){return a.resource}function f(a){D(a||{},this)}var F=new c(n);l=x({},B,l);s(l,function(h,d){var c=/^(POST|PUT|PATCH)$/i.test(h.method);f[d]=function(b,d,k,w){var q={},n,l,y;switch(arguments.length){case 4:y=w,l=k;case 3:case 2:if(u(d)){if(u(b)){l=
b;y=d;break}l=d;y=k}else{q=b;n=d;l=k;break}case 1:u(b)?l=b:c?n=b:q=b;break;case 0:break;default:throw v("badargs",arguments.length);}var t=this instanceof f,m=t?n:h.isArray?[]:new f(n),z={},B=h.interceptor&&h.interceptor.response||e,C=h.interceptor&&h.interceptor.responseError||A;s(h,function(a,b){"params"!=b&&("isArray"!=b&&"interceptor"!=b)&&(z[b]=G(a))});c&&(z.data=n);F.setUrlParams(z,x({},r(n,h.params||{}),q),h.url);q=p(z).then(function(b){var d=b.data,k=m.$promise;if(d){if(a.isArray(d)!==!!h.isArray)throw v("badcfg",
h.isArray?"array":"object",a.isArray(d)?"array":"object");h.isArray?(m.length=0,s(d,function(b){m.push(new f(b))})):(D(d,m),m.$promise=k)}m.$resolved=!0;b.resource=m;return b},function(b){m.$resolved=!0;(y||E)(b);return g.reject(b)});q=q.then(function(b){var a=B(b);(l||E)(a,b.headers);return a},C);return t?q:(m.$promise=q,m.$resolved=!1,m)};f.prototype["$"+d]=function(b,a,k){u(b)&&(k=a,a=b,b={});b=f[d].call(this,b,this,a,k);return b.$promise||b}});f.bind=function(a){return t(n,x({},w,a),l)};return f}
var B={get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}},E=a.noop,s=a.forEach,x=a.extend,G=a.copy,u=a.isFunction;c.prototype={setUrlParams:function(c,g,l){var r=this,e=l||r.template,f,p,h=r.urlParams={};s(e.split(/\W/),function(a){if("hasOwnProperty"===a)throw v("badname");!/^\d+$/.test(a)&&(a&&RegExp("(^|[^\\\\]):"+a+"(\\W|$)").test(e))&&(h[a]=!0)});e=e.replace(/\\:/g,":");g=g||{};s(r.urlParams,function(d,c){f=g.hasOwnProperty(c)?
g[c]:r.defaults[c];a.isDefined(f)&&null!==f?(p=encodeURIComponent(f).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"%20").replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+"),e=e.replace(RegExp(":"+c+"(\\W|$)","g"),function(a,c){return p+c})):e=e.replace(RegExp("(/?):"+c+"(\\W|$)","g"),function(a,c,d){return"/"==d.charAt(0)?d:c+d})});e=e.replace(/\/+$/,"")||"/";e=e.replace(/\/\.(?=\w+($|\?))/,".");c.url=e.replace(/\/\\\./,"/.");s(g,function(a,
e){r.urlParams[e]||(c.params=c.params||{},c.params[e]=a)})}};return t}])})(window,window.angular);
//# sourceMappingURL=angular-resource.min.js.map

File diff suppressed because one or more lines are too long

View file

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

View file

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

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,212 @@
/*
AngularJS v1.2.17
(c) 2010-2014 Google, Inc. http://angularjs.org
License: MIT
*/
(function(P,V,s){'use strict';function u(b){return function(){var a=arguments[0],c,a="["+(b?b+":":"")+a+"] http://errors.angularjs.org/1.2.17/"+(b?b+"/":"")+a;for(c=1;c<arguments.length;c++)a=a+(1==c?"?":"&")+"p"+(c-1)+"="+encodeURIComponent("function"==typeof arguments[c]?arguments[c].toString().replace(/ \{[\s\S]*$/,""):"undefined"==typeof arguments[c]?"undefined":"string"!=typeof arguments[c]?JSON.stringify(arguments[c]):arguments[c]);return Error(a)}}function db(b){if(null==b||Da(b))return!1;
var a=b.length;return 1===b.nodeType&&a?!0:C(b)||K(b)||0===a||"number"===typeof a&&0<a&&a-1 in b}function q(b,a,c){var d;if(b)if(Q(b))for(d in b)"prototype"==d||("length"==d||"name"==d||b.hasOwnProperty&&!b.hasOwnProperty(d))||a.call(c,b[d],d);else if(b.forEach&&b.forEach!==q)b.forEach(a,c);else if(db(b))for(d=0;d<b.length;d++)a.call(c,b[d],d);else for(d in b)b.hasOwnProperty(d)&&a.call(c,b[d],d);return b}function Wb(b){var a=[],c;for(c in b)b.hasOwnProperty(c)&&a.push(c);return a.sort()}function Sc(b,
a,c){for(var d=Wb(b),e=0;e<d.length;e++)a.call(c,b[d[e]],d[e]);return d}function Xb(b){return function(a,c){b(c,a)}}function eb(){for(var b=ja.length,a;b;){b--;a=ja[b].charCodeAt(0);if(57==a)return ja[b]="A",ja.join("");if(90==a)ja[b]="0";else return ja[b]=String.fromCharCode(a+1),ja.join("")}ja.unshift("0");return ja.join("")}function Yb(b,a){a?b.$$hashKey=a:delete b.$$hashKey}function E(b){var a=b.$$hashKey;q(arguments,function(a){a!==b&&q(a,function(a,c){b[c]=a})});Yb(b,a);return b}function Y(b){return parseInt(b,
10)}function Zb(b,a){return E(new (E(function(){},{prototype:b})),a)}function w(){}function Ea(b){return b}function aa(b){return function(){return b}}function H(b){return"undefined"===typeof b}function z(b){return"undefined"!==typeof b}function U(b){return null!=b&&"object"===typeof b}function C(b){return"string"===typeof b}function zb(b){return"number"===typeof b}function Ma(b){return"[object Date]"===wa.call(b)}function K(b){return"[object Array]"===wa.call(b)}function Q(b){return"function"===typeof b}
function fb(b){return"[object RegExp]"===wa.call(b)}function Da(b){return b&&b.document&&b.location&&b.alert&&b.setInterval}function Tc(b){return!(!b||!(b.nodeName||b.prop&&b.attr&&b.find))}function Uc(b,a,c){var d=[];q(b,function(b,f,g){d.push(a.call(c,b,f,g))});return d}function Na(b,a){if(b.indexOf)return b.indexOf(a);for(var c=0;c<b.length;c++)if(a===b[c])return c;return-1}function Oa(b,a){var c=Na(b,a);0<=c&&b.splice(c,1);return a}function Fa(b,a,c,d){if(Da(b)||b&&b.$evalAsync&&b.$watch)throw Pa("cpws");
if(a){if(b===a)throw Pa("cpi");c=c||[];d=d||[];if(U(b)){var e=Na(c,b);if(-1!==e)return d[e];c.push(b);d.push(a)}if(K(b))for(var f=a.length=0;f<b.length;f++)e=Fa(b[f],null,c,d),U(b[f])&&(c.push(b[f]),d.push(e)),a.push(e);else{var g=a.$$hashKey;q(a,function(b,c){delete a[c]});for(f in b)e=Fa(b[f],null,c,d),U(b[f])&&(c.push(b[f]),d.push(e)),a[f]=e;Yb(a,g)}}else(a=b)&&(K(b)?a=Fa(b,[],c,d):Ma(b)?a=new Date(b.getTime()):fb(b)?a=RegExp(b.source):U(b)&&(a=Fa(b,{},c,d)));return a}function ka(b,a){if(K(b)){a=
a||[];for(var c=0;c<b.length;c++)a[c]=b[c]}else if(U(b))for(c in a=a||{},b)!Ab.call(b,c)||"$"===c.charAt(0)&&"$"===c.charAt(1)||(a[c]=b[c]);return a||b}function xa(b,a){if(b===a)return!0;if(null===b||null===a)return!1;if(b!==b&&a!==a)return!0;var c=typeof b,d;if(c==typeof a&&"object"==c)if(K(b)){if(!K(a))return!1;if((c=b.length)==a.length){for(d=0;d<c;d++)if(!xa(b[d],a[d]))return!1;return!0}}else{if(Ma(b))return Ma(a)&&b.getTime()==a.getTime();if(fb(b)&&fb(a))return b.toString()==a.toString();if(b&&
b.$evalAsync&&b.$watch||a&&a.$evalAsync&&a.$watch||Da(b)||Da(a)||K(a))return!1;c={};for(d in b)if("$"!==d.charAt(0)&&!Q(b[d])){if(!xa(b[d],a[d]))return!1;c[d]=!0}for(d in a)if(!c.hasOwnProperty(d)&&"$"!==d.charAt(0)&&a[d]!==s&&!Q(a[d]))return!1;return!0}return!1}function $b(){return V.securityPolicy&&V.securityPolicy.isActive||V.querySelector&&!(!V.querySelector("[ng-csp]")&&!V.querySelector("[data-ng-csp]"))}function gb(b,a){var c=2<arguments.length?ya.call(arguments,2):[];return!Q(a)||a instanceof
RegExp?a:c.length?function(){return arguments.length?a.apply(b,c.concat(ya.call(arguments,0))):a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}}function Vc(b,a){var c=a;"string"===typeof b&&"$"===b.charAt(0)?c=s:Da(a)?c="$WINDOW":a&&V===a?c="$DOCUMENT":a&&(a.$evalAsync&&a.$watch)&&(c="$SCOPE");return c}function qa(b,a){return"undefined"===typeof b?s:JSON.stringify(b,Vc,a?" ":null)}function ac(b){return C(b)?JSON.parse(b):b}function Qa(b){"function"===typeof b?b=!0:
b&&0!==b.length?(b=J(""+b),b=!("f"==b||"0"==b||"false"==b||"no"==b||"n"==b||"[]"==b)):b=!1;return b}function ha(b){b=y(b).clone();try{b.empty()}catch(a){}var c=y("<div>").append(b).html();try{return 3===b[0].nodeType?J(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+J(b)})}catch(d){return J(c)}}function bc(b){try{return decodeURIComponent(b)}catch(a){}}function cc(b){var a={},c,d;q((b||"").split("&"),function(b){b&&(c=b.split("="),d=bc(c[0]),z(d)&&(b=z(c[1])?bc(c[1]):!0,
a[d]?K(a[d])?a[d].push(b):a[d]=[a[d],b]:a[d]=b))});return a}function Bb(b){var a=[];q(b,function(b,d){K(b)?q(b,function(b){a.push(za(d,!0)+(!0===b?"":"="+za(b,!0)))}):a.push(za(d,!0)+(!0===b?"":"="+za(b,!0)))});return a.length?a.join("&"):""}function hb(b){return za(b,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function za(b,a){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,a?"%20":"+")}function Wc(b,
a){function c(a){a&&d.push(a)}var d=[b],e,f,g=["ng:app","ng-app","x-ng-app","data-ng-app"],h=/\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;q(g,function(a){g[a]=!0;c(V.getElementById(a));a=a.replace(":","\\:");b.querySelectorAll&&(q(b.querySelectorAll("."+a),c),q(b.querySelectorAll("."+a+"\\:"),c),q(b.querySelectorAll("["+a+"]"),c))});q(d,function(a){if(!e){var b=h.exec(" "+a.className+" ");b?(e=a,f=(b[2]||"").replace(/\s+/g,",")):q(a.attributes,function(b){!e&&g[b.name]&&(e=a,f=b.value)})}});e&&a(e,f?[f]:[])}
function dc(b,a){var c=function(){b=y(b);if(b.injector()){var c=b[0]===V?"document":ha(b);throw Pa("btstrpd",c);}a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);a.unshift("ng");c=ec(a);c.invoke(["$rootScope","$rootElement","$compile","$injector","$animate",function(a,b,c,d,e){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return c},d=/^NG_DEFER_BOOTSTRAP!/;if(P&&!d.test(P.name))return c();P.name=P.name.replace(d,"");Ra.resumeBootstrap=function(b){q(b,function(b){a.push(b)});
c()}}function ib(b,a){a=a||"_";return b.replace(Xc,function(b,d){return(d?a:"")+b.toLowerCase()})}function Cb(b,a,c){if(!b)throw Pa("areq",a||"?",c||"required");return b}function Sa(b,a,c){c&&K(b)&&(b=b[b.length-1]);Cb(Q(b),a,"not a function, got "+(b&&"object"==typeof b?b.constructor.name||"Object":typeof b));return b}function Aa(b,a){if("hasOwnProperty"===b)throw Pa("badname",a);}function fc(b,a,c){if(!a)return b;a=a.split(".");for(var d,e=b,f=a.length,g=0;g<f;g++)d=a[g],b&&(b=(e=b)[d]);return!c&&
Q(b)?gb(e,b):b}function Db(b){var a=b[0];b=b[b.length-1];if(a===b)return y(a);var c=[a];do{a=a.nextSibling;if(!a)break;c.push(a)}while(a!==b);return y(c)}function Yc(b){var a=u("$injector"),c=u("ng");b=b.angular||(b.angular={});b.$$minErr=b.$$minErr||u;return b.module||(b.module=function(){var b={};return function(e,f,g){if("hasOwnProperty"===e)throw c("badname","module");f&&b.hasOwnProperty(e)&&(b[e]=null);return b[e]||(b[e]=function(){function b(a,d,e){return function(){c[e||"push"]([a,d,arguments]);
return n}}if(!f)throw a("nomod",e);var c=[],d=[],l=b("$injector","invoke"),n={_invokeQueue:c,_runBlocks:d,requires:f,name:e,provider:b("$provide","provider"),factory:b("$provide","factory"),service:b("$provide","service"),value:b("$provide","value"),constant:b("$provide","constant","unshift"),animation:b("$animateProvider","register"),filter:b("$filterProvider","register"),controller:b("$controllerProvider","register"),directive:b("$compileProvider","directive"),config:l,run:function(a){d.push(a);
return this}};g&&l(g);return n}())}}())}function Zc(b){E(b,{bootstrap:dc,copy:Fa,extend:E,equals:xa,element:y,forEach:q,injector:ec,noop:w,bind:gb,toJson:qa,fromJson:ac,identity:Ea,isUndefined:H,isDefined:z,isString:C,isFunction:Q,isObject:U,isNumber:zb,isElement:Tc,isArray:K,version:$c,isDate:Ma,lowercase:J,uppercase:Ga,callbacks:{counter:0},$$minErr:u,$$csp:$b});Ta=Yc(P);try{Ta("ngLocale")}catch(a){Ta("ngLocale",[]).provider("$locale",ad)}Ta("ng",["ngLocale"],["$provide",function(a){a.provider({$$sanitizeUri:bd});
a.provider("$compile",gc).directive({a:cd,input:hc,textarea:hc,form:dd,script:ed,select:fd,style:gd,option:hd,ngBind:id,ngBindHtml:jd,ngBindTemplate:kd,ngClass:ld,ngClassEven:md,ngClassOdd:nd,ngCloak:od,ngController:pd,ngForm:qd,ngHide:rd,ngIf:sd,ngInclude:td,ngInit:ud,ngNonBindable:vd,ngPluralize:wd,ngRepeat:xd,ngShow:yd,ngStyle:zd,ngSwitch:Ad,ngSwitchWhen:Bd,ngSwitchDefault:Cd,ngOptions:Dd,ngTransclude:Ed,ngModel:Fd,ngList:Gd,ngChange:Hd,required:ic,ngRequired:ic,ngValue:Id}).directive({ngInclude:Jd}).directive(Eb).directive(jc);
a.provider({$anchorScroll:Kd,$animate:Ld,$browser:Md,$cacheFactory:Nd,$controller:Od,$document:Pd,$exceptionHandler:Qd,$filter:kc,$interpolate:Rd,$interval:Sd,$http:Td,$httpBackend:Ud,$location:Vd,$log:Wd,$parse:Xd,$rootScope:Yd,$q:Zd,$sce:$d,$sceDelegate:ae,$sniffer:be,$templateCache:ce,$timeout:de,$window:ee,$$rAF:fe,$$asyncCallback:ge})}])}function Ua(b){return b.replace(he,function(a,b,d,e){return e?d.toUpperCase():d}).replace(ie,"Moz$1")}function Fb(b,a,c,d){function e(b){var e=c&&b?[this.filter(b)]:
[this],m=a,k,l,n,p,r,A;if(!d||null!=b)for(;e.length;)for(k=e.shift(),l=0,n=k.length;l<n;l++)for(p=y(k[l]),m?p.triggerHandler("$destroy"):m=!m,r=0,p=(A=p.children()).length;r<p;r++)e.push(Ba(A[r]));return f.apply(this,arguments)}var f=Ba.fn[b],f=f.$original||f;e.$original=f;Ba.fn[b]=e}function M(b){if(b instanceof M)return b;C(b)&&(b=ba(b));if(!(this instanceof M)){if(C(b)&&"<"!=b.charAt(0))throw Gb("nosel");return new M(b)}if(C(b)){var a=b;b=V;var c;if(c=je.exec(a))b=[b.createElement(c[1])];else{var d=
b,e;b=d.createDocumentFragment();c=[];if(Hb.test(a)){d=b.appendChild(d.createElement("div"));e=(ke.exec(a)||["",""])[1].toLowerCase();e=da[e]||da._default;d.innerHTML="<div>&#160;</div>"+e[1]+a.replace(le,"<$1></$2>")+e[2];d.removeChild(d.firstChild);for(a=e[0];a--;)d=d.lastChild;a=0;for(e=d.childNodes.length;a<e;++a)c.push(d.childNodes[a]);d=b.firstChild;d.textContent=""}else c.push(d.createTextNode(a));b.textContent="";b.innerHTML="";b=c}Ib(this,b);y(V.createDocumentFragment()).append(this)}else Ib(this,
b)}function Jb(b){return b.cloneNode(!0)}function Ha(b){lc(b);var a=0;for(b=b.childNodes||[];a<b.length;a++)Ha(b[a])}function mc(b,a,c,d){if(z(d))throw Gb("offargs");var e=la(b,"events");la(b,"handle")&&(H(a)?q(e,function(a,c){Va(b,c,a);delete e[c]}):q(a.split(" "),function(a){H(c)?(Va(b,a,e[a]),delete e[a]):Oa(e[a]||[],c)}))}function lc(b,a){var c=b[jb],d=Wa[c];d&&(a?delete Wa[c].data[a]:(d.handle&&(d.events.$destroy&&d.handle({},"$destroy"),mc(b)),delete Wa[c],b[jb]=s))}function la(b,a,c){var d=
b[jb],d=Wa[d||-1];if(z(c))d||(b[jb]=d=++me,d=Wa[d]={}),d[a]=c;else return d&&d[a]}function nc(b,a,c){var d=la(b,"data"),e=z(c),f=!e&&z(a),g=f&&!U(a);d||g||la(b,"data",d={});if(e)d[a]=c;else if(f){if(g)return d&&d[a];E(d,a)}else return d}function Kb(b,a){return b.getAttribute?-1<(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").indexOf(" "+a+" "):!1}function kb(b,a){a&&b.setAttribute&&q(a.split(" "),function(a){b.setAttribute("class",ba((" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g,
" ").replace(" "+ba(a)+" "," ")))})}function lb(b,a){if(a&&b.setAttribute){var c=(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ");q(a.split(" "),function(a){a=ba(a);-1===c.indexOf(" "+a+" ")&&(c+=a+" ")});b.setAttribute("class",ba(c))}}function Ib(b,a){if(a){a=a.nodeName||!z(a.length)||Da(a)?[a]:a;for(var c=0;c<a.length;c++)b.push(a[c])}}function oc(b,a){return mb(b,"$"+(a||"ngController")+"Controller")}function mb(b,a,c){b=y(b);9==b[0].nodeType&&(b=b.find("html"));for(a=K(a)?a:[a];b.length;){for(var d=
b[0],e=0,f=a.length;e<f;e++)if((c=b.data(a[e]))!==s)return c;b=y(d.parentNode||11===d.nodeType&&d.host)}}function pc(b){for(var a=0,c=b.childNodes;a<c.length;a++)Ha(c[a]);for(;b.firstChild;)b.removeChild(b.firstChild)}function qc(b,a){var c=nb[a.toLowerCase()];return c&&rc[b.nodeName]&&c}function ne(b,a){var c=function(c,e){c.preventDefault||(c.preventDefault=function(){c.returnValue=!1});c.stopPropagation||(c.stopPropagation=function(){c.cancelBubble=!0});c.target||(c.target=c.srcElement||V);if(H(c.defaultPrevented)){var f=
c.preventDefault;c.preventDefault=function(){c.defaultPrevented=!0;f.call(c)};c.defaultPrevented=!1}c.isDefaultPrevented=function(){return c.defaultPrevented||!1===c.returnValue};var g=ka(a[e||c.type]||[]);q(g,function(a){a.call(b,c)});8>=S?(c.preventDefault=null,c.stopPropagation=null,c.isDefaultPrevented=null):(delete c.preventDefault,delete c.stopPropagation,delete c.isDefaultPrevented)};c.elem=b;return c}function Ia(b){var a=typeof b,c;"object"==a&&null!==b?"function"==typeof(c=b.$$hashKey)?c=
b.$$hashKey():c===s&&(c=b.$$hashKey=eb()):c=b;return a+":"+c}function Xa(b){q(b,this.put,this)}function sc(b){var a,c;"function"==typeof b?(a=b.$inject)||(a=[],b.length&&(c=b.toString().replace(oe,""),c=c.match(pe),q(c[1].split(qe),function(b){b.replace(re,function(b,c,d){a.push(d)})})),b.$inject=a):K(b)?(c=b.length-1,Sa(b[c],"fn"),a=b.slice(0,c)):Sa(b,"fn",!0);return a}function ec(b){function a(a){return function(b,c){if(U(b))q(b,Xb(a));else return a(b,c)}}function c(a,b){Aa(a,"service");if(Q(b)||
K(b))b=n.instantiate(b);if(!b.$get)throw Ya("pget",a);return l[a+h]=b}function d(a,b){return c(a,{$get:b})}function e(a){var b=[],c,d,f,h;q(a,function(a){if(!k.get(a)){k.put(a,!0);try{if(C(a))for(c=Ta(a),b=b.concat(e(c.requires)).concat(c._runBlocks),d=c._invokeQueue,f=0,h=d.length;f<h;f++){var g=d[f],m=n.get(g[0]);m[g[1]].apply(m,g[2])}else Q(a)?b.push(n.invoke(a)):K(a)?b.push(n.invoke(a)):Sa(a,"module")}catch(l){throw K(a)&&(a=a[a.length-1]),l.message&&(l.stack&&-1==l.stack.indexOf(l.message))&&
(l=l.message+"\n"+l.stack),Ya("modulerr",a,l.stack||l.message||l);}}});return b}function f(a,b){function c(d){if(a.hasOwnProperty(d)){if(a[d]===g)throw Ya("cdep",m.join(" <- "));return a[d]}try{return m.unshift(d),a[d]=g,a[d]=b(d)}catch(e){throw a[d]===g&&delete a[d],e;}finally{m.shift()}}function d(a,b,e){var f=[],h=sc(a),g,m,k;m=0;for(g=h.length;m<g;m++){k=h[m];if("string"!==typeof k)throw Ya("itkn",k);f.push(e&&e.hasOwnProperty(k)?e[k]:c(k))}a.$inject||(a=a[g]);return a.apply(b,f)}return{invoke:d,
instantiate:function(a,b){var c=function(){},e;c.prototype=(K(a)?a[a.length-1]:a).prototype;c=new c;e=d(a,c,b);return U(e)||Q(e)?e:c},get:c,annotate:sc,has:function(b){return l.hasOwnProperty(b+h)||a.hasOwnProperty(b)}}}var g={},h="Provider",m=[],k=new Xa,l={$provide:{provider:a(c),factory:a(d),service:a(function(a,b){return d(a,["$injector",function(a){return a.instantiate(b)}])}),value:a(function(a,b){return d(a,aa(b))}),constant:a(function(a,b){Aa(a,"constant");l[a]=b;p[a]=b}),decorator:function(a,
b){var c=n.get(a+h),d=c.$get;c.$get=function(){var a=r.invoke(d,c);return r.invoke(b,null,{$delegate:a})}}}},n=l.$injector=f(l,function(){throw Ya("unpr",m.join(" <- "));}),p={},r=p.$injector=f(p,function(a){a=n.get(a+h);return r.invoke(a.$get,a)});q(e(b),function(a){r.invoke(a||w)});return r}function Kd(){var b=!0;this.disableAutoScrolling=function(){b=!1};this.$get=["$window","$location","$rootScope",function(a,c,d){function e(a){var b=null;q(a,function(a){b||"a"!==J(a.nodeName)||(b=a)});return b}
function f(){var b=c.hash(),d;b?(d=g.getElementById(b))?d.scrollIntoView():(d=e(g.getElementsByName(b)))?d.scrollIntoView():"top"===b&&a.scrollTo(0,0):a.scrollTo(0,0)}var g=a.document;b&&d.$watch(function(){return c.hash()},function(){d.$evalAsync(f)});return f}]}function ge(){this.$get=["$$rAF","$timeout",function(b,a){return b.supported?function(a){return b(a)}:function(b){return a(b,0,!1)}}]}function se(b,a,c,d){function e(a){try{a.apply(null,ya.call(arguments,1))}finally{if(A--,0===A)for(;D.length;)try{D.pop()()}catch(b){c.error(b)}}}
function f(a,b){(function T(){q(x,function(a){a()});t=b(T,a)})()}function g(){v=null;N!=h.url()&&(N=h.url(),q(ma,function(a){a(h.url())}))}var h=this,m=a[0],k=b.location,l=b.history,n=b.setTimeout,p=b.clearTimeout,r={};h.isMock=!1;var A=0,D=[];h.$$completeOutstandingRequest=e;h.$$incOutstandingRequestCount=function(){A++};h.notifyWhenNoOutstandingRequests=function(a){q(x,function(a){a()});0===A?a():D.push(a)};var x=[],t;h.addPollFn=function(a){H(t)&&f(100,n);x.push(a);return a};var N=k.href,B=a.find("base"),
v=null;h.url=function(a,c){k!==b.location&&(k=b.location);l!==b.history&&(l=b.history);if(a){if(N!=a)return N=a,d.history?c?l.replaceState(null,"",a):(l.pushState(null,"",a),B.attr("href",B.attr("href"))):(v=a,c?k.replace(a):k.href=a),h}else return v||k.href.replace(/%27/g,"'")};var ma=[],L=!1;h.onUrlChange=function(a){if(!L){if(d.history)y(b).on("popstate",g);if(d.hashchange)y(b).on("hashchange",g);else h.addPollFn(g);L=!0}ma.push(a);return a};h.baseHref=function(){var a=B.attr("href");return a?
a.replace(/^(https?\:)?\/\/[^\/]*/,""):""};var O={},ca="",F=h.baseHref();h.cookies=function(a,b){var d,e,f,h;if(a)b===s?m.cookie=escape(a)+"=;path="+F+";expires=Thu, 01 Jan 1970 00:00:00 GMT":C(b)&&(d=(m.cookie=escape(a)+"="+escape(b)+";path="+F).length+1,4096<d&&c.warn("Cookie '"+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!"));else{if(m.cookie!==ca)for(ca=m.cookie,d=ca.split("; "),O={},f=0;f<d.length;f++)e=d[f],h=e.indexOf("="),0<h&&(a=unescape(e.substring(0,
h)),O[a]===s&&(O[a]=unescape(e.substring(h+1))));return O}};h.defer=function(a,b){var c;A++;c=n(function(){delete r[c];e(a)},b||0);r[c]=!0;return c};h.defer.cancel=function(a){return r[a]?(delete r[a],p(a),e(w),!0):!1}}function Md(){this.$get=["$window","$log","$sniffer","$document",function(b,a,c,d){return new se(b,d,a,c)}]}function Nd(){this.$get=function(){function b(b,d){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(b in
a)throw u("$cacheFactory")("iid",b);var g=0,h=E({},d,{id:b}),m={},k=d&&d.capacity||Number.MAX_VALUE,l={},n=null,p=null;return a[b]={put:function(a,b){if(k<Number.MAX_VALUE){var c=l[a]||(l[a]={key:a});e(c)}if(!H(b))return a in m||g++,m[a]=b,g>k&&this.remove(p.key),b},get:function(a){if(k<Number.MAX_VALUE){var b=l[a];if(!b)return;e(b)}return m[a]},remove:function(a){if(k<Number.MAX_VALUE){var b=l[a];if(!b)return;b==n&&(n=b.p);b==p&&(p=b.n);f(b.n,b.p);delete l[a]}delete m[a];g--},removeAll:function(){m=
{};g=0;l={};n=p=null},destroy:function(){l=h=m=null;delete a[b]},info:function(){return E({},h,{size:g})}}}var a={};b.info=function(){var b={};q(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return a[b]};return b}}function ce(){this.$get=["$cacheFactory",function(b){return b("templates")}]}function gc(b,a){var c={},d="Directive",e=/^\s*directive\:\s*([\d\w_\-]+)\s+(.*)$/,f=/(([\d\w_\-]+)(?:\:([^;]+))?;?)/,g=/^(on[a-z]+|formaction)$/;this.directive=function m(a,e){Aa(a,"directive");C(a)?
(Cb(e,"directiveFactory"),c.hasOwnProperty(a)||(c[a]=[],b.factory(a+d,["$injector","$exceptionHandler",function(b,d){var e=[];q(c[a],function(c,f){try{var g=b.invoke(c);Q(g)?g={compile:aa(g)}:!g.compile&&g.link&&(g.compile=aa(g.link));g.priority=g.priority||0;g.index=f;g.name=g.name||a;g.require=g.require||g.controller&&g.name;g.restrict=g.restrict||"A";e.push(g)}catch(m){d(m)}});return e}])),c[a].push(e)):q(a,Xb(m));return this};this.aHrefSanitizationWhitelist=function(b){return z(b)?(a.aHrefSanitizationWhitelist(b),
this):a.aHrefSanitizationWhitelist()};this.imgSrcSanitizationWhitelist=function(b){return z(b)?(a.imgSrcSanitizationWhitelist(b),this):a.imgSrcSanitizationWhitelist()};this.$get=["$injector","$interpolate","$exceptionHandler","$http","$templateCache","$parse","$controller","$rootScope","$document","$sce","$animate","$$sanitizeUri",function(a,b,l,n,p,r,A,D,x,t,N,B){function v(a,b,c,d,e){a instanceof y||(a=y(a));q(a,function(b,c){3==b.nodeType&&b.nodeValue.match(/\S+/)&&(a[c]=y(b).wrap("<span></span>").parent()[0])});
var f=L(a,b,a,c,d,e);ma(a,"ng-scope");return function(b,c,d){Cb(b,"scope");var e=c?Ja.clone.call(a):a;q(d,function(a,b){e.data("$"+b+"Controller",a)});d=0;for(var g=e.length;d<g;d++){var m=e[d].nodeType;1!==m&&9!==m||e.eq(d).data("$scope",b)}c&&c(e,b);f&&f(b,e,e);return e}}function ma(a,b){try{a.addClass(b)}catch(c){}}function L(a,b,c,d,e,f){function g(a,c,d,e){var f,k,l,r,n,p,A;f=c.length;var I=Array(f);for(n=0;n<f;n++)I[n]=c[n];A=n=0;for(p=m.length;n<p;A++)k=I[A],c=m[n++],f=m[n++],l=y(k),c?(c.scope?
(r=a.$new(),l.data("$scope",r)):r=a,(l=c.transclude)||!e&&b?c(f,r,k,d,O(a,l||b)):c(f,r,k,d,e)):f&&f(a,k.childNodes,s,e)}for(var m=[],k,l,r,n,p=0;p<a.length;p++)k=new Lb,l=ca(a[p],[],k,0===p?d:s,e),(f=l.length?ea(l,a[p],k,b,c,null,[],[],f):null)&&f.scope&&ma(y(a[p]),"ng-scope"),k=f&&f.terminal||!(r=a[p].childNodes)||!r.length?null:L(r,f?f.transclude:b),m.push(f,k),n=n||f||k,f=null;return n?g:null}function O(a,b){return function(c,d,e){var f=!1;c||(c=a.$new(),f=c.$$transcluded=!0);d=b(c,d,e);if(f)d.on("$destroy",
gb(c,c.$destroy));return d}}function ca(a,b,c,d,g){var m=c.$attr,k;switch(a.nodeType){case 1:T(b,na(Ka(a).toLowerCase()),"E",d,g);var l,r,n;k=a.attributes;for(var p=0,A=k&&k.length;p<A;p++){var x=!1,D=!1;l=k[p];if(!S||8<=S||l.specified){r=l.name;n=na(r);X.test(n)&&(r=ib(n.substr(6),"-"));var N=n.replace(/(Start|End)$/,"");n===N+"Start"&&(x=r,D=r.substr(0,r.length-5)+"end",r=r.substr(0,r.length-6));n=na(r.toLowerCase());m[n]=r;c[n]=l=ba(l.value);qc(a,n)&&(c[n]=!0);M(a,b,l,n);T(b,n,"A",d,g,x,D)}}a=
a.className;if(C(a)&&""!==a)for(;k=f.exec(a);)n=na(k[2]),T(b,n,"C",d,g)&&(c[n]=ba(k[3])),a=a.substr(k.index+k[0].length);break;case 3:u(b,a.nodeValue);break;case 8:try{if(k=e.exec(a.nodeValue))n=na(k[1]),T(b,n,"M",d,g)&&(c[n]=ba(k[2]))}catch(t){}}b.sort(H);return b}function F(a,b,c){var d=[],e=0;if(b&&a.hasAttribute&&a.hasAttribute(b)){do{if(!a)throw ia("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 y(d)}function R(a,
b,c){return function(d,e,f,g,k){e=F(e[0],b,c);return a(d,e,f,g,k)}}function ea(a,c,d,e,f,g,m,n,p){function x(a,b,c,d){if(a){c&&(a=R(a,c,d));a.require=G.require;a.directiveName=u;if(O===G||G.$$isolateScope)a=uc(a,{isolateScope:!0});m.push(a)}if(b){c&&(b=R(b,c,d));b.require=G.require;b.directiveName=u;if(O===G||G.$$isolateScope)b=uc(b,{isolateScope:!0});n.push(b)}}function D(a,b,c,d){var e,f="data",g=!1;if(C(b)){for(;"^"==(e=b.charAt(0))||"?"==e;)b=b.substr(1),"^"==e&&(f="inheritedData"),g=g||"?"==
e;e=null;d&&"data"===f&&(e=d[b]);e=e||c[f]("$"+b+"Controller");if(!e&&!g)throw ia("ctreq",b,a);}else K(b)&&(e=[],q(b,function(b){e.push(D(a,b,c,d))}));return e}function N(a,e,f,g,p){function x(a,b){var c;2>arguments.length&&(b=a,a=s);E&&(c=ca);return p(a,b,c)}var t,I,v,B,R,F,ca={},ob;t=c===f?d:ka(d,new Lb(y(f),d.$attr));I=t.$$element;if(O){var T=/^\s*([@=&])(\??)\s*(\w*)\s*$/;g=y(f);F=e.$new(!0);!ea||ea!==O&&ea!==O.$$originalDirective?g.data("$isolateScopeNoTemplate",F):g.data("$isolateScope",F);
ma(g,"ng-isolate-scope");q(O.scope,function(a,c){var d=a.match(T)||[],f=d[3]||c,g="?"==d[2],d=d[1],m,l,n,p;F.$$isolateBindings[c]=d+f;switch(d){case "@":t.$observe(f,function(a){F[c]=a});t.$$observers[f].$$scope=e;t[f]&&(F[c]=b(t[f])(e));break;case "=":if(g&&!t[f])break;l=r(t[f]);p=l.literal?xa:function(a,b){return a===b};n=l.assign||function(){m=F[c]=l(e);throw ia("nonassign",t[f],O.name);};m=F[c]=l(e);F.$watch(function(){var a=l(e);p(a,F[c])||(p(a,m)?n(e,a=F[c]):F[c]=a);return m=a},null,l.literal);
break;case "&":l=r(t[f]);F[c]=function(a){return l(e,a)};break;default:throw ia("iscp",O.name,c,a);}})}ob=p&&x;L&&q(L,function(a){var b={$scope:a===O||a.$$isolateScope?F:e,$element:I,$attrs:t,$transclude:ob},c;R=a.controller;"@"==R&&(R=t[a.name]);c=A(R,b);ca[a.name]=c;E||I.data("$"+a.name+"Controller",c);a.controllerAs&&(b.$scope[a.controllerAs]=c)});g=0;for(v=m.length;g<v;g++)try{B=m[g],B(B.isolateScope?F:e,I,t,B.require&&D(B.directiveName,B.require,I,ca),ob)}catch(G){l(G,ha(I))}g=e;O&&(O.template||
null===O.templateUrl)&&(g=F);a&&a(g,f.childNodes,s,p);for(g=n.length-1;0<=g;g--)try{B=n[g],B(B.isolateScope?F:e,I,t,B.require&&D(B.directiveName,B.require,I,ca),ob)}catch(z){l(z,ha(I))}}p=p||{};for(var t=-Number.MAX_VALUE,B,L=p.controllerDirectives,O=p.newIsolateScopeDirective,ea=p.templateDirective,T=p.nonTlbTranscludeDirective,H=!1,E=p.hasElementTranscludeDirective,Z=d.$$element=y(c),G,u,W,Za=e,P,M=0,S=a.length;M<S;M++){G=a[M];var ra=G.$$start,X=G.$$end;ra&&(Z=F(c,ra,X));W=s;if(t>G.priority)break;
if(W=G.scope)B=B||G,G.templateUrl||(J("new/isolated scope",O,G,Z),U(W)&&(O=G));u=G.name;!G.templateUrl&&G.controller&&(W=G.controller,L=L||{},J("'"+u+"' controller",L[u],G,Z),L[u]=G);if(W=G.transclude)H=!0,G.$$tlb||(J("transclusion",T,G,Z),T=G),"element"==W?(E=!0,t=G.priority,W=F(c,ra,X),Z=d.$$element=y(V.createComment(" "+u+": "+d[u]+" ")),c=Z[0],pb(f,y(ya.call(W,0)),c),Za=v(W,e,t,g&&g.name,{nonTlbTranscludeDirective:T})):(W=y(Jb(c)).contents(),Z.empty(),Za=v(W,e));if(G.template)if(J("template",
ea,G,Z),ea=G,W=Q(G.template)?G.template(Z,d):G.template,W=Y(W),G.replace){g=G;W=Hb.test(W)?y(ba(W)):[];c=W[0];if(1!=W.length||1!==c.nodeType)throw ia("tplrt",u,"");pb(f,Z,c);S={$attr:{}};W=ca(c,[],S);var $=a.splice(M+1,a.length-(M+1));O&&tc(W);a=a.concat(W).concat($);z(d,S);S=a.length}else Z.html(W);if(G.templateUrl)J("template",ea,G,Z),ea=G,G.replace&&(g=G),N=w(a.splice(M,a.length-M),Z,d,f,Za,m,n,{controllerDirectives:L,newIsolateScopeDirective:O,templateDirective:ea,nonTlbTranscludeDirective:T}),
S=a.length;else if(G.compile)try{P=G.compile(Z,d,Za),Q(P)?x(null,P,ra,X):P&&x(P.pre,P.post,ra,X)}catch(aa){l(aa,ha(Z))}G.terminal&&(N.terminal=!0,t=Math.max(t,G.priority))}N.scope=B&&!0===B.scope;N.transclude=H&&Za;p.hasElementTranscludeDirective=E;return N}function tc(a){for(var b=0,c=a.length;b<c;b++)a[b]=Zb(a[b],{$$isolateScope:!0})}function T(b,e,f,g,k,r,n){if(e===k)return null;k=null;if(c.hasOwnProperty(e)){var p;e=a.get(e+d);for(var A=0,x=e.length;A<x;A++)try{p=e[A],(g===s||g>p.priority)&&-1!=
p.restrict.indexOf(f)&&(r&&(p=Zb(p,{$$start:r,$$end:n})),b.push(p),k=p)}catch(D){l(D)}}return k}function z(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;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,f){"class"==f?(ma(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):"style"==f?(e.attr("style",e.attr("style")+";"+b),a.style=(a.style?a.style+";":"")+b):"$"==f.charAt(0)||a.hasOwnProperty(f)||(a[f]=b,d[f]=c[f])})}function w(a,b,
c,d,e,f,g,k){var m=[],l,r,A=b[0],x=a.shift(),D=E({},x,{templateUrl:null,transclude:null,replace:null,$$originalDirective:x}),N=Q(x.templateUrl)?x.templateUrl(b,c):x.templateUrl;b.empty();n.get(t.getTrustedResourceUrl(N),{cache:p}).success(function(n){var p,t;n=Y(n);if(x.replace){n=Hb.test(n)?y(ba(n)):[];p=n[0];if(1!=n.length||1!==p.nodeType)throw ia("tplrt",x.name,N);n={$attr:{}};pb(d,b,p);var v=ca(p,[],n);U(x.scope)&&tc(v);a=v.concat(a);z(c,n)}else p=A,b.html(n);a.unshift(D);l=ea(a,p,c,e,b,x,f,g,
k);q(d,function(a,c){a==p&&(d[c]=b[0])});for(r=L(b[0].childNodes,e);m.length;){n=m.shift();t=m.shift();var B=m.shift(),R=m.shift(),v=b[0];if(t!==A){var F=t.className;k.hasElementTranscludeDirective&&x.replace||(v=Jb(p));pb(B,y(t),v);ma(y(v),F)}t=l.transclude?O(n,l.transclude):R;l(r,n,v,d,t)}m=null}).error(function(a,b,c,d){throw ia("tpload",d.url);});return function(a,b,c,d,e){m?(m.push(b),m.push(c),m.push(d),m.push(e)):l(r,b,c,d,e)}}function H(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 J(a,b,c,d){if(b)throw ia("multidir",b.name,c.name,a,ha(d));}function u(a,c){var d=b(c,!0);d&&a.push({priority:0,compile:aa(function(a,b){var c=b.parent(),e=c.data("$binding")||[];e.push(d);ma(c.data("$binding",e),"ng-binding");a.$watch(d,function(a){b[0].nodeValue=a})})})}function P(a,b){if("srcdoc"==b)return t.HTML;var c=Ka(a);if("xlinkHref"==b||"FORM"==c&&"action"==b||"IMG"!=c&&("src"==b||"ngSrc"==b))return t.RESOURCE_URL}function M(a,c,d,e){var f=
b(d,!0);if(f){if("multiple"===e&&"SELECT"===Ka(a))throw ia("selmulti",ha(a));c.push({priority:100,compile:function(){return{pre:function(c,d,m){d=m.$$observers||(m.$$observers={});if(g.test(e))throw ia("nodomevents");if(f=b(m[e],!0,P(a,e)))m[e]=f(c),(d[e]||(d[e]=[])).$$inter=!0,(m.$$observers&&m.$$observers[e].$$scope||c).$watch(f,function(a,b){"class"===e&&a!=b?m.$updateClass(a,b):m.$set(e,a)})}}}})}}function pb(a,b,c){var d=b[0],e=b.length,f=d.parentNode,g,m;if(a)for(g=0,m=a.length;g<m;g++)if(a[g]==
d){a[g++]=c;m=g+e-1;for(var k=a.length;g<k;g++,m++)m<k?a[g]=a[m]:delete a[g];a.length-=e-1;break}f&&f.replaceChild(c,d);a=V.createDocumentFragment();a.appendChild(d);c[y.expando]=d[y.expando];d=1;for(e=b.length;d<e;d++)f=b[d],y(f).remove(),a.appendChild(f),delete b[d];b[0]=c;b.length=1}function uc(a,b){return E(function(){return a.apply(null,arguments)},a,b)}var Lb=function(a,b){this.$$element=a;this.$attr=b||{}};Lb.prototype={$normalize:na,$addClass:function(a){a&&0<a.length&&N.addClass(this.$$element,
a)},$removeClass:function(a){a&&0<a.length&&N.removeClass(this.$$element,a)},$updateClass:function(a,b){var c=vc(a,b),d=vc(b,a);0===c.length?N.removeClass(this.$$element,d):0===d.length?N.addClass(this.$$element,c):N.setClass(this.$$element,c,d)},$set:function(a,b,c,d){var e=qc(this.$$element[0],a);e&&(this.$$element.prop(a,b),d=e);this[a]=b;d?this.$attr[a]=d:(d=this.$attr[a])||(this.$attr[a]=d=ib(a,"-"));e=Ka(this.$$element);if("A"===e&&"href"===a||"IMG"===e&&"src"===a)this[a]=b=B(b,"src"===a);!1!==
c&&(null===b||b===s?this.$$element.removeAttr(d):this.$$element.attr(d,b));(c=this.$$observers)&&q(c[a],function(a){try{a(b)}catch(c){l(c)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers={}),e=d[a]||(d[a]=[]);e.push(b);D.$evalAsync(function(){e.$$inter||b(c[a])});return b}};var Z=b.startSymbol(),ra=b.endSymbol(),Y="{{"==Z||"}}"==ra?Ea:function(a){return a.replace(/\{\{/g,Z).replace(/}}/g,ra)},X=/^ngAttr[A-Z]/;return v}]}function na(b){return Ua(b.replace(te,""))}function vc(b,
a){var c="",d=b.split(/\s+/),e=a.split(/\s+/),f=0;a:for(;f<d.length;f++){for(var g=d[f],h=0;h<e.length;h++)if(g==e[h])continue a;c+=(0<c.length?" ":"")+g}return c}function Od(){var b={},a=/^(\S+)(\s+as\s+(\w+))?$/;this.register=function(a,d){Aa(a,"controller");U(a)?E(b,a):b[a]=d};this.$get=["$injector","$window",function(c,d){return function(e,f){var g,h,m;C(e)&&(g=e.match(a),h=g[1],m=g[3],e=b.hasOwnProperty(h)?b[h]:fc(f.$scope,h,!0)||fc(d,h,!0),Sa(e,h,!0));g=c.instantiate(e,f);if(m){if(!f||"object"!=
typeof f.$scope)throw u("$controller")("noscp",h||e.name,m);f.$scope[m]=g}return g}}]}function Pd(){this.$get=["$window",function(b){return y(b.document)}]}function Qd(){this.$get=["$log",function(b){return function(a,c){b.error.apply(b,arguments)}}]}function wc(b){var a={},c,d,e;if(!b)return a;q(b.split("\n"),function(b){e=b.indexOf(":");c=J(ba(b.substr(0,e)));d=ba(b.substr(e+1));c&&(a[c]=a[c]?a[c]+(", "+d):d)});return a}function xc(b){var a=U(b)?b:s;return function(c){a||(a=wc(b));return c?a[J(c)]||
null:a}}function yc(b,a,c){if(Q(c))return c(b,a);q(c,function(c){b=c(b,a)});return b}function Td(){var b=/^\s*(\[|\{[^\{])/,a=/[\}\]]\s*$/,c=/^\)\]\}',?\n/,d={"Content-Type":"application/json;charset=utf-8"},e=this.defaults={transformResponse:[function(d){C(d)&&(d=d.replace(c,""),b.test(d)&&a.test(d)&&(d=ac(d)));return d}],transformRequest:[function(a){return U(a)&&"[object File]"!==wa.call(a)&&"[object Blob]"!==wa.call(a)?qa(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},post:ka(d),
put:ka(d),patch:ka(d)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN"},f=this.interceptors=[],g=this.responseInterceptors=[];this.$get=["$httpBackend","$browser","$cacheFactory","$rootScope","$q","$injector",function(a,b,c,d,n,p){function r(a){function c(a){var b=E({},a,{data:yc(a.data,a.headers,d.transformResponse)});return 200<=a.status&&300>a.status?b:n.reject(b)}var d={method:"get",transformRequest:e.transformRequest,transformResponse:e.transformResponse},f=function(a){function b(a){var c;
q(a,function(b,d){Q(b)&&(c=b(),null!=c?a[d]=c:delete a[d])})}var c=e.headers,d=E({},a.headers),f,g,c=E({},c.common,c[J(a.method)]);b(c);b(d);a:for(f in c){a=J(f);for(g in d)if(J(g)===a)continue a;d[f]=c[f]}return d}(a);E(d,a);d.headers=f;d.method=Ga(d.method);(a=Mb(d.url)?b.cookies()[d.xsrfCookieName||e.xsrfCookieName]:s)&&(f[d.xsrfHeaderName||e.xsrfHeaderName]=a);var g=[function(a){f=a.headers;var b=yc(a.data,xc(f),a.transformRequest);H(a.data)&&q(f,function(a,b){"content-type"===J(b)&&delete f[b]});
H(a.withCredentials)&&!H(e.withCredentials)&&(a.withCredentials=e.withCredentials);return A(a,b,f).then(c,c)},s],h=n.when(d);for(q(t,function(a){(a.request||a.requestError)&&g.unshift(a.request,a.requestError);(a.response||a.responseError)&&g.push(a.response,a.responseError)});g.length;){a=g.shift();var k=g.shift(),h=h.then(a,k)}h.success=function(a){h.then(function(b){a(b.data,b.status,b.headers,d)});return h};h.error=function(a){h.then(null,function(b){a(b.data,b.status,b.headers,d)});return h};
return h}function A(b,c,f){function g(a,b,c,e){t&&(200<=a&&300>a?t.put(s,[a,b,wc(c),e]):t.remove(s));m(b,a,c,e);d.$$phase||d.$apply()}function m(a,c,d,e){c=Math.max(c,0);(200<=c&&300>c?p.resolve:p.reject)({data:a,status:c,headers:xc(d),config:b,statusText:e})}function k(){var a=Na(r.pendingRequests,b);-1!==a&&r.pendingRequests.splice(a,1)}var p=n.defer(),A=p.promise,t,q,s=D(b.url,b.params);r.pendingRequests.push(b);A.then(k,k);(b.cache||e.cache)&&(!1!==b.cache&&"GET"==b.method)&&(t=U(b.cache)?b.cache:
U(e.cache)?e.cache:x);if(t)if(q=t.get(s),z(q)){if(q.then)return q.then(k,k),q;K(q)?m(q[1],q[0],ka(q[2]),q[3]):m(q,200,{},"OK")}else t.put(s,A);H(q)&&a(b.method,s,c,g,f,b.timeout,b.withCredentials,b.responseType);return A}function D(a,b){if(!b)return a;var c=[];Sc(b,function(a,b){null===a||H(a)||(K(a)||(a=[a]),q(a,function(a){U(a)&&(a=qa(a));c.push(za(b)+"="+za(a))}))});0<c.length&&(a+=(-1==a.indexOf("?")?"?":"&")+c.join("&"));return a}var x=c("$http"),t=[];q(f,function(a){t.unshift(C(a)?p.get(a):
p.invoke(a))});q(g,function(a,b){var c=C(a)?p.get(a):p.invoke(a);t.splice(b,0,{response:function(a){return c(n.when(a))},responseError:function(a){return c(n.reject(a))}})});r.pendingRequests=[];(function(a){q(arguments,function(a){r[a]=function(b,c){return r(E(c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){q(arguments,function(a){r[a]=function(b,c,d){return r(E(d||{},{method:a,url:b,data:c}))}})})("post","put");r.defaults=e;return r}]}function ue(b){if(8>=S&&(!b.match(/^(get|post|head|put|delete|options)$/i)||
!P.XMLHttpRequest))return new P.ActiveXObject("Microsoft.XMLHTTP");if(P.XMLHttpRequest)return new P.XMLHttpRequest;throw u("$httpBackend")("noxhr");}function Ud(){this.$get=["$browser","$window","$document",function(b,a,c){return ve(b,ue,b.defer,a.angular.callbacks,c[0])}]}function ve(b,a,c,d,e){function f(a,b,c){var f=e.createElement("script"),g=null;f.type="text/javascript";f.src=a;f.async=!0;g=function(a){Va(f,"load",g);Va(f,"error",g);e.body.removeChild(f);f=null;var h=-1,A="unknown";a&&("load"!==
a.type||d[b].called||(a={type:"error"}),A=a.type,h="error"===a.type?404:200);c&&c(h,A)};qb(f,"load",g);qb(f,"error",g);8>=S&&(f.onreadystatechange=function(){C(f.readyState)&&/loaded|complete/.test(f.readyState)&&(f.onreadystatechange=null,g({type:"load"}))});e.body.appendChild(f);return g}var g=-1;return function(e,m,k,l,n,p,r,A){function D(){t=g;B&&B();v&&v.abort()}function x(a,d,e,f,g){L&&c.cancel(L);B=v=null;0===d&&(d=e?200:"file"==sa(m).protocol?404:0);a(1223===d?204:d,e,f,g||"");b.$$completeOutstandingRequest(w)}
var t;b.$$incOutstandingRequestCount();m=m||b.url();if("jsonp"==J(e)){var N="_"+(d.counter++).toString(36);d[N]=function(a){d[N].data=a;d[N].called=!0};var B=f(m.replace("JSON_CALLBACK","angular.callbacks."+N),N,function(a,b){x(l,a,d[N].data,"",b);d[N]=w})}else{var v=a(e);v.open(e,m,!0);q(n,function(a,b){z(a)&&v.setRequestHeader(b,a)});v.onreadystatechange=function(){if(v&&4==v.readyState){var a=null,b=null;t!==g&&(a=v.getAllResponseHeaders(),b="response"in v?v.response:v.responseText);x(l,t||v.status,
b,a,v.statusText||"")}};r&&(v.withCredentials=!0);if(A)try{v.responseType=A}catch(s){if("json"!==A)throw s;}v.send(k||null)}if(0<p)var L=c(D,p);else p&&p.then&&p.then(D)}}function Rd(){var b="{{",a="}}";this.startSymbol=function(a){return a?(b=a,this):b};this.endSymbol=function(b){return b?(a=b,this):a};this.$get=["$parse","$exceptionHandler","$sce",function(c,d,e){function f(f,k,l){for(var n,p,r=0,A=[],D=f.length,x=!1,t=[];r<D;)-1!=(n=f.indexOf(b,r))&&-1!=(p=f.indexOf(a,n+g))?(r!=n&&A.push(f.substring(r,
n)),A.push(r=c(x=f.substring(n+g,p))),r.exp=x,r=p+h,x=!0):(r!=D&&A.push(f.substring(r)),r=D);(D=A.length)||(A.push(""),D=1);if(l&&1<A.length)throw zc("noconcat",f);if(!k||x)return t.length=D,r=function(a){try{for(var b=0,c=D,g;b<c;b++){if("function"==typeof(g=A[b]))if(g=g(a),g=l?e.getTrusted(l,g):e.valueOf(g),null==g)g="";else switch(typeof g){case "string":break;case "number":g=""+g;break;default:g=qa(g)}t[b]=g}return t.join("")}catch(h){a=zc("interr",f,h.toString()),d(a)}},r.exp=f,r.parts=A,r}var g=
b.length,h=a.length;f.startSymbol=function(){return b};f.endSymbol=function(){return a};return f}]}function Sd(){this.$get=["$rootScope","$window","$q",function(b,a,c){function d(d,g,h,m){var k=a.setInterval,l=a.clearInterval,n=c.defer(),p=n.promise,r=0,A=z(m)&&!m;h=z(h)?h:0;p.then(null,null,d);p.$$intervalId=k(function(){n.notify(r++);0<h&&r>=h&&(n.resolve(r),l(p.$$intervalId),delete e[p.$$intervalId]);A||b.$apply()},g);e[p.$$intervalId]=n;return p}var e={};d.cancel=function(a){return a&&a.$$intervalId in
e?(e[a.$$intervalId].reject("canceled"),clearInterval(a.$$intervalId),delete e[a.$$intervalId],!0):!1};return d}]}function ad(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0,maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",posSuf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},DATETIME_FORMATS:{MONTH:"January February March April May June July August September October November December".split(" "),
SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss a",shortTime:"h:mm a"},pluralCat:function(b){return 1===b?"one":"other"}}}}function Nb(b){b=b.split("/");for(var a=b.length;a--;)b[a]=
hb(b[a]);return b.join("/")}function Ac(b,a,c){b=sa(b,c);a.$$protocol=b.protocol;a.$$host=b.hostname;a.$$port=Y(b.port)||we[b.protocol]||null}function Bc(b,a,c){var d="/"!==b.charAt(0);d&&(b="/"+b);b=sa(b,c);a.$$path=decodeURIComponent(d&&"/"===b.pathname.charAt(0)?b.pathname.substring(1):b.pathname);a.$$search=cc(b.search);a.$$hash=decodeURIComponent(b.hash);a.$$path&&"/"!=a.$$path.charAt(0)&&(a.$$path="/"+a.$$path)}function oa(b,a){if(0===a.indexOf(b))return a.substr(b.length)}function $a(b){var a=
b.indexOf("#");return-1==a?b:b.substr(0,a)}function Ob(b){return b.substr(0,$a(b).lastIndexOf("/")+1)}function Cc(b,a){this.$$html5=!0;a=a||"";var c=Ob(b);Ac(b,this,b);this.$$parse=function(a){var e=oa(c,a);if(!C(e))throw Pb("ipthprfx",a,c);Bc(e,this,b);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=Bb(this.$$search),b=this.$$hash?"#"+hb(this.$$hash):"";this.$$url=Nb(this.$$path)+(a?"?"+a:"")+b;this.$$absUrl=c+this.$$url.substr(1)};this.$$rewrite=function(d){var e;
if((e=oa(b,d))!==s)return d=e,(e=oa(a,e))!==s?c+(oa("/",e)||e):b+d;if((e=oa(c,d))!==s)return c+e;if(c==d+"/")return c}}function Qb(b,a){var c=Ob(b);Ac(b,this,b);this.$$parse=function(d){var e=oa(b,d)||oa(c,d),e="#"==e.charAt(0)?oa(a,e):this.$$html5?e:"";if(!C(e))throw Pb("ihshprfx",d,a);Bc(e,this,b);d=this.$$path;var f=/^\/[A-Z]:(\/.*)/;0===e.indexOf(b)&&(e=e.replace(b,""));f.exec(e)||(d=(e=f.exec(d))?e[1]:d);this.$$path=d;this.$$compose()};this.$$compose=function(){var c=Bb(this.$$search),e=this.$$hash?
"#"+hb(this.$$hash):"";this.$$url=Nb(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=b+(this.$$url?a+this.$$url:"")};this.$$rewrite=function(a){if($a(b)==$a(a))return a}}function Rb(b,a){this.$$html5=!0;Qb.apply(this,arguments);var c=Ob(b);this.$$rewrite=function(d){var e;if(b==$a(d))return d;if(e=oa(c,d))return b+a+e;if(c===d+"/")return c};this.$$compose=function(){var c=Bb(this.$$search),e=this.$$hash?"#"+hb(this.$$hash):"";this.$$url=Nb(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=b+a+this.$$url}}function rb(b){return function(){return this[b]}}
function Dc(b,a){return function(c){if(H(c))return this[b];this[b]=a(c);this.$$compose();return this}}function Vd(){var b="",a=!1;this.hashPrefix=function(a){return z(a)?(b=a,this):b};this.html5Mode=function(b){return z(b)?(a=b,this):a};this.$get=["$rootScope","$browser","$sniffer","$rootElement",function(c,d,e,f){function g(a){c.$broadcast("$locationChangeSuccess",h.absUrl(),a)}var h,m,k=d.baseHref(),l=d.url(),n;a?(n=l.substring(0,l.indexOf("/",l.indexOf("//")+2))+(k||"/"),m=e.history?Cc:Rb):(n=
$a(l),m=Qb);h=new m(n,"#"+b);h.$$parse(h.$$rewrite(l));f.on("click",function(a){if(!a.ctrlKey&&!a.metaKey&&2!=a.which){for(var e=y(a.target);"a"!==J(e[0].nodeName);)if(e[0]===f[0]||!(e=e.parent())[0])return;var g=e.prop("href");U(g)&&"[object SVGAnimatedString]"===g.toString()&&(g=sa(g.animVal).href);if(m===Rb){var k=e.attr("href")||e.attr("xlink:href");if(0>k.indexOf("://"))if(g="#"+b,"/"==k[0])g=n+g+k;else if("#"==k[0])g=n+g+(h.path()||"/")+k;else{for(var l=h.path().split("/"),k=k.split("/"),p=
0;p<k.length;p++)"."!=k[p]&&(".."==k[p]?l.pop():k[p].length&&l.push(k[p]));g=n+g+l.join("/")}}l=h.$$rewrite(g);g&&(!e.attr("target")&&l&&!a.isDefaultPrevented())&&(a.preventDefault(),l!=d.url()&&(h.$$parse(l),c.$apply(),P.angular["ff-684208-preventDefault"]=!0))}});h.absUrl()!=l&&d.url(h.absUrl(),!0);d.onUrlChange(function(a){h.absUrl()!=a&&(c.$evalAsync(function(){var b=h.absUrl();h.$$parse(a);c.$broadcast("$locationChangeStart",a,b).defaultPrevented?(h.$$parse(b),d.url(b)):g(b)}),c.$$phase||c.$digest())});
var p=0;c.$watch(function(){var a=d.url(),b=h.$$replace;p&&a==h.absUrl()||(p++,c.$evalAsync(function(){c.$broadcast("$locationChangeStart",h.absUrl(),a).defaultPrevented?h.$$parse(a):(d.url(h.absUrl(),b),g(a))}));h.$$replace=!1;return p});return h}]}function Wd(){var b=!0,a=this;this.debugEnabled=function(a){return z(a)?(b=a,this):b};this.$get=["$window",function(c){function d(a){a instanceof Error&&(a.stack?a=a.message&&-1===a.stack.indexOf(a.message)?"Error: "+a.message+"\n"+a.stack:a.stack:a.sourceURL&&
(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function e(a){var b=c.console||{},e=b[a]||b.log||w;a=!1;try{a=!!e.apply}catch(m){}return a?function(){var a=[];q(arguments,function(b){a.push(d(b))});return e.apply(b,a)}:function(a,b){e(a,null==b?"":b)}}return{log:e("log"),info:e("info"),warn:e("warn"),error:e("error"),debug:function(){var c=e("debug");return function(){b&&c.apply(a,arguments)}}()}}]}function fa(b,a){if("constructor"===b)throw Ca("isecfld",a);return b}function ab(b,a){if(b){if(b.constructor===
b)throw Ca("isecfn",a);if(b.document&&b.location&&b.alert&&b.setInterval)throw Ca("isecwindow",a);if(b.children&&(b.nodeName||b.prop&&b.attr&&b.find))throw Ca("isecdom",a);}return b}function sb(b,a,c,d,e){e=e||{};a=a.split(".");for(var f,g=0;1<a.length;g++){f=fa(a.shift(),d);var h=b[f];h||(h={},b[f]=h);b=h;b.then&&e.unwrapPromises&&(ta(d),"$$v"in b||function(a){a.then(function(b){a.$$v=b})}(b),b.$$v===s&&(b.$$v={}),b=b.$$v)}f=fa(a.shift(),d);return b[f]=c}function Ec(b,a,c,d,e,f,g){fa(b,f);fa(a,f);
fa(c,f);fa(d,f);fa(e,f);return g.unwrapPromises?function(g,m){var k=m&&m.hasOwnProperty(b)?m:g,l;if(null==k)return k;(k=k[b])&&k.then&&(ta(f),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);if(!a)return k;if(null==k)return s;(k=k[a])&&k.then&&(ta(f),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);if(!c)return k;if(null==k)return s;(k=k[c])&&k.then&&(ta(f),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);if(!d)return k;if(null==k)return s;(k=k[d])&&k.then&&
(ta(f),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);if(!e)return k;if(null==k)return s;(k=k[e])&&k.then&&(ta(f),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);return k}:function(f,g){var k=g&&g.hasOwnProperty(b)?g:f;if(null==k)return k;k=k[b];if(!a)return k;if(null==k)return s;k=k[a];if(!c)return k;if(null==k)return s;k=k[c];if(!d)return k;if(null==k)return s;k=k[d];return e?null==k?s:k=k[e]:k}}function xe(b,a){fa(b,a);return function(a,d){return null==a?s:(d&&d.hasOwnProperty(b)?
d:a)[b]}}function ye(b,a,c){fa(b,c);fa(a,c);return function(c,e){if(null==c)return s;c=(e&&e.hasOwnProperty(b)?e:c)[b];return null==c?s:c[a]}}function Fc(b,a,c){if(Sb.hasOwnProperty(b))return Sb[b];var d=b.split("."),e=d.length,f;if(a.unwrapPromises||1!==e)if(a.unwrapPromises||2!==e)if(a.csp)f=6>e?Ec(d[0],d[1],d[2],d[3],d[4],c,a):function(b,f){var g=0,h;do h=Ec(d[g++],d[g++],d[g++],d[g++],d[g++],c,a)(b,f),f=s,b=h;while(g<e);return h};else{var g="var p;\n";q(d,function(b,d){fa(b,c);g+="if(s == null) return undefined;\ns="+
(d?"s":'((k&&k.hasOwnProperty("'+b+'"))?k:s)')+'["'+b+'"];\n'+(a.unwrapPromises?'if (s && s.then) {\n pw("'+c.replace(/(["\r\n])/g,"\\$1")+'");\n if (!("$$v" in s)) {\n p=s;\n p.$$v = undefined;\n p.then(function(v) {p.$$v=v;});\n}\n s=s.$$v\n}\n':"")});var g=g+"return s;",h=new Function("s","k","pw",g);h.toString=aa(g);f=a.unwrapPromises?function(a,b){return h(a,b,ta)}:h}else f=ye(d[0],d[1],c);else f=xe(d[0],c);"hasOwnProperty"!==b&&(Sb[b]=f);return f}function Xd(){var b={},a={csp:!1,unwrapPromises:!1,
logPromiseWarnings:!0};this.unwrapPromises=function(b){return z(b)?(a.unwrapPromises=!!b,this):a.unwrapPromises};this.logPromiseWarnings=function(b){return z(b)?(a.logPromiseWarnings=b,this):a.logPromiseWarnings};this.$get=["$filter","$sniffer","$log",function(c,d,e){a.csp=d.csp;ta=function(b){a.logPromiseWarnings&&!Gc.hasOwnProperty(b)&&(Gc[b]=!0,e.warn("[$parse] Promise found in the expression `"+b+"`. Automatic unwrapping of promises in Angular expressions is deprecated."))};return function(d){var e;
switch(typeof d){case "string":if(b.hasOwnProperty(d))return b[d];e=new Tb(a);e=(new bb(e,c,a)).parse(d);"hasOwnProperty"!==d&&(b[d]=e);return e;case "function":return d;default:return w}}}]}function Zd(){this.$get=["$rootScope","$exceptionHandler",function(b,a){return ze(function(a){b.$evalAsync(a)},a)}]}function ze(b,a){function c(a){return a}function d(a){return g(a)}var e=function(){var g=[],k,l;return l={resolve:function(a){if(g){var c=g;g=s;k=f(a);c.length&&b(function(){for(var a,b=0,d=c.length;b<
d;b++)a=c[b],k.then(a[0],a[1],a[2])})}},reject:function(a){l.resolve(h(a))},notify:function(a){if(g){var c=g;g.length&&b(function(){for(var b,d=0,e=c.length;d<e;d++)b=c[d],b[2](a)})}},promise:{then:function(b,f,h){var l=e(),D=function(d){try{l.resolve((Q(b)?b:c)(d))}catch(e){l.reject(e),a(e)}},x=function(b){try{l.resolve((Q(f)?f:d)(b))}catch(c){l.reject(c),a(c)}},t=function(b){try{l.notify((Q(h)?h:c)(b))}catch(d){a(d)}};g?g.push([D,x,t]):k.then(D,x,t);return l.promise},"catch":function(a){return this.then(null,
a)},"finally":function(a){function b(a,c){var d=e();c?d.resolve(a):d.reject(a);return d.promise}function d(e,f){var g=null;try{g=(a||c)()}catch(h){return b(h,!1)}return g&&Q(g.then)?g.then(function(){return b(e,f)},function(a){return b(a,!1)}):b(e,f)}return this.then(function(a){return d(a,!0)},function(a){return d(a,!1)})}}}},f=function(a){return a&&Q(a.then)?a:{then:function(c){var d=e();b(function(){d.resolve(c(a))});return d.promise}}},g=function(a){var b=e();b.reject(a);return b.promise},h=function(c){return{then:function(f,
g){var h=e();b(function(){try{h.resolve((Q(g)?g:d)(c))}catch(b){h.reject(b),a(b)}});return h.promise}}};return{defer:e,reject:g,when:function(h,k,l,n){var p=e(),r,A=function(b){try{return(Q(k)?k:c)(b)}catch(d){return a(d),g(d)}},D=function(b){try{return(Q(l)?l:d)(b)}catch(c){return a(c),g(c)}},x=function(b){try{return(Q(n)?n:c)(b)}catch(d){a(d)}};b(function(){f(h).then(function(a){r||(r=!0,p.resolve(f(a).then(A,D,x)))},function(a){r||(r=!0,p.resolve(D(a)))},function(a){r||p.notify(x(a))})});return p.promise},
all:function(a){var b=e(),c=0,d=K(a)?[]:{};q(a,function(a,e){c++;f(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}}}function fe(){this.$get=["$window","$timeout",function(b,a){var c=b.requestAnimationFrame||b.webkitRequestAnimationFrame||b.mozRequestAnimationFrame,d=b.cancelAnimationFrame||b.webkitCancelAnimationFrame||b.mozCancelAnimationFrame||b.webkitCancelRequestAnimationFrame,e=!!c,f=e?
function(a){var b=c(a);return function(){d(b)}}:function(b){var c=a(b,16.66,!1);return function(){a.cancel(c)}};f.supported=e;return f}]}function Yd(){var b=10,a=u("$rootScope"),c=null;this.digestTtl=function(a){arguments.length&&(b=a);return b};this.$get=["$injector","$exceptionHandler","$parse","$browser",function(d,e,f,g){function h(){this.$id=eb();this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null;this["this"]=this.$root=this;
this.$$destroyed=!1;this.$$asyncQueue=[];this.$$postDigestQueue=[];this.$$listeners={};this.$$listenerCount={};this.$$isolateBindings={}}function m(b){if(p.$$phase)throw a("inprog",p.$$phase);p.$$phase=b}function k(a,b){var c=f(a);Sa(c,b);return c}function l(a,b,c){do a.$$listenerCount[c]-=b,0===a.$$listenerCount[c]&&delete a.$$listenerCount[c];while(a=a.$parent)}function n(){}h.prototype={constructor:h,$new:function(a){a?(a=new h,a.$root=this.$root,a.$$asyncQueue=this.$$asyncQueue,a.$$postDigestQueue=
this.$$postDigestQueue):(this.$$childScopeClass||(this.$$childScopeClass=function(){this.$$watchers=this.$$nextSibling=this.$$childHead=this.$$childTail=null;this.$$listeners={};this.$$listenerCount={};this.$id=eb();this.$$childScopeClass=null},this.$$childScopeClass.prototype=this),a=new this.$$childScopeClass);a["this"]=a;a.$parent=this;a.$$prevSibling=this.$$childTail;this.$$childHead?this.$$childTail=this.$$childTail.$$nextSibling=a:this.$$childHead=this.$$childTail=a;return a},$watch:function(a,
b,d){var e=k(a,"watch"),f=this.$$watchers,g={fn:b,last:n,get:e,exp:a,eq:!!d};c=null;if(!Q(b)){var h=k(b||w,"listener");g.fn=function(a,b,c){h(c)}}if("string"==typeof a&&e.constant){var m=g.fn;g.fn=function(a,b,c){m.call(this,a,b,c);Oa(f,g)}}f||(f=this.$$watchers=[]);f.unshift(g);return function(){Oa(f,g);c=null}},$watchCollection:function(a,b){var c=this,d,e,g,h=1<b.length,k=0,m=f(a),l=[],n={},p=!0,q=0;return this.$watch(function(){d=m(c);var a,b;if(U(d))if(db(d))for(e!==l&&(e=l,q=e.length=0,k++),
a=d.length,q!==a&&(k++,e.length=q=a),b=0;b<a;b++)e[b]!==e[b]&&d[b]!==d[b]||e[b]===d[b]||(k++,e[b]=d[b]);else{e!==n&&(e=n={},q=0,k++);a=0;for(b in d)d.hasOwnProperty(b)&&(a++,e.hasOwnProperty(b)?e[b]!==d[b]&&(k++,e[b]=d[b]):(q++,e[b]=d[b],k++));if(q>a)for(b in k++,e)e.hasOwnProperty(b)&&!d.hasOwnProperty(b)&&(q--,delete e[b])}else e!==d&&(e=d,k++);return k},function(){p?(p=!1,b(d,d,c)):b(d,g,c);if(h)if(U(d))if(db(d)){g=Array(d.length);for(var a=0;a<d.length;a++)g[a]=d[a]}else for(a in g={},d)Ab.call(d,
a)&&(g[a]=d[a]);else g=d})},$digest:function(){var d,f,g,h,k=this.$$asyncQueue,l=this.$$postDigestQueue,q,v,s=b,L,O=[],y,F,R;m("$digest");c=null;do{v=!1;for(L=this;k.length;){try{R=k.shift(),R.scope.$eval(R.expression)}catch(z){p.$$phase=null,e(z)}c=null}a:do{if(h=L.$$watchers)for(q=h.length;q--;)try{if(d=h[q])if((f=d.get(L))!==(g=d.last)&&!(d.eq?xa(f,g):"number"==typeof f&&"number"==typeof g&&isNaN(f)&&isNaN(g)))v=!0,c=d,d.last=d.eq?Fa(f,null):f,d.fn(f,g===n?f:g,L),5>s&&(y=4-s,O[y]||(O[y]=[]),F=
Q(d.exp)?"fn: "+(d.exp.name||d.exp.toString()):d.exp,F+="; newVal: "+qa(f)+"; oldVal: "+qa(g),O[y].push(F));else if(d===c){v=!1;break a}}catch(C){p.$$phase=null,e(C)}if(!(h=L.$$childHead||L!==this&&L.$$nextSibling))for(;L!==this&&!(h=L.$$nextSibling);)L=L.$parent}while(L=h);if((v||k.length)&&!s--)throw p.$$phase=null,a("infdig",b,qa(O));}while(v||k.length);for(p.$$phase=null;l.length;)try{l.shift()()}catch(T){e(T)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");
this.$$destroyed=!0;this!==p&&(q(this.$$listenerCount,gb(null,l,this)),a.$$childHead==this&&(a.$$childHead=this.$$nextSibling),a.$$childTail==this&&(a.$$childTail=this.$$prevSibling),this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=this.$$nextSibling),this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling),this.$parent=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=this.$root=null,this.$$listeners={},this.$$watchers=this.$$asyncQueue=this.$$postDigestQueue=
[],this.$destroy=this.$digest=this.$apply=w,this.$on=this.$watch=function(){return w})}},$eval:function(a,b){return f(a)(this,b)},$evalAsync:function(a){p.$$phase||p.$$asyncQueue.length||g.defer(function(){p.$$asyncQueue.length&&p.$digest()});this.$$asyncQueue.push({scope:this,expression:a})},$$postDigest:function(a){this.$$postDigestQueue.push(a)},$apply:function(a){try{return m("$apply"),this.$eval(a)}catch(b){e(b)}finally{p.$$phase=null;try{p.$digest()}catch(c){throw e(c),c;}}},$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(){c[Na(c,b)]=null;l(e,1,a)}},$emit:function(a,b){var c=[],d,f=this,g=!1,h={name:a,targetScope:f,stopPropagation:function(){g=!0},preventDefault:function(){h.defaultPrevented=!0},defaultPrevented:!1},k=[h].concat(ya.call(arguments,1)),m,l;do{d=f.$$listeners[a]||c;h.currentScope=f;m=0;for(l=d.length;m<l;m++)if(d[m])try{d[m].apply(null,
k)}catch(n){e(n)}else d.splice(m,1),m--,l--;if(g)break;f=f.$parent}while(f);return h},$broadcast:function(a,b){for(var c=this,d=this,f={name:a,targetScope:this,preventDefault:function(){f.defaultPrevented=!0},defaultPrevented:!1},g=[f].concat(ya.call(arguments,1)),h,k;c=d;){f.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(m){e(m)}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}return f}};var p=new h;return p}]}function bd(){var b=/^\s*(https?|ftp|mailto|tel|file):/,a=/^\s*(https?|ftp|file):|data:image\//;this.aHrefSanitizationWhitelist=function(a){return z(a)?(b=a,this):b};this.imgSrcSanitizationWhitelist=function(b){return z(b)?(a=b,this):a};this.$get=function(){return function(c,d){var e=d?a:b,f;if(!S||8<=S)if(f=sa(c).href,""!==f&&!f.match(e))return"unsafe:"+f;return c}}}function Ae(b){if("self"===b)return b;if(C(b)){if(-1<b.indexOf("***"))throw ua("iwcard",
b);b=b.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08").replace("\\*\\*",".*").replace("\\*","[^:/.?&;]*");return RegExp("^"+b+"$")}if(fb(b))return RegExp("^"+b.source+"$");throw ua("imatcher");}function Hc(b){var a=[];z(b)&&q(b,function(b){a.push(Ae(b))});return a}function ae(){this.SCE_CONTEXTS=ga;var b=["self"],a=[];this.resourceUrlWhitelist=function(a){arguments.length&&(b=Hc(a));return b};this.resourceUrlBlacklist=function(b){arguments.length&&(a=Hc(b));return a};this.$get=
["$injector",function(c){function d(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 e=function(a){throw ua("unsafe");};c.has("$sanitize")&&(e=c.get("$sanitize"));var f=d(),g={};g[ga.HTML]=d(f);g[ga.CSS]=d(f);g[ga.URL]=d(f);g[ga.JS]=d(f);g[ga.RESOURCE_URL]=d(g[ga.URL]);return{trustAs:function(a,b){var c=
g.hasOwnProperty(a)?g[a]:null;if(!c)throw ua("icontext",a,b);if(null===b||b===s||""===b)return b;if("string"!==typeof b)throw ua("itype",a);return new c(b)},getTrusted:function(c,d){if(null===d||d===s||""===d)return d;var f=g.hasOwnProperty(c)?g[c]:null;if(f&&d instanceof f)return d.$$unwrapTrustedValue();if(c===ga.RESOURCE_URL){var f=sa(d.toString()),l,n,p=!1;l=0;for(n=b.length;l<n;l++)if("self"===b[l]?Mb(f):b[l].exec(f.href)){p=!0;break}if(p)for(l=0,n=a.length;l<n;l++)if("self"===a[l]?Mb(f):a[l].exec(f.href)){p=
!1;break}if(p)return d;throw ua("insecurl",d.toString());}if(c===ga.HTML)return e(d);throw ua("unsafe");},valueOf:function(a){return a instanceof f?a.$$unwrapTrustedValue():a}}}]}function $d(){var b=!0;this.enabled=function(a){arguments.length&&(b=!!a);return b};this.$get=["$parse","$sniffer","$sceDelegate",function(a,c,d){if(b&&c.msie&&8>c.msieDocumentMode)throw ua("iequirks");var e=ka(ga);e.isEnabled=function(){return b};e.trustAs=d.trustAs;e.getTrusted=d.getTrusted;e.valueOf=d.valueOf;b||(e.trustAs=
e.getTrusted=function(a,b){return b},e.valueOf=Ea);e.parseAs=function(b,c){var d=a(c);return d.literal&&d.constant?d:function(a,c){return e.getTrusted(b,d(a,c))}};var f=e.parseAs,g=e.getTrusted,h=e.trustAs;q(ga,function(a,b){var c=J(b);e[Ua("parse_as_"+c)]=function(b){return f(a,b)};e[Ua("get_trusted_"+c)]=function(b){return g(a,b)};e[Ua("trust_as_"+c)]=function(b){return h(a,b)}});return e}]}function be(){this.$get=["$window","$document",function(b,a){var c={},d=Y((/android (\d+)/.exec(J((b.navigator||
{}).userAgent))||[])[1]),e=/Boxee/i.test((b.navigator||{}).userAgent),f=a[0]||{},g=f.documentMode,h,m=/^(Moz|webkit|O|ms)(?=[A-Z])/,k=f.body&&f.body.style,l=!1,n=!1;if(k){for(var p in k)if(l=m.exec(p)){h=l[0];h=h.substr(0,1).toUpperCase()+h.substr(1);break}h||(h="WebkitOpacity"in k&&"webkit");l=!!("transition"in k||h+"Transition"in k);n=!!("animation"in k||h+"Animation"in k);!d||l&&n||(l=C(f.body.style.webkitTransition),n=C(f.body.style.webkitAnimation))}return{history:!(!b.history||!b.history.pushState||
4>d||e),hashchange:"onhashchange"in b&&(!g||7<g),hasEvent:function(a){if("input"==a&&9==S)return!1;if(H(c[a])){var b=f.createElement("div");c[a]="on"+a in b}return c[a]},csp:$b(),vendorPrefix:h,transitions:l,animations:n,android:d,msie:S,msieDocumentMode:g}}]}function de(){this.$get=["$rootScope","$browser","$q","$exceptionHandler",function(b,a,c,d){function e(e,h,m){var k=c.defer(),l=k.promise,n=z(m)&&!m;h=a.defer(function(){try{k.resolve(e())}catch(a){k.reject(a),d(a)}finally{delete f[l.$$timeoutId]}n||
b.$apply()},h);l.$$timeoutId=h;f[h]=k;return l}var f={};e.cancel=function(b){return b&&b.$$timeoutId in f?(f[b.$$timeoutId].reject("canceled"),delete f[b.$$timeoutId],a.defer.cancel(b.$$timeoutId)):!1};return e}]}function sa(b,a){var c=b;S&&(X.setAttribute("href",c),c=X.href);X.setAttribute("href",c);return{href:X.href,protocol:X.protocol?X.protocol.replace(/:$/,""):"",host:X.host,search:X.search?X.search.replace(/^\?/,""):"",hash:X.hash?X.hash.replace(/^#/,""):"",hostname:X.hostname,port:X.port,
pathname:"/"===X.pathname.charAt(0)?X.pathname:"/"+X.pathname}}function Mb(b){b=C(b)?sa(b):b;return b.protocol===Ic.protocol&&b.host===Ic.host}function ee(){this.$get=aa(P)}function kc(b){function a(d,e){if(U(d)){var f={};q(d,function(b,c){f[c]=a(c,b)});return f}return b.factory(d+c,e)}var c="Filter";this.register=a;this.$get=["$injector",function(a){return function(b){return a.get(b+c)}}];a("currency",Jc);a("date",Kc);a("filter",Be);a("json",Ce);a("limitTo",De);a("lowercase",Ee);a("number",Lc);a("orderBy",
Mc);a("uppercase",Fe)}function Be(){return function(b,a,c){if(!K(b))return b;var d=typeof c,e=[];e.check=function(a){for(var b=0;b<e.length;b++)if(!e[b](a))return!1;return!0};"function"!==d&&(c="boolean"===d&&c?function(a,b){return Ra.equals(a,b)}:function(a,b){if(a&&b&&"object"===typeof a&&"object"===typeof b){for(var d in a)if("$"!==d.charAt(0)&&Ab.call(a,d)&&c(a[d],b[d]))return!0;return!1}b=(""+b).toLowerCase();return-1<(""+a).toLowerCase().indexOf(b)});var f=function(a,b){if("string"==typeof b&&
"!"===b.charAt(0))return!f(a,b.substr(1));switch(typeof a){case "boolean":case "number":case "string":return c(a,b);case "object":switch(typeof b){case "object":return c(a,b);default:for(var d in a)if("$"!==d.charAt(0)&&f(a[d],b))return!0}return!1;case "array":for(d=0;d<a.length;d++)if(f(a[d],b))return!0;return!1;default:return!1}};switch(typeof a){case "boolean":case "number":case "string":a={$:a};case "object":for(var g in a)(function(b){"undefined"!=typeof a[b]&&e.push(function(c){return f("$"==
b?c:c&&c[b],a[b])})})(g);break;case "function":e.push(a);break;default:return b}d=[];for(g=0;g<b.length;g++){var h=b[g];e.check(h)&&d.push(h)}return d}}function Jc(b){var a=b.NUMBER_FORMATS;return function(b,d){H(d)&&(d=a.CURRENCY_SYM);return Nc(b,a.PATTERNS[1],a.GROUP_SEP,a.DECIMAL_SEP,2).replace(/\u00A4/g,d)}}function Lc(b){var a=b.NUMBER_FORMATS;return function(b,d){return Nc(b,a.PATTERNS[0],a.GROUP_SEP,a.DECIMAL_SEP,d)}}function Nc(b,a,c,d,e){if(null==b||!isFinite(b)||U(b))return"";var f=0>b;
b=Math.abs(b);var g=b+"",h="",m=[],k=!1;if(-1!==g.indexOf("e")){var l=g.match(/([\d\.]+)e(-?)(\d+)/);l&&"-"==l[2]&&l[3]>e+1?g="0":(h=g,k=!0)}if(k)0<e&&(-1<b&&1>b)&&(h=b.toFixed(e));else{g=(g.split(Oc)[1]||"").length;H(e)&&(e=Math.min(Math.max(a.minFrac,g),a.maxFrac));g=Math.pow(10,e+1);b=Math.floor(b*g+5)/g;b=(""+b).split(Oc);g=b[0];b=b[1]||"";var l=0,n=a.lgSize,p=a.gSize;if(g.length>=n+p)for(l=g.length-n,k=0;k<l;k++)0===(l-k)%p&&0!==k&&(h+=c),h+=g.charAt(k);for(k=l;k<g.length;k++)0===(g.length-k)%
n&&0!==k&&(h+=c),h+=g.charAt(k);for(;b.length<e;)b+="0";e&&"0"!==e&&(h+=d+b.substr(0,e))}m.push(f?a.negPre:a.posPre);m.push(h);m.push(f?a.negSuf:a.posSuf);return m.join("")}function Ub(b,a,c){var d="";0>b&&(d="-",b=-b);for(b=""+b;b.length<a;)b="0"+b;c&&(b=b.substr(b.length-a));return d+b}function $(b,a,c,d){c=c||0;return function(e){e=e["get"+b]();if(0<c||e>-c)e+=c;0===e&&-12==c&&(e=12);return Ub(e,a,d)}}function tb(b,a){return function(c,d){var e=c["get"+b](),f=Ga(a?"SHORT"+b:b);return d[f][e]}}
function Kc(b){function a(a){var b;if(b=a.match(c)){a=new Date(0);var f=0,g=0,h=b[8]?a.setUTCFullYear:a.setFullYear,m=b[8]?a.setUTCHours:a.setHours;b[9]&&(f=Y(b[9]+b[10]),g=Y(b[9]+b[11]));h.call(a,Y(b[1]),Y(b[2])-1,Y(b[3]));f=Y(b[4]||0)-f;g=Y(b[5]||0)-g;h=Y(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));m.call(a,f,g,h,b)}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,e){var f="",g=[],h,m;e=e||"mediumDate";
e=b.DATETIME_FORMATS[e]||e;C(c)&&(c=Ge.test(c)?Y(c):a(c));zb(c)&&(c=new Date(c));if(!Ma(c))return c;for(;e;)(m=He.exec(e))?(g=g.concat(ya.call(m,1)),e=g.pop()):(g.push(e),e=null);q(g,function(a){h=Ie[a];f+=h?h(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return f}}function Ce(){return function(b){return qa(b,!0)}}function De(){return function(b,a){if(!K(b)&&!C(b))return b;a=Infinity===Math.abs(Number(a))?Number(a):Y(a);if(C(b))return a?0<=a?b.slice(0,a):b.slice(a,b.length):
"";var c=[],d,e;a>b.length?a=b.length:a<-b.length&&(a=-b.length);0<a?(d=0,e=a):(d=b.length+a,e=b.length);for(;d<e;d++)c.push(b[d]);return c}}function Mc(b){return function(a,c,d){function e(a,b){return Qa(b)?function(b,c){return a(c,b)}:a}function f(a,b){var c=typeof a,d=typeof b;return c==d?("string"==c&&(a=a.toLowerCase(),b=b.toLowerCase()),a===b?0:a<b?-1:1):c<d?-1:1}if(!K(a)||!c)return a;c=K(c)?c:[c];c=Uc(c,function(a){var c=!1,d=a||Ea;if(C(a)){if("+"==a.charAt(0)||"-"==a.charAt(0))c="-"==a.charAt(0),
a=a.substring(1);d=b(a);if(d.constant){var g=d();return e(function(a,b){return f(a[g],b[g])},c)}}return e(function(a,b){return f(d(a),d(b))},c)});for(var g=[],h=0;h<a.length;h++)g.push(a[h]);return g.sort(e(function(a,b){for(var d=0;d<c.length;d++){var e=c[d](a,b);if(0!==e)return e}return 0},d))}}function va(b){Q(b)&&(b={link:b});b.restrict=b.restrict||"AC";return aa(b)}function Pc(b,a,c,d){function e(a,c){c=c?"-"+ib(c,"-"):"";d.removeClass(b,(a?ub:vb)+c);d.addClass(b,(a?vb:ub)+c)}var f=this,g=b.parent().controller("form")||
wb,h=0,m=f.$error={},k=[];f.$name=a.name||a.ngForm;f.$dirty=!1;f.$pristine=!0;f.$valid=!0;f.$invalid=!1;g.$addControl(f);b.addClass(La);e(!0);f.$addControl=function(a){Aa(a.$name,"input");k.push(a);a.$name&&(f[a.$name]=a)};f.$removeControl=function(a){a.$name&&f[a.$name]===a&&delete f[a.$name];q(m,function(b,c){f.$setValidity(c,!0,a)});Oa(k,a)};f.$setValidity=function(a,b,c){var d=m[a];if(b)d&&(Oa(d,c),d.length||(h--,h||(e(b),f.$valid=!0,f.$invalid=!1),m[a]=!1,e(!0,a),g.$setValidity(a,!0,f)));else{h||
e(b);if(d){if(-1!=Na(d,c))return}else m[a]=d=[],h++,e(!1,a),g.$setValidity(a,!1,f);d.push(c);f.$valid=!1;f.$invalid=!0}};f.$setDirty=function(){d.removeClass(b,La);d.addClass(b,xb);f.$dirty=!0;f.$pristine=!1;g.$setDirty()};f.$setPristine=function(){d.removeClass(b,xb);d.addClass(b,La);f.$dirty=!1;f.$pristine=!0;q(k,function(a){a.$setPristine()})}}function pa(b,a,c,d){b.$setValidity(a,c);return c?d:s}function Je(b,a,c){var d=c.prop("validity");U(d)&&b.$parsers.push(function(c){if(b.$error[a]||!(d.badInput||
d.customError||d.typeMismatch)||d.valueMissing)return c;b.$setValidity(a,!1)})}function yb(b,a,c,d,e,f){var g=a.prop("validity"),h=a[0].placeholder,m={};if(!e.android){var k=!1;a.on("compositionstart",function(a){k=!0});a.on("compositionend",function(){k=!1;l()})}var l=function(e){if(!k){var f=a.val();if(S&&"input"===(e||m).type&&a[0].placeholder!==h)h=a[0].placeholder;else if(Qa(c.ngTrim||"T")&&(f=ba(f)),d.$viewValue!==f||g&&""===f&&!g.valueMissing)b.$$phase?d.$setViewValue(f):b.$apply(function(){d.$setViewValue(f)})}};
if(e.hasEvent("input"))a.on("input",l);else{var n,p=function(){n||(n=f.defer(function(){l();n=null}))};a.on("keydown",function(a){a=a.keyCode;91===a||(15<a&&19>a||37<=a&&40>=a)||p()});if(e.hasEvent("paste"))a.on("paste cut",p)}a.on("change",l);d.$render=function(){a.val(d.$isEmpty(d.$viewValue)?"":d.$viewValue)};var r=c.ngPattern;r&&((e=r.match(/^\/(.*)\/([gim]*)$/))?(r=RegExp(e[1],e[2]),e=function(a){return pa(d,"pattern",d.$isEmpty(a)||r.test(a),a)}):e=function(c){var e=b.$eval(r);if(!e||!e.test)throw u("ngPattern")("noregexp",
r,e,ha(a));return pa(d,"pattern",d.$isEmpty(c)||e.test(c),c)},d.$formatters.push(e),d.$parsers.push(e));if(c.ngMinlength){var q=Y(c.ngMinlength);e=function(a){return pa(d,"minlength",d.$isEmpty(a)||a.length>=q,a)};d.$parsers.push(e);d.$formatters.push(e)}if(c.ngMaxlength){var D=Y(c.ngMaxlength);e=function(a){return pa(d,"maxlength",d.$isEmpty(a)||a.length<=D,a)};d.$parsers.push(e);d.$formatters.push(e)}}function Vb(b,a){b="ngClass"+b;return["$animate",function(c){function d(a,b){var c=[],d=0;a:for(;d<
a.length;d++){for(var e=a[d],l=0;l<b.length;l++)if(e==b[l])continue a;c.push(e)}return c}function e(a){if(!K(a)){if(C(a))return a.split(" ");if(U(a)){var b=[];q(a,function(a,c){a&&(b=b.concat(c.split(" ")))});return b}}return a}return{restrict:"AC",link:function(f,g,h){function m(a,b){var c=g.data("$classCounts")||{},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 k(b){if(!0===a||f.$index%2===a){var k=e(b||[]);if(!l){var r=
m(k,1);h.$addClass(r)}else if(!xa(b,l)){var q=e(l),r=d(k,q),k=d(q,k),k=m(k,-1),r=m(r,1);0===r.length?c.removeClass(g,k):0===k.length?c.addClass(g,r):c.setClass(g,r,k)}}l=ka(b)}var l;f.$watch(h[b],k,!0);h.$observe("class",function(a){k(f.$eval(h[b]))});"ngClass"!==b&&f.$watch("$index",function(c,d){var g=c&1;if(g!==(d&1)){var k=e(f.$eval(h[b]));g===a?(g=m(k,1),h.$addClass(g)):(g=m(k,-1),h.$removeClass(g))}})}}}]}var J=function(b){return C(b)?b.toLowerCase():b},Ab=Object.prototype.hasOwnProperty,Ga=
function(b){return C(b)?b.toUpperCase():b},S,y,Ba,ya=[].slice,Ke=[].push,wa=Object.prototype.toString,Pa=u("ng"),Ra=P.angular||(P.angular={}),Ta,Ka,ja=["0","0","0"];S=Y((/msie (\d+)/.exec(J(navigator.userAgent))||[])[1]);isNaN(S)&&(S=Y((/trident\/.*; rv:(\d+)/.exec(J(navigator.userAgent))||[])[1]));w.$inject=[];Ea.$inject=[];var ba=function(){return String.prototype.trim?function(b){return C(b)?b.trim():b}:function(b){return C(b)?b.replace(/^\s\s*/,"").replace(/\s\s*$/,""):b}}();Ka=9>S?function(b){b=
b.nodeName?b:b[0];return b.scopeName&&"HTML"!=b.scopeName?Ga(b.scopeName+":"+b.nodeName):b.nodeName}:function(b){return b.nodeName?b.nodeName:b[0].nodeName};var Xc=/[A-Z]/g,$c={full:"1.2.17",major:1,minor:2,dot:17,codeName:"quantum-disentanglement"},Wa=M.cache={},jb=M.expando="ng"+(new Date).getTime(),me=1,qb=P.document.addEventListener?function(b,a,c){b.addEventListener(a,c,!1)}:function(b,a,c){b.attachEvent("on"+a,c)},Va=P.document.removeEventListener?function(b,a,c){b.removeEventListener(a,c,!1)}:
function(b,a,c){b.detachEvent("on"+a,c)};M._data=function(b){return this.cache[b[this.expando]]||{}};var he=/([\:\-\_]+(.))/g,ie=/^moz([A-Z])/,Gb=u("jqLite"),je=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,Hb=/<|&#?\w+;/,ke=/<([\w:]+)/,le=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,da={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,"",""]};da.optgroup=da.option;da.tbody=da.tfoot=da.colgroup=da.caption=da.thead;da.th=da.td;var Ja=M.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=!1;"complete"===V.readyState?setTimeout(a):(this.on("DOMContentLoaded",a),M(P).on("load",a))},toString:function(){var b=[];q(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return 0<=b?y(this[b]):y(this[this.length+b])},length:0,push:Ke,sort:[].sort,splice:[].splice},nb={};
q("multiple selected checked disabled readOnly required open".split(" "),function(b){nb[J(b)]=b});var rc={};q("input select option textarea button form details".split(" "),function(b){rc[Ga(b)]=!0});q({data:nc,inheritedData:mb,scope:function(b){return y(b).data("$scope")||mb(b.parentNode||b,["$isolateScope","$scope"])},isolateScope:function(b){return y(b).data("$isolateScope")||y(b).data("$isolateScopeNoTemplate")},controller:oc,injector:function(b){return mb(b,"$injector")},removeAttr:function(b,
a){b.removeAttribute(a)},hasClass:Kb,css:function(b,a,c){a=Ua(a);if(z(c))b.style[a]=c;else{var d;8>=S&&(d=b.currentStyle&&b.currentStyle[a],""===d&&(d="auto"));d=d||b.style[a];8>=S&&(d=""===d?s:d);return d}},attr:function(b,a,c){var d=J(a);if(nb[d])if(z(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||w).specified?d:s;else if(z(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),null===b?s:b},prop:function(b,
a,c){if(z(c))b[a]=c;else return b[a]},text:function(){function b(b,d){var e=a[b.nodeType];if(H(d))return e?b[e]:"";b[e]=d}var a=[];9>S?(a[1]="innerText",a[3]="nodeValue"):a[1]=a[3]="textContent";b.$dv="";return b}(),val:function(b,a){if(H(a)){if("SELECT"===Ka(b)&&b.multiple){var c=[];q(b.options,function(a){a.selected&&c.push(a.value||a.text)});return 0===c.length?null:c}return b.value}b.value=a},html:function(b,a){if(H(a))return b.innerHTML;for(var c=0,d=b.childNodes;c<d.length;c++)Ha(d[c]);b.innerHTML=
a},empty:pc},function(b,a){M.prototype[a]=function(a,d){var e,f;if(b!==pc&&(2==b.length&&b!==Kb&&b!==oc?a:d)===s){if(U(a)){for(e=0;e<this.length;e++)if(b===nc)b(this[e],a);else for(f in a)b(this[e],f,a[f]);return this}e=b.$dv;f=e===s?Math.min(this.length,1):this.length;for(var g=0;g<f;g++){var h=b(this[g],a,d);e=e?e+h:h}return e}for(e=0;e<this.length;e++)b(this[e],a,d);return this}});q({removeData:lc,dealoc:Ha,on:function a(c,d,e,f){if(z(f))throw Gb("onargs");var g=la(c,"events"),h=la(c,"handle");
g||la(c,"events",g={});h||la(c,"handle",h=ne(c,g));q(d.split(" "),function(d){var f=g[d];if(!f){if("mouseenter"==d||"mouseleave"==d){var l=V.body.contains||V.body.compareDocumentPosition?function(a,c){var d=9===a.nodeType?a.documentElement:a,e=c&&c.parentNode;return a===e||!!(e&&1===e.nodeType&&(d.contains?d.contains(e):a.compareDocumentPosition&&a.compareDocumentPosition(e)&16))}:function(a,c){if(c)for(;c=c.parentNode;)if(c===a)return!0;return!1};g[d]=[];a(c,{mouseleave:"mouseout",mouseenter:"mouseover"}[d],
function(a){var c=a.relatedTarget;c&&(c===this||l(this,c))||h(a,d)})}else qb(c,d,h),g[d]=[];f=g[d]}f.push(e)})},off:mc,one:function(a,c,d){a=y(a);a.on(c,function f(){a.off(c,d);a.off(c,f)});a.on(c,d)},replaceWith:function(a,c){var d,e=a.parentNode;Ha(a);q(new M(c),function(c){d?e.insertBefore(c,d.nextSibling):e.replaceChild(c,a);d=c})},children:function(a){var c=[];q(a.childNodes,function(a){1===a.nodeType&&c.push(a)});return c},contents:function(a){return a.contentDocument||a.childNodes||[]},append:function(a,
c){q(new M(c),function(c){1!==a.nodeType&&11!==a.nodeType||a.appendChild(c)})},prepend:function(a,c){if(1===a.nodeType){var d=a.firstChild;q(new M(c),function(c){a.insertBefore(c,d)})}},wrap:function(a,c){c=y(c)[0];var d=a.parentNode;d&&d.replaceChild(c,a);c.appendChild(a)},remove:function(a){Ha(a);var c=a.parentNode;c&&c.removeChild(a)},after:function(a,c){var d=a,e=a.parentNode;q(new M(c),function(a){e.insertBefore(a,d.nextSibling);d=a})},addClass:lb,removeClass:kb,toggleClass:function(a,c,d){c&&
q(c.split(" "),function(c){var f=d;H(f)&&(f=!Kb(a,c));(f?lb:kb)(a,c)})},parent:function(a){return(a=a.parentNode)&&11!==a.nodeType?a:null},next:function(a){if(a.nextElementSibling)return a.nextElementSibling;for(a=a.nextSibling;null!=a&&1!==a.nodeType;)a=a.nextSibling;return a},find:function(a,c){return a.getElementsByTagName?a.getElementsByTagName(c):[]},clone:Jb,triggerHandler:function(a,c,d){c=(la(a,"events")||{})[c];d=d||[];var e=[{preventDefault:w,stopPropagation:w}];q(c,function(c){c.apply(a,
e.concat(d))})}},function(a,c){M.prototype[c]=function(c,e,f){for(var g,h=0;h<this.length;h++)H(g)?(g=a(this[h],c,e,f),z(g)&&(g=y(g))):Ib(g,a(this[h],c,e,f));return z(g)?g:this};M.prototype.bind=M.prototype.on;M.prototype.unbind=M.prototype.off});Xa.prototype={put:function(a,c){this[Ia(a)]=c},get:function(a){return this[Ia(a)]},remove:function(a){var c=this[a=Ia(a)];delete this[a];return c}};var pe=/^function\s*[^\(]*\(\s*([^\)]*)\)/m,qe=/,/,re=/^\s*(_?)(\S+?)\1\s*$/,oe=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,
Ya=u("$injector"),Le=u("$animate"),Ld=["$provide",function(a){this.$$selectors={};this.register=function(c,d){var e=c+"-animation";if(c&&"."!=c.charAt(0))throw Le("notcsel",c);this.$$selectors[c.substr(1)]=e;a.factory(e,d)};this.classNameFilter=function(a){1===arguments.length&&(this.$$classNameFilter=a instanceof RegExp?a:null);return this.$$classNameFilter};this.$get=["$timeout","$$asyncCallback",function(a,d){return{enter:function(a,c,g,h){g?g.after(a):(c&&c[0]||(c=g.parent()),c.append(a));h&&
d(h)},leave:function(a,c){a.remove();c&&d(c)},move:function(a,c,d,h){this.enter(a,c,d,h)},addClass:function(a,c,g){c=C(c)?c:K(c)?c.join(" "):"";q(a,function(a){lb(a,c)});g&&d(g)},removeClass:function(a,c,g){c=C(c)?c:K(c)?c.join(" "):"";q(a,function(a){kb(a,c)});g&&d(g)},setClass:function(a,c,g,h){q(a,function(a){lb(a,c);kb(a,g)});h&&d(h)},enabled:w}}]}],ia=u("$compile");gc.$inject=["$provide","$$sanitizeUriProvider"];var te=/^(x[\:\-_]|data[\:\-_])/i,zc=u("$interpolate"),Me=/^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
we={http:80,https:443,ftp:21},Pb=u("$location");Rb.prototype=Qb.prototype=Cc.prototype={$$html5:!1,$$replace:!1,absUrl:rb("$$absUrl"),url:function(a,c){if(H(a))return this.$$url;var d=Me.exec(a);d[1]&&this.path(decodeURIComponent(d[1]));(d[2]||d[1])&&this.search(d[3]||"");this.hash(d[5]||"",c);return this},protocol:rb("$$protocol"),host:rb("$$host"),port:rb("$$port"),path:Dc("$$path",function(a){return"/"==a.charAt(0)?a:"/"+a}),search:function(a,c){switch(arguments.length){case 0:return this.$$search;
case 1:if(C(a))this.$$search=cc(a);else if(U(a))this.$$search=a;else throw Pb("isrcharg");break;default:H(c)||null===c?delete this.$$search[a]:this.$$search[a]=c}this.$$compose();return this},hash:Dc("$$hash",Ea),replace:function(){this.$$replace=!0;return this}};var Ca=u("$parse"),Gc={},ta,cb={"null":function(){return null},"true":function(){return!0},"false":function(){return!1},undefined:w,"+":function(a,c,d,e){d=d(a,c);e=e(a,c);return z(d)?z(e)?d+e:d:z(e)?e:s},"-":function(a,c,d,e){d=d(a,c);e=
e(a,c);return(z(d)?d:0)-(z(e)?e:0)},"*":function(a,c,d,e){return d(a,c)*e(a,c)},"/":function(a,c,d,e){return d(a,c)/e(a,c)},"%":function(a,c,d,e){return d(a,c)%e(a,c)},"^":function(a,c,d,e){return d(a,c)^e(a,c)},"=":w,"===":function(a,c,d,e){return d(a,c)===e(a,c)},"!==":function(a,c,d,e){return d(a,c)!==e(a,c)},"==":function(a,c,d,e){return d(a,c)==e(a,c)},"!=":function(a,c,d,e){return d(a,c)!=e(a,c)},"<":function(a,c,d,e){return d(a,c)<e(a,c)},">":function(a,c,d,e){return d(a,c)>e(a,c)},"<=":function(a,
c,d,e){return d(a,c)<=e(a,c)},">=":function(a,c,d,e){return d(a,c)>=e(a,c)},"&&":function(a,c,d,e){return d(a,c)&&e(a,c)},"||":function(a,c,d,e){return d(a,c)||e(a,c)},"&":function(a,c,d,e){return d(a,c)&e(a,c)},"|":function(a,c,d,e){return e(a,c)(a,c,d(a,c))},"!":function(a,c,d){return!d(a,c)}},Ne={n:"\n",f:"\f",r:"\r",t:"\t",v:"\v","'":"'",'"':'"'},Tb=function(a){this.options=a};Tb.prototype={constructor:Tb,lex:function(a){this.text=a;this.index=0;this.ch=s;this.lastCh=":";for(this.tokens=[];this.index<
this.text.length;){this.ch=this.text.charAt(this.index);if(this.is("\"'"))this.readString(this.ch);else if(this.isNumber(this.ch)||this.is(".")&&this.isNumber(this.peek()))this.readNumber();else if(this.isIdent(this.ch))this.readIdent();else if(this.is("(){}[].,;:?"))this.tokens.push({index:this.index,text:this.ch}),this.index++;else if(this.isWhitespace(this.ch)){this.index++;continue}else{a=this.ch+this.peek();var c=a+this.peek(2),d=cb[this.ch],e=cb[a],f=cb[c];f?(this.tokens.push({index:this.index,
text:c,fn:f}),this.index+=3):e?(this.tokens.push({index:this.index,text:a,fn:e}),this.index+=2):d?(this.tokens.push({index:this.index,text:this.ch,fn:d}),this.index+=1):this.throwError("Unexpected next character ",this.index,this.index+1)}this.lastCh=this.ch}return this.tokens},is:function(a){return-1!==a.indexOf(this.ch)},was:function(a){return-1!==a.indexOf(this.lastCh)},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},isWhitespace:function(a){return" "===a||"\r"===a||"\t"===a||"\n"===a||"\v"===a||"\u00a0"===a},isIdent:function(a){return"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},throwError:function(a,c,d){d=d||this.index;c=z(c)?"s "+c+"-"+this.index+" ["+this.text.substring(c,d)+"]":" "+d;throw Ca("lexerr",a,c,this.text);},readNumber:function(){for(var a="",c=this.index;this.index<this.text.length;){var d=J(this.text.charAt(this.index));
if("."==d||this.isNumber(d))a+=d;else{var e=this.peek();if("e"==d&&this.isExpOperator(e))a+=d;else if(this.isExpOperator(d)&&e&&this.isNumber(e)&&"e"==a.charAt(a.length-1))a+=d;else if(!this.isExpOperator(d)||e&&this.isNumber(e)||"e"!=a.charAt(a.length-1))break;else this.throwError("Invalid exponent")}this.index++}a*=1;this.tokens.push({index:c,text:a,literal:!0,constant:!0,fn:function(){return a}})},readIdent:function(){for(var a=this,c="",d=this.index,e,f,g,h;this.index<this.text.length;){h=this.text.charAt(this.index);
if("."===h||this.isIdent(h)||this.isNumber(h))"."===h&&(e=this.index),c+=h;else break;this.index++}if(e)for(f=this.index;f<this.text.length;){h=this.text.charAt(f);if("("===h){g=c.substr(e-d+1);c=c.substr(0,e-d);this.index=f;break}if(this.isWhitespace(h))f++;else break}d={index:d,text:c};if(cb.hasOwnProperty(c))d.fn=cb[c],d.literal=!0,d.constant=!0;else{var m=Fc(c,this.options,this.text);d.fn=E(function(a,c){return m(a,c)},{assign:function(d,e){return sb(d,c,e,a.text,a.options)}})}this.tokens.push(d);
g&&(this.tokens.push({index:e,text:"."}),this.tokens.push({index:e+1,text:g}))},readString:function(a){var c=this.index;this.index++;for(var d="",e=a,f=!1;this.index<this.text.length;){var g=this.text.charAt(this.index),e=e+g;if(f)"u"===g?(g=this.text.substring(this.index+1,this.index+5),g.match(/[\da-f]{4}/i)||this.throwError("Invalid unicode escape [\\u"+g+"]"),this.index+=4,d+=String.fromCharCode(parseInt(g,16))):d=(f=Ne[g])?d+f:d+g,f=!1;else if("\\"===g)f=!0;else{if(g===a){this.index++;this.tokens.push({index:c,
text:e,string:d,literal:!0,constant:!0,fn:function(){return d}});return}d+=g}this.index++}this.throwError("Unterminated quote",c)}};var bb=function(a,c,d){this.lexer=a;this.$filter=c;this.options=d};bb.ZERO=E(function(){return 0},{constant:!0});bb.prototype={constructor:bb,parse:function(a){this.text=a;this.tokens=this.lexer.lex(a);a=this.statements();0!==this.tokens.length&&this.throwError("is an unexpected token",this.tokens[0]);a.literal=!!a.literal;a.constant=!!a.constant;return a},primary:function(){var a;
if(this.expect("("))a=this.filterChain(),this.consume(")");else if(this.expect("["))a=this.arrayDeclaration();else if(this.expect("{"))a=this.object();else{var c=this.expect();(a=c.fn)||this.throwError("not a primary expression",c);a.literal=!!c.literal;a.constant=!!c.constant}for(var d;c=this.expect("(","[",".");)"("===c.text?(a=this.functionCall(a,d),d=null):"["===c.text?(d=a,a=this.objectIndex(a)):"."===c.text?(d=a,a=this.fieldAccess(a)):this.throwError("IMPOSSIBLE");return a},throwError:function(a,
c){throw Ca("syntax",c.text,a,c.index+1,this.text,this.text.substring(c.index));},peekToken:function(){if(0===this.tokens.length)throw Ca("ueoe",this.text);return this.tokens[0]},peek:function(a,c,d,e){if(0<this.tokens.length){var f=this.tokens[0],g=f.text;if(g===a||g===c||g===d||g===e||!(a||c||d||e))return f}return!1},expect:function(a,c,d,e){return(a=this.peek(a,c,d,e))?(this.tokens.shift(),a):!1},consume:function(a){this.expect(a)||this.throwError("is unexpected, expecting ["+a+"]",this.peek())},
unaryFn:function(a,c){return E(function(d,e){return a(d,e,c)},{constant:c.constant})},ternaryFn:function(a,c,d){return E(function(e,f){return a(e,f)?c(e,f):d(e,f)},{constant:a.constant&&c.constant&&d.constant})},binaryFn:function(a,c,d){return E(function(e,f){return c(e,f,a,d)},{constant:a.constant&&d.constant})},statements:function(){for(var a=[];;)if(0<this.tokens.length&&!this.peek("}",")",";","]")&&a.push(this.filterChain()),!this.expect(";"))return 1===a.length?a[0]:function(c,d){for(var e,f=
0;f<a.length;f++){var g=a[f];g&&(e=g(c,d))}return e}},filterChain:function(){for(var a=this.expression(),c;;)if(c=this.expect("|"))a=this.binaryFn(a,c.fn,this.filter());else return a},filter:function(){for(var a=this.expect(),c=this.$filter(a.text),d=[];;)if(a=this.expect(":"))d.push(this.expression());else{var e=function(a,e,h){h=[h];for(var m=0;m<d.length;m++)h.push(d[m](a,e));return c.apply(a,h)};return function(){return e}}},expression:function(){return this.assignment()},assignment:function(){var a=
this.ternary(),c,d;return(d=this.expect("="))?(a.assign||this.throwError("implies assignment but ["+this.text.substring(0,d.index)+"] can not be assigned to",d),c=this.ternary(),function(d,f){return a.assign(d,c(d,f),f)}):a},ternary:function(){var a=this.logicalOR(),c,d;if(this.expect("?")){c=this.ternary();if(d=this.expect(":"))return this.ternaryFn(a,c,this.ternary());this.throwError("expected :",d)}else return a},logicalOR:function(){for(var a=this.logicalAND(),c;;)if(c=this.expect("||"))a=this.binaryFn(a,
c.fn,this.logicalAND());else return a},logicalAND:function(){var a=this.equality(),c;if(c=this.expect("&&"))a=this.binaryFn(a,c.fn,this.logicalAND());return a},equality:function(){var a=this.relational(),c;if(c=this.expect("==","!=","===","!=="))a=this.binaryFn(a,c.fn,this.equality());return a},relational:function(){var a=this.additive(),c;if(c=this.expect("<",">","<=",">="))a=this.binaryFn(a,c.fn,this.relational());return a},additive:function(){for(var a=this.multiplicative(),c;c=this.expect("+",
"-");)a=this.binaryFn(a,c.fn,this.multiplicative());return a},multiplicative:function(){for(var a=this.unary(),c;c=this.expect("*","/","%");)a=this.binaryFn(a,c.fn,this.unary());return a},unary:function(){var a;return this.expect("+")?this.primary():(a=this.expect("-"))?this.binaryFn(bb.ZERO,a.fn,this.unary()):(a=this.expect("!"))?this.unaryFn(a.fn,this.unary()):this.primary()},fieldAccess:function(a){var c=this,d=this.expect().text,e=Fc(d,this.options,this.text);return E(function(c,d,h){return e(h||
a(c,d))},{assign:function(e,g,h){return sb(a(e,h),d,g,c.text,c.options)}})},objectIndex:function(a){var c=this,d=this.expression();this.consume("]");return E(function(e,f){var g=a(e,f),h=d(e,f),m;if(!g)return s;(g=ab(g[h],c.text))&&(g.then&&c.options.unwrapPromises)&&(m=g,"$$v"in g||(m.$$v=s,m.then(function(a){m.$$v=a})),g=g.$$v);return g},{assign:function(e,f,g){var h=d(e,g);return ab(a(e,g),c.text)[h]=f}})},functionCall:function(a,c){var d=[];if(")"!==this.peekToken().text){do d.push(this.expression());
while(this.expect(","))}this.consume(")");var e=this;return function(f,g){for(var h=[],m=c?c(f,g):f,k=0;k<d.length;k++)h.push(d[k](f,g));k=a(f,g,m)||w;ab(m,e.text);ab(k,e.text);h=k.apply?k.apply(m,h):k(h[0],h[1],h[2],h[3],h[4]);return ab(h,e.text)}},arrayDeclaration:function(){var a=[],c=!0;if("]"!==this.peekToken().text){do{if(this.peek("]"))break;var d=this.expression();a.push(d);d.constant||(c=!1)}while(this.expect(","))}this.consume("]");return E(function(c,d){for(var g=[],h=0;h<a.length;h++)g.push(a[h](c,
d));return g},{literal:!0,constant:c})},object:function(){var a=[],c=!0;if("}"!==this.peekToken().text){do{if(this.peek("}"))break;var d=this.expect(),d=d.string||d.text;this.consume(":");var e=this.expression();a.push({key:d,value:e});e.constant||(c=!1)}while(this.expect(","))}this.consume("}");return E(function(c,d){for(var e={},m=0;m<a.length;m++){var k=a[m];e[k.key]=k.value(c,d)}return e},{literal:!0,constant:c})}};var Sb={},ua=u("$sce"),ga={HTML:"html",CSS:"css",URL:"url",RESOURCE_URL:"resourceUrl",
JS:"js"},X=V.createElement("a"),Ic=sa(P.location.href,!0);kc.$inject=["$provide"];Jc.$inject=["$locale"];Lc.$inject=["$locale"];var Oc=".",Ie={yyyy:$("FullYear",4),yy:$("FullYear",2,0,!0),y:$("FullYear",1),MMMM:tb("Month"),MMM:tb("Month",!0),MM:$("Month",2,1),M:$("Month",1,1),dd:$("Date",2),d:$("Date",1),HH:$("Hours",2),H:$("Hours",1),hh:$("Hours",2,-12),h:$("Hours",1,-12),mm:$("Minutes",2),m:$("Minutes",1),ss:$("Seconds",2),s:$("Seconds",1),sss:$("Milliseconds",3),EEEE:tb("Day"),EEE:tb("Day",!0),
a:function(a,c){return 12>a.getHours()?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){a=-1*a.getTimezoneOffset();return a=(0<=a?"+":"")+(Ub(Math[0<a?"floor":"ceil"](a/60),2)+Ub(Math.abs(a%60),2))}},He=/((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/,Ge=/^\-?\d+$/;Kc.$inject=["$locale"];var Ee=aa(J),Fe=aa(Ga);Mc.$inject=["$parse"];var cd=aa({restrict:"E",compile:function(a,c){8>=S&&(c.href||c.name||c.$set("href",""),a.append(V.createComment("IE fix")));if(!c.href&&!c.xlinkHref&&
!c.name)return function(a,c){var f="[object SVGAnimatedString]"===wa.call(c.prop("href"))?"xlink:href":"href";c.on("click",function(a){c.attr(f)||a.preventDefault()})}}}),Eb={};q(nb,function(a,c){if("multiple"!=a){var d=na("ng-"+c);Eb[d]=function(){return{priority:100,link:function(a,f,g){a.$watch(g[d],function(a){g.$set(c,!!a)})}}}}});q(["src","srcset","href"],function(a){var c=na("ng-"+a);Eb[c]=function(){return{priority:99,link:function(d,e,f){var g=a,h=a;"href"===a&&"[object SVGAnimatedString]"===
wa.call(e.prop("href"))&&(h="xlinkHref",f.$attr[h]="xlink:href",g=null);f.$observe(c,function(a){a&&(f.$set(h,a),S&&g&&e.prop(g,f[h]))})}}}});var wb={$addControl:w,$removeControl:w,$setValidity:w,$setDirty:w,$setPristine:w};Pc.$inject=["$element","$attrs","$scope","$animate"];var Qc=function(a){return["$timeout",function(c){return{name:"form",restrict:a?"EAC":"E",controller:Pc,compile:function(){return{pre:function(a,e,f,g){if(!f.action){var h=function(a){a.preventDefault?a.preventDefault():a.returnValue=
!1};qb(e[0],"submit",h);e.on("$destroy",function(){c(function(){Va(e[0],"submit",h)},0,!1)})}var m=e.parent().controller("form"),k=f.name||f.ngForm;k&&sb(a,k,g,k);if(m)e.on("$destroy",function(){m.$removeControl(g);k&&sb(a,k,s,k);E(g,wb)})}}}}}]},dd=Qc(),qd=Qc(!0),Oe=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,Pe=/^[a-z0-9!#$%&'*+/=?^_`{|}~.-]+@[a-z0-9-]+(\.[a-z0-9-]+)*$/i,Qe=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,Rc={text:yb,number:function(a,c,d,e,f,g){yb(a,
c,d,e,f,g);e.$parsers.push(function(a){var c=e.$isEmpty(a);if(c||Qe.test(a))return e.$setValidity("number",!0),""===a?null:c?a:parseFloat(a);e.$setValidity("number",!1);return s});Je(e,"number",c);e.$formatters.push(function(a){return e.$isEmpty(a)?"":""+a});d.min&&(a=function(a){var c=parseFloat(d.min);return pa(e,"min",e.$isEmpty(a)||a>=c,a)},e.$parsers.push(a),e.$formatters.push(a));d.max&&(a=function(a){var c=parseFloat(d.max);return pa(e,"max",e.$isEmpty(a)||a<=c,a)},e.$parsers.push(a),e.$formatters.push(a));
e.$formatters.push(function(a){return pa(e,"number",e.$isEmpty(a)||zb(a),a)})},url:function(a,c,d,e,f,g){yb(a,c,d,e,f,g);a=function(a){return pa(e,"url",e.$isEmpty(a)||Oe.test(a),a)};e.$formatters.push(a);e.$parsers.push(a)},email:function(a,c,d,e,f,g){yb(a,c,d,e,f,g);a=function(a){return pa(e,"email",e.$isEmpty(a)||Pe.test(a),a)};e.$formatters.push(a);e.$parsers.push(a)},radio:function(a,c,d,e){H(d.name)&&c.attr("name",eb());c.on("click",function(){c[0].checked&&a.$apply(function(){e.$setViewValue(d.value)})});
e.$render=function(){c[0].checked=d.value==e.$viewValue};d.$observe("value",e.$render)},checkbox:function(a,c,d,e){var f=d.ngTrueValue,g=d.ngFalseValue;C(f)||(f=!0);C(g)||(g=!1);c.on("click",function(){a.$apply(function(){e.$setViewValue(c[0].checked)})});e.$render=function(){c[0].checked=e.$viewValue};e.$isEmpty=function(a){return a!==f};e.$formatters.push(function(a){return a===f});e.$parsers.push(function(a){return a?f:g})},hidden:w,button:w,submit:w,reset:w,file:w},hc=["$browser","$sniffer",function(a,
c){return{restrict:"E",require:"?ngModel",link:function(d,e,f,g){g&&(Rc[J(f.type)]||Rc.text)(d,e,f,g,c,a)}}}],vb="ng-valid",ub="ng-invalid",La="ng-pristine",xb="ng-dirty",Re=["$scope","$exceptionHandler","$attrs","$element","$parse","$animate",function(a,c,d,e,f,g){function h(a,c){c=c?"-"+ib(c,"-"):"";g.removeClass(e,(a?ub:vb)+c);g.addClass(e,(a?vb:ub)+c)}this.$modelValue=this.$viewValue=Number.NaN;this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$pristine=!0;this.$dirty=!1;
this.$valid=!0;this.$invalid=!1;this.$name=d.name;var m=f(d.ngModel),k=m.assign;if(!k)throw u("ngModel")("nonassign",d.ngModel,ha(e));this.$render=w;this.$isEmpty=function(a){return H(a)||""===a||null===a||a!==a};var l=e.inheritedData("$formController")||wb,n=0,p=this.$error={};e.addClass(La);h(!0);this.$setValidity=function(a,c){p[a]!==!c&&(c?(p[a]&&n--,n||(h(!0),this.$valid=!0,this.$invalid=!1)):(h(!1),this.$invalid=!0,this.$valid=!1,n++),p[a]=!c,h(c,a),l.$setValidity(a,c,this))};this.$setPristine=
function(){this.$dirty=!1;this.$pristine=!0;g.removeClass(e,xb);g.addClass(e,La)};this.$setViewValue=function(d){this.$viewValue=d;this.$pristine&&(this.$dirty=!0,this.$pristine=!1,g.removeClass(e,La),g.addClass(e,xb),l.$setDirty());q(this.$parsers,function(a){d=a(d)});this.$modelValue!==d&&(this.$modelValue=d,k(a,d),q(this.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}}))};var r=this;a.$watch(function(){var c=m(a);if(r.$modelValue!==c){var d=r.$formatters,e=d.length;for(r.$modelValue=c;e--;)c=
d[e](c);r.$viewValue!==c&&(r.$viewValue=c,r.$render())}return c})}],Fd=function(){return{require:["ngModel","^?form"],controller:Re,link:function(a,c,d,e){var f=e[0],g=e[1]||wb;g.$addControl(f);a.$on("$destroy",function(){g.$removeControl(f)})}}},Hd=aa({require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),ic=function(){return{require:"?ngModel",link:function(a,c,d,e){if(e){d.required=!0;var f=function(a){if(d.required&&e.$isEmpty(a))e.$setValidity("required",
!1);else return e.$setValidity("required",!0),a};e.$formatters.push(f);e.$parsers.unshift(f);d.$observe("required",function(){f(e.$viewValue)})}}}},Gd=function(){return{require:"ngModel",link:function(a,c,d,e){var f=(a=/\/(.*)\//.exec(d.ngList))&&RegExp(a[1])||d.ngList||",";e.$parsers.push(function(a){if(!H(a)){var c=[];a&&q(a.split(f),function(a){a&&c.push(ba(a))});return c}});e.$formatters.push(function(a){return K(a)?a.join(", "):s});e.$isEmpty=function(a){return!a||!a.length}}}},Se=/^(true|false|\d+)$/,
Id=function(){return{priority:100,compile:function(a,c){return Se.test(c.ngValue)?function(a,c,f){f.$set("value",a.$eval(f.ngValue))}:function(a,c,f){a.$watch(f.ngValue,function(a){f.$set("value",a)})}}}},id=va(function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBind);a.$watch(d.ngBind,function(a){c.text(a==s?"":a)})}),kd=["$interpolate",function(a){return function(c,d,e){c=a(d.attr(e.$attr.ngBindTemplate));d.addClass("ng-binding").data("$binding",c);e.$observe("ngBindTemplate",function(a){d.text(a)})}}],
jd=["$sce","$parse",function(a,c){return function(d,e,f){e.addClass("ng-binding").data("$binding",f.ngBindHtml);var g=c(f.ngBindHtml);d.$watch(function(){return(g(d)||"").toString()},function(c){e.html(a.getTrustedHtml(g(d))||"")})}}],ld=Vb("",!0),nd=Vb("Odd",0),md=Vb("Even",1),od=va({compile:function(a,c){c.$set("ngCloak",s);a.removeClass("ng-cloak")}}),pd=[function(){return{scope:!0,controller:"@",priority:500}}],jc={};q("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),
function(a){var c=na("ng-"+a);jc[c]=["$parse",function(d){return{compile:function(e,f){var g=d(f[c]);return function(c,d,e){d.on(J(a),function(a){c.$apply(function(){g(c,{$event:a})})})}}}}]});var sd=["$animate",function(a){return{transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(c,d,e,f,g){var h,m,k;c.$watch(e.ngIf,function(f){Qa(f)?m||(m=c.$new(),g(m,function(c){c[c.length++]=V.createComment(" end ngIf: "+e.ngIf+" ");h={clone:c};a.enter(c,d.parent(),d)})):(k&&(k.remove(),
k=null),m&&(m.$destroy(),m=null),h&&(k=Db(h.clone),a.leave(k,function(){k=null}),h=null))})}}}],td=["$http","$templateCache","$anchorScroll","$animate","$sce",function(a,c,d,e,f){return{restrict:"ECA",priority:400,terminal:!0,transclude:"element",controller:Ra.noop,compile:function(g,h){var m=h.ngInclude||h.src,k=h.onload||"",l=h.autoscroll;return function(g,h,q,s,D){var x=0,t,y,B,v=function(){y&&(y.remove(),y=null);t&&(t.$destroy(),t=null);B&&(e.leave(B,function(){y=null}),y=B,B=null)};g.$watch(f.parseAsResourceUrl(m),
function(f){var m=function(){!z(l)||l&&!g.$eval(l)||d()},q=++x;f?(a.get(f,{cache:c}).success(function(a){if(q===x){var c=g.$new();s.template=a;a=D(c,function(a){v();e.enter(a,null,h,m)});t=c;B=a;t.$emit("$includeContentLoaded");g.$eval(k)}}).error(function(){q===x&&v()}),g.$emit("$includeContentRequested")):(v(),s.template=null)})}}}}],Jd=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(c,d,e,f){d.html(f.template);a(d.contents())(c)}}}],ud=va({priority:450,
compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),vd=va({terminal:!0,priority:1E3}),wd=["$locale","$interpolate",function(a,c){var d=/{}/g;return{restrict:"EA",link:function(e,f,g){var h=g.count,m=g.$attr.when&&f.attr(g.$attr.when),k=g.offset||0,l=e.$eval(m)||{},n={},p=c.startSymbol(),r=c.endSymbol(),s=/^when(Minus)?(.+)$/;q(g,function(a,c){s.test(c)&&(l[J(c.replace("when","").replace("Minus","-"))]=f.attr(g.$attr[c]))});q(l,function(a,e){n[e]=c(a.replace(d,p+h+"-"+k+r))});e.$watch(function(){var c=
parseFloat(e.$eval(h));if(isNaN(c))return"";c in l||(c=a.pluralCat(c-k));return n[c](e,f,!0)},function(a){f.text(a)})}}}],xd=["$parse","$animate",function(a,c){var d=u("ngRepeat");return{transclude:"element",priority:1E3,terminal:!0,$$tlb:!0,link:function(e,f,g,h,m){var k=g.ngRepeat,l=k.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),n,p,r,s,D,x,t={$id:Ia};if(!l)throw d("iexp",k);g=l[1];h=l[2];(l=l[3])?(n=a(l),p=function(a,c,d){x&&(t[x]=a);t[D]=c;t.$index=d;return n(e,
t)}):(r=function(a,c){return Ia(c)},s=function(a){return a});l=g.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);if(!l)throw d("iidexp",g);D=l[3]||l[1];x=l[2];var z={};e.$watchCollection(h,function(a){var g,h,l=f[0],n,t={},F,R,C,w,T,u,E=[];if(db(a))T=a,n=p||r;else{n=p||s;T=[];for(C in a)a.hasOwnProperty(C)&&"$"!=C.charAt(0)&&T.push(C);T.sort()}F=T.length;h=E.length=T.length;for(g=0;g<h;g++)if(C=a===T?g:T[g],w=a[C],w=n(C,w,g),Aa(w,"`track by` id"),z.hasOwnProperty(w))u=z[w],delete z[w],t[w]=
u,E[g]=u;else{if(t.hasOwnProperty(w))throw q(E,function(a){a&&a.scope&&(z[a.id]=a)}),d("dupes",k,w);E[g]={id:w};t[w]=!1}for(C in z)z.hasOwnProperty(C)&&(u=z[C],g=Db(u.clone),c.leave(g),q(g,function(a){a.$$NG_REMOVED=!0}),u.scope.$destroy());g=0;for(h=T.length;g<h;g++){C=a===T?g:T[g];w=a[C];u=E[g];E[g-1]&&(l=E[g-1].clone[E[g-1].clone.length-1]);if(u.scope){R=u.scope;n=l;do n=n.nextSibling;while(n&&n.$$NG_REMOVED);u.clone[0]!=n&&c.move(Db(u.clone),null,y(l));l=u.clone[u.clone.length-1]}else R=e.$new();
R[D]=w;x&&(R[x]=C);R.$index=g;R.$first=0===g;R.$last=g===F-1;R.$middle=!(R.$first||R.$last);R.$odd=!(R.$even=0===(g&1));u.scope||m(R,function(a){a[a.length++]=V.createComment(" end ngRepeat: "+k+" ");c.enter(a,null,y(l));l=a;u.scope=R;u.clone=a;t[u.id]=u})}z=t})}}}],yd=["$animate",function(a){return function(c,d,e){c.$watch(e.ngShow,function(c){a[Qa(c)?"removeClass":"addClass"](d,"ng-hide")})}}],rd=["$animate",function(a){return function(c,d,e){c.$watch(e.ngHide,function(c){a[Qa(c)?"addClass":"removeClass"](d,
"ng-hide")})}}],zd=va(function(a,c,d){a.$watch(d.ngStyle,function(a,d){d&&a!==d&&q(d,function(a,d){c.css(d,"")});a&&c.css(a)},!0)}),Ad=["$animate",function(a){return{restrict:"EA",require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(c,d,e,f){var g=[],h=[],m=[],k=[];c.$watch(e.ngSwitch||e.on,function(d){var n,p;n=0;for(p=m.length;n<p;++n)m[n].remove();n=m.length=0;for(p=k.length;n<p;++n){var r=h[n];k[n].$destroy();m[n]=r;a.leave(r,function(){m.splice(n,1)})}h.length=0;k.length=
0;if(g=f.cases["!"+d]||f.cases["?"])c.$eval(e.change),q(g,function(d){var e=c.$new();k.push(e);d.transclude(e,function(c){var e=d.element;h.push(c);a.enter(c,e.parent(),e)})})})}}}],Bd=va({transclude:"element",priority:800,require:"^ngSwitch",link:function(a,c,d,e,f){e.cases["!"+d.ngSwitchWhen]=e.cases["!"+d.ngSwitchWhen]||[];e.cases["!"+d.ngSwitchWhen].push({transclude:f,element:c})}}),Cd=va({transclude:"element",priority:800,require:"^ngSwitch",link:function(a,c,d,e,f){e.cases["?"]=e.cases["?"]||
[];e.cases["?"].push({transclude:f,element:c})}}),Ed=va({link:function(a,c,d,e,f){if(!f)throw u("ngTransclude")("orphan",ha(c));f(function(a){c.empty();c.append(a)})}}),ed=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(c,d){"text/ng-template"==d.type&&a.put(d.id,c[0].text)}}}],Te=u("ngOptions"),Dd=aa({terminal:!0}),fd=["$compile","$parse",function(a,c){var d=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/,
e={$setViewValue:w};return{restrict:"E",require:["select","?ngModel"],controller:["$element","$scope","$attrs",function(a,c,d){var m=this,k={},l=e,n;m.databound=d.ngModel;m.init=function(a,c,d){l=a;n=d};m.addOption=function(c){Aa(c,'"option value"');k[c]=!0;l.$viewValue==c&&(a.val(c),n.parent()&&n.remove())};m.removeOption=function(a){this.hasOption(a)&&(delete k[a],l.$viewValue==a&&this.renderUnknownOption(a))};m.renderUnknownOption=function(c){c="? "+Ia(c)+" ?";n.val(c);a.prepend(n);a.val(c);n.prop("selected",
!0)};m.hasOption=function(a){return k.hasOwnProperty(a)};c.$on("$destroy",function(){m.renderUnknownOption=w})}],link:function(e,g,h,m){function k(a,c,d,e){d.$render=function(){var a=d.$viewValue;e.hasOption(a)?(w.parent()&&w.remove(),c.val(a),""===a&&x.prop("selected",!0)):H(a)&&x?c.val(""):e.renderUnknownOption(a)};c.on("change",function(){a.$apply(function(){w.parent()&&w.remove();d.$setViewValue(c.val())})})}function l(a,c,d){var e;d.$render=function(){var a=new Xa(d.$viewValue);q(c.find("option"),
function(c){c.selected=z(a.get(c.value))})};a.$watch(function(){xa(e,d.$viewValue)||(e=ka(d.$viewValue),d.$render())});c.on("change",function(){a.$apply(function(){var a=[];q(c.find("option"),function(c){c.selected&&a.push(c.value)});d.$setViewValue(a)})})}function n(e,f,g){function h(){var a={"":[]},c=[""],d,k,s,u,v;u=g.$modelValue;v=y(e)||[];var A=n?Wb(v):v,E,I,B;I={};s=!1;var F,H;if(r)if(w&&K(u))for(s=new Xa([]),B=0;B<u.length;B++)I[m]=u[B],s.put(w(e,I),u[B]);else s=new Xa(u);for(B=0;E=A.length,
B<E;B++){k=B;if(n){k=A[B];if("$"===k.charAt(0))continue;I[n]=k}I[m]=v[k];d=p(e,I)||"";(k=a[d])||(k=a[d]=[],c.push(d));r?d=z(s.remove(w?w(e,I):q(e,I))):(w?(d={},d[m]=u,d=w(e,d)===w(e,I)):d=u===q(e,I),s=s||d);F=l(e,I);F=z(F)?F:"";k.push({id:w?w(e,I):n?A[B]:B,label:F,selected:d})}r||(D||null===u?a[""].unshift({id:"",label:"",selected:!s}):s||a[""].unshift({id:"?",label:"",selected:!0}));I=0;for(A=c.length;I<A;I++){d=c[I];k=a[d];x.length<=I?(u={element:C.clone().attr("label",d),label:k.label},v=[u],x.push(v),
f.append(u.element)):(v=x[I],u=v[0],u.label!=d&&u.element.attr("label",u.label=d));F=null;B=0;for(E=k.length;B<E;B++)s=k[B],(d=v[B+1])?(F=d.element,d.label!==s.label&&F.text(d.label=s.label),d.id!==s.id&&F.val(d.id=s.id),d.selected!==s.selected&&F.prop("selected",d.selected=s.selected)):(""===s.id&&D?H=D:(H=t.clone()).val(s.id).attr("selected",s.selected).text(s.label),v.push({element:H,label:s.label,id:s.id,selected:s.selected}),F?F.after(H):u.element.append(H),F=H);for(B++;v.length>B;)v.pop().element.remove()}for(;x.length>
I;)x.pop()[0].element.remove()}var k;if(!(k=u.match(d)))throw Te("iexp",u,ha(f));var l=c(k[2]||k[1]),m=k[4]||k[6],n=k[5],p=c(k[3]||""),q=c(k[2]?k[1]:m),y=c(k[7]),w=k[8]?c(k[8]):null,x=[[{element:f,label:""}]];D&&(a(D)(e),D.removeClass("ng-scope"),D.remove());f.empty();f.on("change",function(){e.$apply(function(){var a,c=y(e)||[],d={},h,k,l,p,t,u,v;if(r)for(k=[],p=0,u=x.length;p<u;p++)for(a=x[p],l=1,t=a.length;l<t;l++){if((h=a[l].element)[0].selected){h=h.val();n&&(d[n]=h);if(w)for(v=0;v<c.length&&
(d[m]=c[v],w(e,d)!=h);v++);else d[m]=c[h];k.push(q(e,d))}}else{h=f.val();if("?"==h)k=s;else if(""===h)k=null;else if(w)for(v=0;v<c.length;v++){if(d[m]=c[v],w(e,d)==h){k=q(e,d);break}}else d[m]=c[h],n&&(d[n]=h),k=q(e,d);1<x[0].length&&x[0][1].id!==h&&(x[0][1].selected=!1)}g.$setViewValue(k)})});g.$render=h;e.$watch(h)}if(m[1]){var p=m[0];m=m[1];var r=h.multiple,u=h.ngOptions,D=!1,x,t=y(V.createElement("option")),C=y(V.createElement("optgroup")),w=t.clone();h=0;for(var v=g.children(),E=v.length;h<E;h++)if(""===
v[h].value){x=D=v.eq(h);break}p.init(m,D,w);r&&(m.$isEmpty=function(a){return!a||0===a.length});u?n(e,g,m):r?l(e,g,m):k(e,g,m,p)}}}}],hd=["$interpolate",function(a){var c={addOption:w,removeOption:w};return{restrict:"E",priority:100,compile:function(d,e){if(H(e.value)){var f=a(d.text(),!0);f||e.$set("value",d.text())}return function(a,d,e){var k=d.parent(),l=k.data("$selectController")||k.parent().data("$selectController");l&&l.databound?d.prop("selected",!1):l=c;f?a.$watch(f,function(a,c){e.$set("value",
a);a!==c&&l.removeOption(c);l.addOption(a)}):l.addOption(e.value);d.on("$destroy",function(){l.removeOption(e.value)})}}}}],gd=aa({restrict:"E",terminal:!0});P.angular.bootstrap?console.log("WARNING: Tried to load angular more than once."):((Ba=P.jQuery)&&Ba.fn.on?(y=Ba,E(Ba.fn,{scope:Ja.scope,isolateScope:Ja.isolateScope,controller:Ja.controller,injector:Ja.injector,inheritedData:Ja.inheritedData}),Fb("remove",!0,!0,!1),Fb("empty",!1,!1,!1),Fb("html",!1,!1,!0)):y=M,Ra.element=y,Zc(Ra),y(V).ready(function(){Wc(V,
dc)}))})(window,document);!window.angular.$$csp()&&window.angular.element(document).find("head").prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide{display:none !important;}ng\\:form{display:block;}.ng-animate-block-transitions{transition:0s all!important;-webkit-transition:0s all!important;}.ng-hide-add-active,.ng-hide-remove{display:block!important;}</style>');
//# sourceMappingURL=angular.min.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,45 @@
angular.module('bravoUiAlert', [])
.directive('bravoAlert', function () {
return {
restrict: "A",
transclude: true,
template: '<div ng-transclude></div>',
scope: {
alert_show: '=alertShow',
on_close: '&bravoAlertClose',
on_closed: '&bravoAlertClosed'
},
compile: function (elem, attr) {
var manual = attr['alertShow'];
return function (scope, elem, attr) {
elem.on('click', function(event) {
var obj = angular.element(event.target);
if (obj.attr('data-dismiss')) {
scope.on_destory();
}
});
scope.on_destory = function () {
if (!manual) {
scope.on_close();
elem.addClass('ng-hide');
scope.on_closed();
} else {
scope.on_close();
scope.alert_show = false;
scope.$apply();
}
};
scope.$watch('alert_show', function (nv, ov) {
if (nv != ov) {
if (!nv) {
elem.addClass('ng-hide');
scope.on_closed();
} else {
elem.removeClass('ng-hide');
}
}
});
}
}
}
});

View file

@ -0,0 +1,233 @@
angular.module('bravoUiPopover', [])
.directive("bravoPopover", function($compile, $position, $sce){
// $parse : ng表达式 {{1+2}} {{text}}
// $compile : 编译一段html字符串可以包括ng表达式
return {
restrict: "A",
scope: {
confirm: '&bravoPopoverConfirm'
},
compile: function (elem, attr) {
var confirm_template = attr['bravoPopoverConfirm'] ? '<span>' +
'<a class="btn btn-danger" ng-click="on_confirm()">Confirm</a> ' +
'<a class="btn btn-info" ng-click="on_cancel()">Cancel</a>' +
'</span>' : '';
var template =
'<div class="popover fade {{placement}} in" ng-show="popoover_show == \'in\'">' +
'<div class="arrow"></div>' +
'<h3 class="popover-title">{{title}}</h3>' +
'<div class="popover-content" ng-bind-html="content">' +
'</div>' +
'<div class="popover-content" ng-show="show_confirm_template">' +
confirm_template +
'</div>' +
'</div>';
var linker = $compile(template);
return function (scope, elem, attr) {
scope.popoover_show = "";
scope.title = attr['title'];
scope.content = $sce.trustAsHtml(attr['content']);
scope.placement = attr['placement'] ? attr['placement'] : 'top';
scope.trigger = attr['bravoPopoverConfirm'] ? 'click' : attr['trigger'];
scope.show_confirm_template = attr['bravoPopoverConfirm'] ? true : false;
var tooltip = linker(scope, function (o) {
elem.after(o);
});
tooltip.css({ top: 0, left: 0, display: 'block' });
if (!scope.trigger || scope.trigger == 'click') {
elem.on('click', function (event) {
toggle();
});
} else {
var eventIn = scope.trigger == 'hover' ? 'mouseenter' : 'focus';
var eventOut = scope.trigger == 'hover' ? 'mouseleave' : 'blur';
elem.on(eventIn, function (event) {
show_popover();
});
elem.on(eventOut, function () {
hide_popover();
});
}
var toggle = function() {
scope.popoover_show == 'in'? hide_popover() : show_popover();
render_css(tooltip);
};
var show_popover = function() {
scope.popoover_show = "in";
scope.$apply();
render_css(tooltip);
};
var hide_popover = function() {
scope.popoover_show = "";
scope.$apply();
};
var render_css = function (scope_element) {
var ttPosition = $position.positionElements(elem, scope_element, scope.placement, false);
ttPosition.top += 'px';
ttPosition.left += 'px';
// Now set the calculated positioning.
scope_element.css( ttPosition );
};
render_css(tooltip);
scope.on_cancel = function () {
scope.popoover_show = "";
};
scope.on_confirm = function () {
scope.confirm();
scope.popoover_show = "";
};
}
}
};
})
.factory('$position', ['$document', '$window', function ($document, $window) {
function getStyle(el, cssprop) {
if (el.currentStyle) { //IE
return el.currentStyle[cssprop];
} else if ($window.getComputedStyle) {
return $window.getComputedStyle(el)[cssprop];
}
// finally try and get inline style
return el.style[cssprop];
}
/**
* Checks if a given element is statically positioned
* @param element - raw DOM element
*/
function isStaticPositioned(element) {
return (getStyle(element, 'position') || 'static' ) === 'static';
}
/**
* returns the closest, non-statically positioned parentOffset of a given element
* @param element
*/
var parentOffsetEl = function (element) {
var docDomEl = $document[0];
var offsetParent = element.offsetParent || docDomEl;
while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) {
offsetParent = offsetParent.offsetParent;
}
return offsetParent || docDomEl;
};
return {
/**
* Provides read-only equivalent of jQuery's position function:
* http://api.jquery.com/position/
*/
position: function (element) {
var elBCR = this.offset(element);
var offsetParentBCR = { top: 0, left: 0 };
var offsetParentEl = parentOffsetEl(element[0]);
if (offsetParentEl != $document[0]) {
offsetParentBCR = this.offset(angular.element(offsetParentEl));
offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
}
var boundingClientRect = element[0].getBoundingClientRect();
return {
width: boundingClientRect.width || element.prop('offsetWidth'),
height: boundingClientRect.height || element.prop('offsetHeight'),
top: elBCR.top - offsetParentBCR.top,
left: elBCR.left - offsetParentBCR.left
};
},
/**
* Provides read-only equivalent of jQuery's offset function:
* http://api.jquery.com/offset/
*/
offset: function (element) {
var boundingClientRect = element[0].getBoundingClientRect();
return {
width: boundingClientRect.width || element.prop('offsetWidth'),
height: boundingClientRect.height || element.prop('offsetHeight'),
top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop),
left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)
};
},
/**
* Provides coordinates for the targetEl in relation to hostEl
*/
positionElements: function (hostEl, targetEl, positionStr, appendToBody) {
var positionStrParts = positionStr.split('-');
var pos0 = positionStrParts[0], pos1 = positionStrParts[1] || 'center';
var hostElPos,
targetElWidth,
targetElHeight,
targetElPos;
hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl);
targetElWidth = targetEl.prop('offsetWidth');
targetElHeight = targetEl.prop('offsetHeight');
var shiftWidth = {
center: function () {
return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
},
left: function () {
return hostElPos.left;
},
right: function () {
return hostElPos.left + hostElPos.width;
}
};
var shiftHeight = {
center: function () {
return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;
},
top: function () {
return hostElPos.top;
},
bottom: function () {
return hostElPos.top + hostElPos.height;
}
};
switch (pos0) {
case 'right':
targetElPos = {
top: shiftHeight[pos1](),
left: shiftWidth[pos0]()
};
break;
case 'left':
targetElPos = {
top: shiftHeight[pos1](),
left: hostElPos.left - targetElWidth
};
break;
case 'bottom':
targetElPos = {
top: shiftHeight[pos0](),
left: shiftWidth[pos1]()
};
break;
default:
targetElPos = {
top: hostElPos.top - targetElHeight,
left: shiftWidth[pos1]()
};
break;
}
return targetElPos;
}
};
}]);

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -0,0 +1,106 @@
var scApp = angular.module("scApp", ["ngRoute", "ngResource",
"bravoUiAlert", "bravoUiPopover"
]);
scApp.config(["$routeProvider", function($routeProvider){
$routeProvider.otherwise({redirectTo:"/connect"})
.when("/connect", {templateUrl:"views/connect.html", controller:"CSCConnect"})
.when("/vhosts", {templateUrl:"views/vhosts.html", controller:"CSCVhosts"})
.when("/vhosts/:id", {templateUrl:"views/vhost.html", controller:"CSCVhost"})
.when("/streams", {templateUrl:"views/streams.html", controller:"CSCStreams"})
.when("/streams/:id", {templateUrl:"views/stream.html", controller:"CSCStream"})
.when("/clients", {templateUrl:"views/clients.html", controller:"CSCClients"})
.when("/clients/:id", {templateUrl:"views/client.html", controller:"CSCClient"})
.when("/configs", {templateUrl:"views/configs.html", controller:"CSCConfigs"})
.when("/configs/:id", {templateUrl:"views/config.html", controller:"CSCConfig"})
.when("/dvr", {templateUrl:"views/dvrs.html", controller:"CSCDvrs"})
.when("/dvr/:vid/:sid/:app/:stream", {templateUrl:"views/dvr.html", controller:"CSCDvr"})
.when("/summaries", {templateUrl:"views/summary.html", controller:"CSCSummary"});
}]);
scApp.filter("sc_filter_time", function(){
return function(v){
var s = "";
if (v > 3600 * 24) {
s = Number(v / 3600 / 24).toFixed(0) + "天 ";
v = v % (3600 * 24);
}
s += relative_seconds_to_HHMMSS(v);
return s;
};
});
scApp.filter("sc_filter_yesno", function(){
return function(v){
return v? "是":"否";
};
});
scApp.filter("sc_filter_enabled", function(){
return function(v){
return v? "开启":"关闭";
};
});
scApp.filter("sc_filter_yn", function(){
return function(v){
return v? "Y":"N";
};
});
scApp.filter("sc_filter_has_stream", function(){
return function(v){
return v? "有流":"无流";
};
});
scApp.filter("sc_filter_ctype", function(){
return function(v){
return v? "推流":"播放";
};
});
scApp.filter("sc_filter_obj", function(){
return function(v) {
return v !== undefined? v : "未设置";
};
});
scApp.filter("sc_filter_security", function(){
return function(v) {
var action = v.action === "allow"? "允许":"禁止";
var method = v.method === "all"? "任何操作": (v.method === "publish"? "推流":"播放");
var entry = v.entry === "all"? "所有人" : v.entry;
return action + " " + entry + " " + method;
}
});
var scDirectiveTemplate = ''
+ '<td class="{{data.error| sc_filter_style_error}}">'
+ '{{key}}'
+ '</td>'
+ '<td colspan="{{editing? 2:0}}" title="{{data.value}}" class="{{data.error| sc_filter_style_error}}">'
+ '<div class="form-inline">'
+ '<span class="{{!data.error && data.value == undefined?\'label\':\'\'}}" ng-show="!editing">'
+ '<span ng-show="bool == \'true\' && data.value != undefined">{{data.value| sc_filter_enabled}}</span>'
+ '<span ng-show="bool != \'true\' || data.value == undefined">{{data.value| sc_filter_obj| sc_filter_less}}</span>'
+ '</span> '
+ '<input type="text" class="{{span}} inline" ng-show="editing && bool != \'true\' && !select" ng-model="data.value"> '
+ '<label class="checkbox" ng-show="editing && bool == \'true\'"><input type="checkbox" ng-model="data.value">开启</label> '
+ '<select ng-model="data.value" ng-options="s as s for s in selects" ng-show="editing && select"></select>'
+ '<a href="javascript:void(0)" ng-click="load_default()" ng-show="editing && default != undefined" title="使用默认值">使用默认值</a> '
+ '</div>'
+ '<div ng-show="editing">{{desc}}</div>'
+ '</td>'
+ '<td ng-show="!editing" class="{{data.error| sc_filter_style_error}}">'
+ '{{desc}}'
+ '</td>'
+ '<td class="span1 {{data.error| sc_filter_style_error}}">'
+ '<a href="javascript:void(0)" ng-click="edit()" ng-show="!editing" title="修改">修改</a> '
+ '<a bravo-popover href="javascript:void(0)"'
+ 'data-content="请确认是否修改?" data-title="请确认" data-placement="left"'
+ 'bravo-popover-confirm="commit()" ng-show="editing">'
+ '提交'
+ '</a> '
+ '<a href="javascript:void(0)" ng-click="cancel()" ng-show="editing" title="取消">放弃</a> '
+ '</td>';

View file

@ -0,0 +1,23 @@
/* Footer
-------------------------------------------------- */
.footer {
text-align: center;
padding: 30px 0;
margin-top: 10px;
border-top: 1px solid #e5e5e5;
background-color: #f5f5f5;
}
.footer p {
margin-bottom: 0;
color: #777;
}
.footer-links {
margin: 10px 0;
}
.footer-links li {
display: inline;
padding: 0 2px;
}
.footer-links li:first-child {
padding-left: 0;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,107 @@
var scApp = angular.module("scApp", ["ngRoute", "ngResource",
"bravoUiAlert", "bravoUiPopover"
]);
scApp.config(["$routeProvider", function($routeProvider){
$routeProvider.otherwise({redirectTo:"/connect"})
.when("/connect", {templateUrl:"views/connect_en.html", controller:"CSCConnect"})
.when("/vhosts", {templateUrl:"views/vhosts_en.html", controller:"CSCVhosts"})
.when("/vhosts/:id", {templateUrl:"views/vhost_en.html", controller:"CSCVhost"})
.when("/streams", {templateUrl:"views/streams_en.html", controller:"CSCStreams"})
.when("/streams/:id", {templateUrl:"views/stream_en.html", controller:"CSCStream"})
.when("/clients", {templateUrl:"views/clients_en.html", controller:"CSCClients"})
.when("/clients/:id", {templateUrl:"views/client_en.html", controller:"CSCClient"})
.when("/configs", {templateUrl:"views/configs_en.html", controller:"CSCConfigs"})
.when("/configs/:id", {templateUrl:"views/config_en.html", controller:"CSCConfig"})
.when("/dvr", {templateUrl:"views/dvrs_en.html", controller:"CSCDvrs"})
.when("/dvr/:vid/:sid/:app/:stream", {templateUrl:"views/dvr_en.html", controller:"CSCDvr"})
.when("/summaries", {templateUrl:"views/summary_en.html", controller:"CSCSummary"});
}]);
scApp.filter("sc_filter_time", function(){
return function(v){
var s = "";
if (v > 3600 * 24) {
s = Number(v / 3600 / 24).toFixed(0) + "d ";
v = v % (3600 * 24);
}
s += relative_seconds_to_HHMMSS(v);
return s;
};
});
scApp.filter("sc_filter_yesno", function(){
return function(v){
return v? "Yes":"No";
};
});
scApp.filter("sc_filter_enabled", function(){
return function(v){
return v? "Enabled":"Disabled";
};
});
scApp.filter("sc_filter_yn", function(){
return function(v){
return v? "Y":"N";
};
});
scApp.filter("sc_filter_has_stream", function(){
return function(v){
return v? "Y":"N";
};
});
scApp.filter("sc_filter_ctype", function(){
return function(v){
return v? "Publish":"Play";
};
});
scApp.filter("sc_filter_obj", function(){
return function(v) {
return v !== undefined? v : "Unknown";
};
});
scApp.filter("sc_filter_security", function(){
return function(v) {
var action = v.action === "allow"? "Allow":"Denied";
var method = v.method === "all"? "Any": (v.method === "publish"? "Publish":"Play");
var entry = v.entry === "all"? "All" : v.entry;
return action + " " + entry + " " + method;
}
});
var scDirectiveTemplate = ''
+ '<td class="{{data.error| sc_filter_style_error}}">'
+ '{{key}}'
+ '</td>'
+ '<td colspan="{{editing? 2:0}}" title="{{data.value}}" class="{{data.error| sc_filter_style_error}}">'
+ '<div class="form-inline">'
+ '<span class="{{!data.error && data.value == undefined?\'label\':\'\'}}" ng-show="!editing">'
+ '<span ng-show="bool == \'true\' && data.value != undefined">{{data.value| sc_filter_enabled}}</span>'
+ '<span ng-show="bool != \'true\' || data.value == undefined">{{data.value| sc_filter_obj| sc_filter_less}}</span>'
+ '</span> '
+ '<input type="text" class="{{span}} inline" ng-show="editing && bool != \'true\' && !select" ng-model="data.value"> '
+ '<label class="checkbox" ng-show="editing && bool == \'true\'"><input type="checkbox" ng-model="data.value">Enable</label> '
+ '<select ng-model="data.value" ng-options="s as s for s in selects" ng-show="editing && select"></select>'
+ '<a href="javascript:void(0)" ng-click="load_default()" ng-show="editing && default != undefined" title="Use Default Values">Restore</a> '
+ '</div>'
+ '<div ng-show="editing">{{desc}}</div>'
+ '</td>'
+ '<td ng-show="!editing" class="{{data.error| sc_filter_style_error}}">'
+ '{{desc}}'
+ '</td>'
+ '<td class="span1 {{data.error| sc_filter_style_error}}">'
+ '<a href="javascript:void(0)" ng-click="edit()" ng-show="!editing" title="Mofity it">Update</a> '
+ '<a bravo-popover href="javascript:void(0)"'
+ 'data-content="Confirm Update?" data-title="Please Confirm" data-placement="left"'
+ 'bravo-popover-confirm="commit()" ng-show="editing">'
+ 'Submit'
+ '</a> '
+ '<a href="javascript:void(0)" ng-click="cancel()" ng-show="editing" title="Cancel">Cancel</a> '
+ '</td>';

View file

@ -0,0 +1,685 @@
// winlin.utility.js
/**
* common utilities
* depends: jquery1.10
* https://gitee.com/winlinvip/codes/rpn0c2ewbomj81augzk4y59
* @see: http://blog.csdn.net/win_lin/article/details/17994347
* v 1.0.22
*/
/**
* padding the output.
* padding(3, 5, '0') is 00003
* padding(3, 5, 'x') is xxxx3
* @see http://blog.csdn.net/win_lin/article/details/12065413
*/
function padding(number, length, prefix) {
if(String(number).length >= length){
return String(number);
}
return padding(prefix+number, length, prefix);
}
/**
* extends system array, to remove all specified elem.
* @param arr the array to remove elem from.
* @param elem the elem to remove.
* @remark all elem will be removed.
* for example,
* arr = [10, 15, 20, 30, 20, 40]
* system_array_remove(arr, 10) // arr=[15, 20, 30, 20, 40]
* system_array_remove(arr, 20) // arr=[15, 30, 40]
*/
function system_array_remove(arr, elem) {
if (!arr) {
return;
}
var removed = true;
var i = 0;
while (removed) {
removed = false;
for (; i < arr.length; i++) {
if (elem == arr[i]) {
arr.splice(i, 1);
removed = true;
break;
}
}
}
}
/**
* whether the array contains specified element.
* @param arr the array to find.
* @param elem_or_function the element value or compare function.
* @returns true contains elem; otherwise false.
* for example,
* arr = [10, 15, 20, 30, 20, 40]
* system_array_contains(arr, 10) // true
* system_array_contains(arr, 11) // false
* system_array_contains(arr, function(elem){return elem == 30;}); // true
* system_array_contains(arr, function(elem){return elem == 60;}); // false
*/
function system_array_contains(arr, elem_or_function) {
return system_array_get(arr, elem_or_function) != null;
}
/**
* get the specified element from array
* @param arr the array to find.
* @param elem_or_function the element value or compare function.
* @returns the matched elem; otherwise null.
* for example,
* arr = [10, 15, 20, 30, 20, 40]
* system_array_get(arr, 10) // 10
* system_array_get(arr, 11) // null
* system_array_get(arr, function(elem){return elem == 30;}); // 30
* system_array_get(arr, function(elem){return elem == 60;}); // null
*/
function system_array_get(arr, elem_or_function) {
for (var i = 0; i < arr.length; i++) {
if (typeof elem_or_function == "function") {
if (elem_or_function(arr[i])) {
return arr[i];
}
} else {
if (elem_or_function == arr[i]) {
return arr[i];
}
}
}
return null;
}
/**
* to iterate on array.
* @param arr the array to iterate on.
* @param pfn the function to apply on it. return false to break loop.
* for example,
* arr = [10, 15, 20, 30, 20, 40]
* system_array_foreach(arr, function(elem, index){
* console.log('index=' + index + ',elem=' + elem);
* });
* @return true when iterate all elems.
*/
function system_array_foreach(arr, pfn) {
if (!pfn) {
return false;
}
for (var i = 0; i < arr.length; i++) {
if (!pfn(arr[i], i)) {
return false;
}
}
return true;
}
/**
* whether the str starts with flag.
*/
function system_string_startswith(str, flag) {
if (typeof flag == "object" && flag.constructor == Array) {
for (var i = 0; i < flag.length; i++) {
if (system_string_startswith(str, flag[i])) {
return true;
}
}
}
return str && flag && str.length >= flag.length && str.indexOf(flag) == 0;
}
/**
* whether the str ends with flag.
*/
function system_string_endswith(str, flag) {
if (typeof flag == "object" && flag.constructor == Array) {
for (var i = 0; i < flag.length; i++) {
if (system_string_endswith(str, flag[i])) {
return true;
}
}
}
return str && flag && str.length >= flag.length && str.indexOf(flag) == str.length - flag.length;
}
/**
* trim the start and end of flag in str.
* @param flag a string to trim.
*/
function system_string_trim(str, flag) {
if (!flag || !flag.length || typeof flag != "string") {
return str;
}
while (system_string_startswith(str, flag)) {
str = str.substr(flag.length);
}
while (system_string_endswith(str, flag)) {
str = str.substr(0, str.length - flag.length);
}
return str;
}
/**
* array sort asc, for example:
* [a, b] in [10, 11, 9]
* then sort to: [9, 10, 11]
* Usage, for example:
obj.data.data.sort(function(a, b){
return array_sort_asc(a.metadata.meta_id, b.metadata.meta_id);
});
* @see: http://blog.csdn.net/win_lin/article/details/17994347
* @remark, if need desc, use -1*array_sort_asc(a,b)
*/
function array_sort_asc(elem_a, elem_b) {
if (elem_a > elem_b) {
return 1;
}
return (elem_a < elem_b)? -1 : 0;
}
function array_sort_desc(elem_a, elem_b) {
return -1 * array_sort_asc(elem_a, elem_b);
}
function system_array_sort_asc(elem_a, elem_b) {
return array_sort_asc(elem_a, elem_b);
}
function system_array_sort_desc(elem_a, elem_b) {
return -1 * array_sort_asc(elem_a, elem_b);
}
/**
* parse the query string to object.
* parse the url location object as: host(hostname:http_port), pathname(dir/filename)
* for example, url http://192.168.1.168:1980/ui/players.html?vhost=player.vhost.com&app=test&stream=livestream
* parsed to object:
{
host : "192.168.1.168:1980",
hostname : "192.168.1.168",
http_port : 1980,
pathname : "/ui/players.html",
dir : "/ui",
filename : "/players.html",
vhost : "player.vhost.com",
app : "test",
stream : "livestream"
}
* @see: http://blog.csdn.net/win_lin/article/details/17994347
*/
function parse_query_string(){
var obj = {};
// add the uri object.
// parse the host(hostname:http_port), pathname(dir/filename)
obj.host = window.location.host;
obj.hostname = window.location.hostname;
obj.http_port = (window.location.port == "")? 80:window.location.port;
obj.pathname = window.location.pathname;
if (obj.pathname.lastIndexOf("/") <= 0) {
obj.dir = "/";
obj.filename = "";
} else {
obj.dir = obj.pathname.substr(0, obj.pathname.lastIndexOf("/"));
obj.filename = obj.pathname.substr(obj.pathname.lastIndexOf("/"));
}
// pure user query object.
obj.user_query = {};
// parse the query string.
var query_string = String(window.location.search).replace(" ", "").split("?")[1];
if(query_string === undefined){
query_string = String(window.location.hash).replace(" ", "").split("#")[1];
if(query_string === undefined){
return obj;
}
}
__fill_query(query_string, obj);
return obj;
}
function __fill_query(query_string, obj) {
// pure user query object.
obj.user_query = {};
if (query_string.length === 0) {
return;
}
// split again for angularjs.
if (query_string.indexOf("?") >= 0) {
query_string = query_string.split("?")[1];
}
var queries = query_string.split("&");
for (var i = 0; i < queries.length; i++) {
var elem = queries[i];
var query = elem.split("=");
obj[query[0]] = query[1];
obj.user_query[query[0]] = query[1];
}
// alias domain for vhost.
if (obj.domain) {
obj.vhost = obj.domain;
}
}
/**
* parse the rtmp url,
* for example: rtmp://demo.srs.com:1935/live...vhost...players/livestream
* @return object {server, port, vhost, app, stream}
* for exmaple, rtmp_url is rtmp://demo.srs.com:1935/live...vhost...players/livestream
* parsed to object:
{
server: "demo.srs.com",
port: 1935,
vhost: "players",
app: "live",
stream: "livestream"
}
*/
function parse_rtmp_url(rtmp_url) {
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
var a = document.createElement("a");
a.href = rtmp_url.replace("rtmp://", "http://")
.replace("webrtc://", "http://")
.replace("rtc://", "http://");
var vhost = a.hostname;
var app = a.pathname.substr(1, a.pathname.lastIndexOf("/") - 1);
var stream = a.pathname.substr(a.pathname.lastIndexOf("/") + 1);
// parse the vhost in the params of app, that srs supports.
app = app.replace("...vhost...", "?vhost=");
if (app.indexOf("?") >= 0) {
var params = app.substr(app.indexOf("?"));
app = app.substr(0, app.indexOf("?"));
if (params.indexOf("vhost=") > 0) {
vhost = params.substr(params.indexOf("vhost=") + "vhost=".length);
if (vhost.indexOf("&") > 0) {
vhost = vhost.substr(0, vhost.indexOf("&"));
}
}
}
// when vhost equals to server, and server is ip,
// the vhost is __defaultVhost__
if (a.hostname === vhost) {
var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
if (re.test(a.hostname)) {
vhost = "__defaultVhost__";
}
}
// parse the schema
var schema = "rtmp";
if (rtmp_url.indexOf("://") > 0) {
schema = rtmp_url.substr(0, rtmp_url.indexOf("://"));
}
var port = a.port;
if (!port) {
if (schema === 'http') {
port = 80;
} else if (schema === 'https') {
port = 443;
} else if (schema === 'rtmp') {
port = 1935;
}
}
var ret = {
url: rtmp_url,
schema: schema,
server: a.hostname, port: port,
vhost: vhost, app: app, stream: stream
};
__fill_query(a.search, ret);
// For webrtc API, we use 443 if page is https, or schema specified it.
if (!ret.port) {
if (schema === 'webrtc' || schema === 'rtc') {
if (ret.user_query.schema === 'https') {
ret.port = 443;
} else if (window.location.href.indexOf('https://') === 0) {
ret.port = 443;
} else {
ret.port = 80;
}
}
}
return ret;
}
/**
* get the agent.
* @return an object specifies some browser.
* for example, get_browser_agents().MSIE
* @see: http://blog.csdn.net/win_lin/article/details/17994347
*/
function get_browser_agents() {
var agent = navigator.userAgent;
/**
WindowsPC platform, Win7:
chrome 31.0.1650.63:
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36
firefox 23.0.1:
Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20100101
Firefox/23.0
safari 5.1.7(7534.57.2):
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.57.2
(KHTML, like Gecko) Version/5.1.7 Safari/534.57.2
opera 15.0.1147.153:
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36
OPR/15.0.1147.153
360 6.2.1.272:
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64;
Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729;
.NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C;
.NET4.0E)
IE 10.0.9200.16750(update: 10.0.12):
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64;
Trident/6.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729;
.NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C;
.NET4.0E)
*/
return {
// platform
Android: agent.indexOf("Android") != -1,
Windows: agent.indexOf("Windows") != -1,
iPhone: agent.indexOf("iPhone") != -1,
// Windows Browsers
Chrome: agent.indexOf("Chrome") != -1,
Firefox: agent.indexOf("Firefox") != -1,
QQBrowser: agent.indexOf("QQBrowser") != -1,
MSIE: agent.indexOf("MSIE") != -1,
// Android Browsers
Opera: agent.indexOf("Presto") != -1,
MQQBrowser: agent.indexOf("MQQBrowser") != -1
};
}
/**
* format relative seconds to HH:MM:SS,
* for example, 210s formated to 00:03:30
* @see: http://blog.csdn.net/win_lin/article/details/17994347
* @usage relative_seconds_to_HHMMSS(210)
*/
function relative_seconds_to_HHMMSS(seconds){
var date = new Date();
date.setTime(Number(seconds) * 1000);
var ret = padding(date.getUTCHours(), 2, '0')
+ ":" + padding(date.getUTCMinutes(), 2, '0')
+ ":" + padding(date.getUTCSeconds(), 2, '0');
return ret;
}
/**
* format absolute seconds to HH:MM:SS,
* for example, 1389146480s (2014-01-08 10:01:20 GMT+0800) formated to 10:01:20
* @see: http://blog.csdn.net/win_lin/article/details/17994347
* @usage absolute_seconds_to_HHMMSS(new Date().getTime() / 1000)
*/
function absolute_seconds_to_HHMMSS(seconds){
var date = new Date();
date.setTime(Number(seconds) * 1000);
var ret = padding(date.getHours(), 2, '0')
+ ":" + padding(date.getMinutes(), 2, '0')
+ ":" + padding(date.getSeconds(), 2, '0');
return ret;
}
/**
* format absolute seconds to YYYY-mm-dd,
* for example, 1389146480s (2014-01-08 10:01:20 GMT+0800) formated to 2014-01-08
* @see: http://blog.csdn.net/win_lin/article/details/17994347
* @usage absolute_seconds_to_YYYYmmdd(new Date().getTime() / 1000)
*/
function absolute_seconds_to_YYYYmmdd(seconds) {
var date = new Date();
date.setTime(Number(seconds) * 1000);
var ret = date.getFullYear()
+ "-" + padding(date.getMonth() + 1, 2, '0')
+ "-" + padding(date.getDate(), 2, '0');
return ret;
}
/**
* parse the date in str to Date object.
* @param str the date in str, format as "YYYY-mm-dd", for example, 2014-12-11
* @returns a date object.
* @usage YYYYmmdd_parse("2014-12-11")
*/
function YYYYmmdd_parse(str) {
var date = new Date();
date.setTime(Date.parse(str));
return date;
}
/**
* async refresh function call. to avoid multiple call.
* @remark AsyncRefresh is for jquery to refresh the speicified pfn in a page;
* if angularjs, use AsyncRefresh2 to change pfn, cancel previous request for angularjs use singleton object.
* @param refresh_interval the default refresh interval ms.
* @see: http://blog.csdn.net/win_lin/article/details/17994347
* the pfn can be implements as following:
var async_refresh = new AsyncRefresh(pfn, 3000);
function pfn() {
if (!async_refresh.refresh_is_enabled()) {
async_refresh.request(100);
return;
}
$.ajax({
type: 'GET', async: true, url: 'xxxxx',
complete: function(){
if (!async_refresh.refresh_is_enabled()) {
async_refresh.request(0);
} else {
async_refresh.request(async_refresh.refresh_interval);
}
},
success: function(res){
// if donot allow refresh, directly return.
if (!async_refresh.refresh_is_enabled()) {
return;
}
// render the res.
}
});
}
*/
function AsyncRefresh(pfn, refresh_interval) {
this.refresh_interval = refresh_interval;
this.__handler = null;
this.__pfn = pfn;
this.__enabled = true;
}
/**
* disable the refresher, the pfn must check the refresh state.
*/
AsyncRefresh.prototype.refresh_disable = function() {
this.__enabled = false;
}
AsyncRefresh.prototype.refresh_enable = function() {
this.__enabled = true;
}
AsyncRefresh.prototype.refresh_is_enabled = function() {
return this.__enabled;
}
/**
* start new async request
* @param timeout the timeout in ms.
* user can use the refresh_interval of the AsyncRefresh object,
* which initialized in constructor.
*/
AsyncRefresh.prototype.request = function(timeout) {
if (this.__handler) {
clearTimeout(this.__handler);
}
this.__handler = setTimeout(this.__pfn, timeout);
}
/**
* async refresh v2, support cancellable refresh, and change the refresh pfn.
* @remakr for angularjs. if user only need jquery, maybe AsyncRefresh is better.
* @see: http://blog.csdn.net/win_lin/article/details/17994347
* Usage:
bsmControllers.controller('CServers', ['$scope', 'MServer', function($scope, MServer){
async_refresh2.refresh_change(function(){
// 获取服务器列表
MServer.servers_load({}, function(data){
$scope.servers = data.data.servers;
async_refresh2.request();
});
}, 3000);
async_refresh2.request(0);
}]);
bsmControllers.controller('CStreams', ['$scope', 'MStream', function($scope, MStream){
async_refresh2.refresh_change(function(){
// 获取流列表
MStream.streams_load({}, function(data){
$scope.streams = data.data.streams;
async_refresh2.request();
});
}, 3000);
async_refresh2.request(0);
}]);
*/
function AsyncRefresh2() {
/**
* the function callback before call the pfn.
* the protype is function():bool, which return true to invoke, false to abort the call.
* null to ignore this callback.
*
* for example, user can abort the refresh by find the class popover:
* async_refresh2.on_before_call_pfn = function() {
* if ($(".popover").length > 0) {
* async_refresh2.request();
* return false;
* }
* return true;
* };
*/
this.on_before_call_pfn = null;
// use a anonymous function to call, and check the enabled when actually invoke.
this.__call = {
pfn: null,
timeout: 0,
__enabled: false,
__handler: null
};
}
// singleton
var async_refresh2 = new AsyncRefresh2();
/**
* initialize or refresh change. cancel previous request, setup new request.
* @param pfn a function():void to request after timeout. null to disable refresher.
* @param timeout the timeout in ms, to call pfn. null to disable refresher.
*/
AsyncRefresh2.prototype.initialize = function(pfn, timeout) {
this.refresh_change(pfn, timeout);
}
/**
* stop refresh, the refresh pfn is set to null.
*/
AsyncRefresh2.prototype.stop = function() {
this.__call.__enabled = false;
}
/**
* restart refresh, use previous config.
*/
AsyncRefresh2.prototype.restart = function() {
this.__call.__enabled = true;
this.request(0);
}
/**
* change refresh pfn, the old pfn will set to disabled.
*/
AsyncRefresh2.prototype.refresh_change = function(pfn, timeout) {
// cancel the previous call.
if (this.__call.__handler) {
clearTimeout(this.__handler);
}
this.__call.__enabled = false;
// setup new call.
this.__call = {
pfn: pfn,
timeout: timeout,
__enabled: true,
__handler: null
};
}
/**
* start new request, we never auto start the request,
* user must start new request when previous completed.
* @param timeout [optional] if not specified, use the timeout in initialize or refresh_change.
*/
AsyncRefresh2.prototype.request = function(timeout) {
var self = this;
var this_call = this.__call;
// clear previous timeout.
if (this_call.__handler) {
clearTimeout(this_call.__handler);
}
// override the timeout
if (timeout == undefined) {
timeout = this_call.timeout;
}
// if user disabled refresher.
if (this_call.pfn == null || timeout == null) {
return;
}
this_call.__handler = setTimeout(function(){
// cancelled by refresh_change, ignore.
if (!this_call.__enabled) {
return;
}
// callback if the handler installled.
if (self.on_before_call_pfn) {
if (!self.on_before_call_pfn()) {
return;
}
}
// do the actual call.
this_call.pfn();
}, timeout);
}

View file

@ -0,0 +1,68 @@
<!DOCTYPE html>
<html ng-app="scApp">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>SRS控制台</title>
<link rel="stylesheet" type="text/css" href="js/3rdparty/bootstrap.min.css"/>
<link rel="stylesheet" type="text/css" href="js/srs.console.css"/>
<style>
body {
padding-top: 50px;
}
</style>
<script type="text/javascript" src="js/3rdparty/angular.min.js"></script>
<script type="text/javascript" src="js/3rdparty/angular-route.min.js"></script>
<script type="text/javascript" src="js/3rdparty/angular-resource.min.js"></script>
<script type="text/javascript" src="js/winlin.utility.js"></script>
<script type="text/javascript" src="js/bravo_alert/alert.js"></script>
<script type="text/javascript" src="js/bravo_popover/popover.js"></script>
<script type="text/javascript" src="js/srs.cn.js"></script>
<script type="text/javascript" src="js/srs.console.js"></script>
</head>
<body ng-controller="CSCMain">
<img src='https://ossrs.net:8443/gif/v1/sls.gif?site=ossrs.net&path=/console/cnindex'/>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="https://github.com/ossrs/srs" target="_blank">SRS</a>
<ul class="nav">
<li class="{{'/console'| sc_filter_nav_active}}"><a href="javascript:void(0)" ng-click="gogogo('/connect')">连接SRS</a></li>
<li class="{{'/summaries'| sc_filter_nav_active}}"><a href="javascript:void(0)" ng-click="gogogo('/summaries')">概览</a></li>
<li class="{{'/vhosts'| sc_filter_nav_active}}"><a href="javascript:void(0)" ng-click="gogogo('/vhosts')">虚拟主机(Vhosts)</a></li>
<li class="{{'/streams'| sc_filter_nav_active}}"><a href="javascript:void(0)" ng-click="gogogo('/streams')">视频流</a></li>
<li class="{{'/clients'| sc_filter_nav_active}}"><a href="javascript:void(0)" ng-click="gogogo('/clients')">客户端</a></li>
<li class="{{'/configs'| sc_filter_nav_active}}"><a href="javascript:void(0)" ng-click="gogogo('/configs')">配置</a></li>
<li class="{{'/dvr'| sc_filter_nav_active}}"><a href="javascript:void(0)" ng-click="gogogo('/dvr')">DVR</a></li>
<li><a href="javascript:void(0)" ng-click="redirect('ng_index.html', 'en_index.html')">English</a></li>
<li>
<a href="https://github.com/ossrs/srs-console">
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/ossrs/srs-console?style=social">
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="container-fluid">
<div ng-view></div>
<div bravo-alert ng-repeat="log in logs" class="alert fade in {{log.level|sc_filter_log_level}}">
<button type="button" class="close" data-dismiss="alert">×</button>
<strong>{{log.level}}:</strong> {{log.msg}}
</div>
</div>
<footer class="footer">
<div class="container">
<ul class="footer-links">
<li>&copy; SRS 2020</li>
<li class="muted">|</li>
<li><a href="http://ossrs.net/" target="_blank">Releases</a></li>
<li class="muted">|</li>
<li><a href="https://github.com/ossrs/srs" target="_blank">SRS</a></li>
<li class="muted">|</li>
<li><a href="https://github.com/ossrs/srs-console" target="_blank">Github</a></li>
</ul>
</div>
</footer>
</body>
</html>

View file

@ -0,0 +1,25 @@
<div>
<div class="accordion">
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" href="javascript:void(0)">
客户端-{{client.id}}
</a>
</div>
<div id="collapseOne" class="accordion-body collapse in">
<div class="accordion-inner">
<p>ID: {{client.id}}</p>
<p>IP: {{client.ip}}</p>
<p><a href="javascript:void(0)" ng-click="gogogo('/vhosts/' + client.vhost)">{{client.vhost}}</a></p>
<p><a href="javascript:void(0)" ng-click="gogogo('/streams/' + client.stream)">{{client.stream}}</a></p>
<p>Type: {{client.publish| sc_filter_ctype}}</p>
<p>Alive: {{client.alive| sc_filter_time}}</p>
<p>TcUrl: {{client.tcUrl}}</p>
<p>PageUrl: {{client.pageUrl}}</p>
<p>SwfUrl: {{client.swfUrl}}</p>
<p><a ng-click="kickoff(client)" href="javascript:void(0)">踢Ta</a></p>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,25 @@
<div>
<div class="accordion">
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" href="javascript:void(0)">
Client-{{client.id}}
</a>
</div>
<div id="collapseOne" class="accordion-body collapse in">
<div class="accordion-inner">
<p>ID: {{client.id}}</p>
<p>IP: {{client.ip}}</p>
<p><a href="javascript:void(0)" ng-click="gogogo('/vhosts/' + client.vhost)">{{client.vhost}}</a></p>
<p><a href="javascript:void(0)" ng-click="gogogo('/streams/' + client.stream)">{{client.stream}}</a></p>
<p>Type: {{client.publish| sc_filter_ctype}}</p>
<p>Alive: {{client.alive| sc_filter_time}}</p>
<p>TcUrl: {{client.tcUrl}}</p>
<p>PageUrl: {{client.pageUrl}}</p>
<p>SwfUrl: {{client.swfUrl}}</p>
<p><a ng-click="kickoff(client)" href="javascript:void(0)">Kickoff It</a></p>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,39 @@
<div>
<div class="accordion">
<div class="accordion-group">
<div class="accordion-heading" sc-collapse="in">
<a class="accordion-toggle" href="javascript:void(0)">
客户端(Clients)列表
</a>
</div>
<div id="collapseOne" class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-bordered">
<tr>
<th>ID</th>
<th>IP</th>
<th>Vhost</th>
<th>Stream</th>
<th>类型</th>
<th>时长</th>
<th>TcUrl</th>
<th>管理</th>
</tr>
<tr ng-repeat="client in clients">
<td><a href="javascript:void(0)" ng-click="gogogo('/clients/' + client.id)">{{client.id}}</a></td>
<td>{{client.ip}}</td>
<td><a href="javascript:void(0)" ng-click="gogogo('/vhosts/' + client.vhost)">{{client.vhost}}</a></td>
<td><a href="javascript:void(0)" ng-click="gogogo('/streams/' + client.stream)">{{client.stream}}</a></td>
<td>{{client.publish| sc_filter_ctype}}</td>
<td>{{client.alive| sc_filter_time}}</td>
<td>{{client.tcUrl}}</td>
<td>
<a ng-click="kickoff(client)" href="javascript:void(0)">踢Ta</a>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,39 @@
<div>
<div class="accordion">
<div class="accordion-group">
<div class="accordion-heading" sc-collapse="in">
<a class="accordion-toggle" href="javascript:void(0)">
System Clients
</a>
</div>
<div id="collapseOne" class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-bordered">
<tr>
<th>ID</th>
<th>IP</th>
<th>Vhost</th>
<th>Stream</th>
<th>Type</th>
<th>Duration</th>
<th>TcUrl</th>
<th>Manage</th>
</tr>
<tr ng-repeat="client in clients">
<td><a href="javascript:void(0)" ng-click="gogogo('/clients/' + client.id)">{{client.id}}</a></td>
<td>{{client.ip}}</td>
<td><a href="javascript:void(0)" ng-click="gogogo('/vhosts/' + client.vhost)">{{client.vhost}}</a></td>
<td><a href="javascript:void(0)" ng-click="gogogo('/streams/' + client.stream)">{{client.stream}}</a></td>
<td>{{client.publish| sc_filter_ctype}}</td>
<td>{{client.alive| sc_filter_time}}</td>
<td>{{client.tcUrl}}</td>
<td>
<a ng-click="kickoff(client)" href="javascript:void(0)">Kickoff It</a>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,452 @@
<div>
<div class="accordion">
<div class="accordion-group" ng-show="!support_raw_api">
<div class="accordion-heading" sc-collapse="in">
<a class="accordion-toggle" href="javascript:void(0)">
HTTP RAW API
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<div class="alert alert-block alert-danger" ng-show="warn_raw_api">
该服务器不支持HTTP RAW API或者配置中禁用了该功能。参考<a href='https://github.com/ossrs/srs/wiki/v3_CN_HTTPApi#http-raw-api'>WIKI</a>
</div>
<table class="table table-striped table-hover table-bordered" ng-show="http_api">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty scp-key="http_api.enabled" scp-value="http_api.enabled" scp-bool="true"
scp-desc="是否开启HTTP API开启后就可以访问SRS提供的API管理服务器。默认: {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty scp-key="http_api.listen" scp-value="http_api.listen"
scp-desc="HTTP API的侦听地址格式是&lt;[address:]port&gt;。默认: 1985">
</tr>
<tr sc-pretty scp-key="http_api.crossdomain" scp-value="http_api.crossdomain" scp-bool="true"
scp-desc="是否允许JS跨域开启后JS可以直接跨域(还可以通过JSONP访问)。默认: {{true| sc_filter_enabled}}">
</tr>
<tr sc-pretty scp-key="http_api.raw_api.enabled" scp-value="http_api.raw_api.enabled" scp-bool="true"
scp-desc="是否开启HTTP RAW API允许API修改服务器配置。默认: {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty scp-key="http_api.raw_api.allow_reload" scp-value="http_api.raw_api.allow_reload" scp-bool="true"
scp-desc="是否允许API进行Reload操作。默认: {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty scp-key="http_api.raw_api.allow_query" scp-value="http_api.raw_api.allow_query" scp-bool="true"
scp-desc="是否允许API进行Query查询操作。默认: {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty scp-key="http_api.raw_api.allow_update" scp-value="http_api.raw_api.allow_update" scp-bool="true"
scp-desc="是否允许API进行Update更新操作。默认: {{false| sc_filter_enabled}}">
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="in">
<a class="accordion-toggle" href="javascript:void(0)">
服务器API配置(http_api)
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.http_api.enabled" scp-bool="true"
scp-desc="是否开启HTTP API开启后就可以访问SRS提供的API管理服务器。默认: {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.http_api.listen.value"
scp-desc="HTTP API的侦听地址格式是&lt;[address:]port&gt;。默认: 1985">
</tr>
<tr sc-pretty2 scp-data="global.http_api.crossdomain" scp-bool="true"
scp-desc="是否允许JS跨域开启后JS可以直接跨域(还可以通过JSONP访问)。默认: {{true| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.http_api.raw_api.enabled" scp-bool="true"
scp-desc="是否开启HTTP RAW API允许API修改服务器配置。默认: {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.http_api.raw_api.allow_reload" scp-bool="true"
scp-desc="是否允许API进行Reload操作。默认: {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.http_api.raw_api.allow_query" scp-bool="true"
scp-desc="是否允许API进行Query查询操作。默认: {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.http_api.raw_api.allow_update" scp-bool="true"
scp-desc="是否允许API进行Update更新操作。默认: {{false| sc_filter_enabled}}">
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="out">
<a class="accordion-toggle" href="javascript:void(0)">
服务器全局配置
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-directive scd-data="global.listen" scd-span="span3"
scd-desc="RTMP服务器侦听的端口提供RTMP服务格式是&lt;[address:]port&gt;[,&lt;addressN&gt;portN]"
scd-array="true" scd-default="1935"
scd-submit="submit(global.listen)">
</tr>
<tr sc-directive scd-data="global.pid" scd-span="span3"
scd-desc="服务器使用的PID文件每个进程必须用一个否则无法启动。默认: ./objs/srs.pid"
scd-default="./objs/srs.pid"
scd-submit="submit(global.pid)">
</tr>
<tr sc-directive scd-data="global.chunk_size" scd-span="span3"
scd-desc="默认的ChunkSize即RTMP分包的大小。默认: 60000"
scd-default="60000"
scd-submit="submit(global.chunk_size)">
</tr>
<tr sc-directive scd-data="global.ff_log_dir" scd-span="span4"
scd-desc="默认的FFMPEG日志目录/dev/null禁用日志。默认: ./objs"
scd-default="./objs"
scd-submit="submit(global.ff_log_dir)">
</tr>
<tr sc-directive scd-data="global.srs_log_tank" scd-span="span3"
scd-desc="系统的日志容器,即存储方式。默认: file"
scd-default="file", scd-select="file,console"
scd-submit="submit(global.srs_log_tank)">
</tr>
<tr sc-directive scd-data="global.srs_log_level" scd-span="span3"
scd-desc="系统的日志级别,低于该级别的日志不会打印。默认: trace"
scd-default="trace" scd-select="verbose,info,trace,warn,error"
scd-submit="submit(global.srs_log_level)">
</tr>
<tr sc-directive scd-data="global.srs_log_file" scd-span="span4"
scd-desc="当日志容器为file即日志写入文件时日志文件的路径。默认: ./objs/srs.log"
scd-default="./objs/srs.log"
scd-submit="submit(global.srs_log_file)"
ng-if="global.srs_log_tank.value == 'file'">
</tr>
<tr sc-directive scd-data="global.max_connections" scd-span="span3"
scd-desc="系统的最大连接数,超过后将拒绝新连接。默认: 1000"
scd-default="1000"
scd-submit="submit(global.max_connections)">
</tr>
<tr sc-pretty2 scp-data="global.daemon" scp-bool="true"
scp-desc="是否以后台启动SRS。默认: {{true| sc_filter_yesno}}">
</tr>
<tr sc-directive scd-data="global.utc_time" scd-span="span4"
scd-desc="是否启用UTC时间格式影响日志的时间和路径中包含时间变量的配置。默认: {{false| sc_filter_yesno}}"
scd-default="false" scd-bool="true"
scd-submit="submit(global.utc_time)">
</tr>
<tr sc-directive scd-data="global.pithy_print_ms" scd-span="span3"
scd-desc="简化形式的日志打印间隔,单位: 毫秒。默认: 10000"
scd-default="10000" scd-select="1000,3000,5000,10000,30000"
scd-submit="submit(global.pithy_print_ms)">
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="out">
<a class="accordion-toggle" href="javascript:void(0)">
虚拟主机(Vhosts)列表, 共配置有{{global.nb_vhosts.value}}个Vhost
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>
<a href="javscript:void(0)" ng-click="new_vhost()" class="icon-plus"></a>
</th>
<th>主机名称</th>
<th>开启</th>
<th></th>
<th>DVR</th>
<th>HTTP</th>
<th>FLV</th>
<th>HLS</th>
<th>HDS</th>
<th>回调</th>
<th ng-show="false">Exec</th>
<th ng-show="false">BWT</th>
<th>转发</th>
<th>安全</th>
<th ng-show="false">盗链</th>
<th ng-show="false">MR</th>
<th ng-show="false">RTC</th>
<th ng-show="false">GOP</th>
<th ng-show="false">TcpNoDelay</th>
<th ng-show="false">MixCorrect</th>
<th ng-show="false">TimeJitter</th>
<th ng-show="false">ATC</th>
<th>转码</th>
<th>采集</th>
<th>
<a href="javascript:void(0)" ng-click="new_vhost()">添加</a>
</th>
</tr>
<tr ng-repeat="vhost in global.vhosts">
<td><a href="#/vhosts/{{vhost.id}}" ng-show="vhost.id">{{vhost.id}}</a><span ng-show="!vhost.id">无流</span> </td>
<td colspan="{{vhost.editable?13:1}}">
<span ng-show="!vhost.editable">{{vhost.name}}</span>
<span ng-show="vhost.editable">
<input class="text span5" ng-model="vhost.name"><br/>
请输入vhost的名称。
</span>
</td>
<td ng-show="!vhost.editable">{{vhost.enabled| sc_filter_yesno}}</td>
<td ng-show="!vhost.editable">{{vhost.origin| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.dvr| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.http_static| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.http_remux| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.hls| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.hds| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.http_hooks| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.exec| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.bandcheck| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.forward| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.security| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.refer| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.mr| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.min_latency| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.gop_cache| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.tcp_nodelay| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.mix_correct| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.time_jitter| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.atc| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.transcode| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.ingest| sc_filter_yn}}</td>
<td>
<span ng-show="vhost.editable">
<!-- vhost exists in server -->
<span ng-show="vhost.enabled != undefined">
<a bravo-popover href="javascript:void(0)"
data-content="请确认是否提交{{vhost.name}}?" data-title="请确认" data-placement="left"
bravo-popover-confirm="update_vhost(vhost)">
提交
</a>
<a href="javascript:void(0)" ng-click="cancel_vhost(vhost)">放弃</a>
</span>
<!-- vhost in adding. -->
<span ng-show="vhost.enabled == undefined">
<a bravo-popover href="javascript:void(0)"
data-content="请确认是否提交{{vhost.name}}?" data-title="请确认" data-placement="left"
bravo-popover-confirm="add_vhost(vhost)">
提交
</a>
<a href="javascript:void(0)" ng-click="abort_vhost(vhost)">放弃</a>
</span>
</span>
<span ng-show="!vhost.editable">
<span ng-show="vhost.enabled">
<span>编辑</span>
<a bravo-popover href="javascript:void(0)"
data-content="请确认是否禁用{{vhost.name}}?" data-title="请确认" data-placement="left"
bravo-popover-confirm="disable_vhost(vhost)">
禁用
</a>
<span>删除</span>
</span>
<span ng-show="!vhost.enabled">
<a href="javascript:void(0)" ng-click="edit_vhost(vhost)">编辑</a>
<a bravo-popover href="javascript:void(0)"
data-content="请确认是否启用{{vhost.name}}?" data-title="请确认" data-placement="left"
bravo-popover-confirm="enable_vhost(vhost)">
启用
</a>
<a bravo-popover href="javascript:void(0)"
data-content="请确认是否删除{{vhost.name}}?" data-title="请确认" data-placement="left"
bravo-popover-confirm="delete_vhost(vhost)">
删除
</a>
</span>
<a href="#/configs/{{vhost.name}}" ng-show="!vhost.editable">详细</a>
</span>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="hide">
<a class="accordion-toggle" href="javascript:void(0)">
Kafka集群配置(kafka)
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.kafka.enabled" scp-bool="true"
scp-desc="是否开启Kafka开启后SRS将数据汇报给Kafka集群。默认: {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.kafka.brokers"
scp-desc="Broker列表首次连接到Broker获取集群信息格式是&lt;ip:port&gt;。">
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="hide">
<a class="accordion-toggle" href="javascript:void(0)">
服务器心跳汇报(heartbeat)
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.heartbeat.enabled"
scp-desc="是否开启服务器心跳,向外部系统汇报。默认: {{false| sc_filter_enabled}}"
scd-bool="true">
</tr>
<tr sc-pretty2 scp-data="global.heartbeat.interval"
scp-desc="心跳间隔的时间,单位: 秒。默认: 9.9"
ng-show="global.heartbeat.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.heartbeat.url"
scp-desc="汇报的HTTP服务器地址汇报信息格式是JSON。默认: http://127.0.0.1:8085/api/v1/servers"
ng-show="global.heartbeat.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.heartbeat.device_id"
scp-desc="设备IDSRS会写入汇报信息中。"
ng-show="global.heartbeat.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.heartbeat.smmaries"
scp-desc="是否汇报摘要即服务器的summaries信息。默认: {{false| sc_filter_yesno}}"
scd-bool="true"
scd-submit="submit(global.heartbeat.summaries)"
ng-show="global.heartbeat.enabled.value">
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="hide">
<a class="accordion-toggle" href="javascript:void(0)">
服务器统计信息配置(stats)
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.stats.network"
scp-desc="统计的网卡索引从0开始算第一块网卡。默认: 0">
</tr>
<tr sc-pretty2 scp-data="global.stats.disk"
scp-desc="统计的磁盘名称,可以在/proc/diskstats中查看。"
scd-array="true">
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="hide">
<a class="accordion-toggle" href="javascript:void(0)">
HTTP服务器配置(http_server)
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.http_server.enabled"
scp-desc="是否开启HTTP文件服务器和HTTP流服务器。默认: {{false| sc_filter_enabled}}"
scd-bool="true">
</tr>
<tr sc-pretty2 scp-data="global.http_server.listen"
scp-desc="HTTP服务器侦听的地址格式是&lt;[address:]port&gt;。默认: 8080"
ng-show="global.http_server.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.http_server.dir"
scp-desc="HTTP服务器的主目录。默认: ./objs/nginx/html"
ng-show="global.http_server.enabled.value">
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="hide">
<a class="accordion-toggle" href="javascript:void(0)">
StreamCaster流转换器(stream_caster)
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<td>Opt</td>
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.enabled"
scp-desc="是否开启StreamCaster即接收特殊的流后转换成RTMP送到SRS。默认: {{false| sc_filter_enabled}}"
scd-bool="true">
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.caster"
scp-desc="转换器的类型不同的类型需要不同的配置mpegts_over_udp,rtsp,flv"
scd-select="mpegts_over_udp,rtsp,flv"
ng-show="global.stream_caster.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.output"
scp-desc="输出的RTMP地址转换器侦听端口接收特定的流转换成RTMP后送到SRS。"
ng-show="global.stream_caster.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.listen"
scp-desc="转换器侦听的地址,格式是&lt;port&gt;"
ng-show="global.stream_caster.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.rtp_port_min"
scp-desc="转换器会在[rtp_port_min, rtp_port_max]中间选择一个新端口并侦听参考RTP协议。"
ng-show="global.stream_caster.enabled.value && global.stream_caster.caster.value == 'rtsp'">
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.rtp_port_max"
scp-desc="转换器会在[rtp_port_min, rtp_port_max]中间选择一个新端口并侦听参考RTP协议。"
ng-show="global.stream_caster.enabled.value && global.stream_caster.caster.value == 'rtsp'">
</tr>
</table>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,452 @@
<div>
<div class="accordion">
<div class="accordion-group" ng-show="!support_raw_api">
<div class="accordion-heading" sc-collapse="in">
<a class="accordion-toggle" href="javascript:void(0)">
HTTP RAW API
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<div class="alert alert-block alert-danger" ng-show="warn_raw_api">
HTTP RAW API is not supported or disabled. Read <a href='https://github.com/ossrs/srs/wiki/v3_EN_HTTPApi#http-raw-api'>WIKI</a>
</div>
<table class="table table-striped table-hover table-bordered" ng-show="http_api">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty scp-key="http_api.enabled" scp-value="http_api.enabled" scp-bool="true"
scp-desc="Whether enable HTTP API. Default is {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty scp-key="http_api.listen" scp-value="http_api.listen"
scp-desc="The listen port for HTTP API, format is &lt;[address:]port&gt;. Default is 1985">
</tr>
<tr sc-pretty scp-key="http_api.crossdomain" scp-value="http_api.crossdomain" scp-bool="true"
scp-desc="Whether allow js CORS(JSONP). Default is {{true| sc_filter_enabled}}">
</tr>
<tr sc-pretty scp-key="http_api.raw_api.enabled" scp-value="http_api.raw_api.enabled" scp-bool="true"
scp-desc="Whether enable HTTP RAW API. Default is {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty scp-key="http_api.raw_api.allow_reload" scp-value="http_api.raw_api.allow_reload" scp-bool="true"
scp-desc="Whether allow HTTP RAW API to reload. Default is {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty scp-key="http_api.raw_api.allow_query" scp-value="http_api.raw_api.allow_query" scp-bool="true"
scp-desc="Whether allow HTTP RAW API to query. Default is {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty scp-key="http_api.raw_api.allow_update" scp-value="http_api.raw_api.allow_update" scp-bool="true"
scp-desc="Whether allow HTTP RAW API to update. Default is {{false| sc_filter_enabled}}">
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="in">
<a class="accordion-toggle" href="javascript:void(0)">
HTTP API Server
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.http_api.enabled" scp-bool="true"
scp-desc="Whether enabel HTTP API. Default is {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.http_api.listen.value"
scp-desc="The HTTP API listen port, format is &lt;[address:]port&gt;. Default is 1985">
</tr>
<tr sc-pretty2 scp-data="global.http_api.crossdomain" scp-bool="true"
scp-desc="Whether allow CORS for js(JSONP). Default is {{true| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.http_api.raw_api.enabled" scp-bool="true"
scp-desc="Whether enable HTTP RAW API. Default is {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.http_api.raw_api.allow_reload" scp-bool="true"
scp-desc="Whether allow reload by HTTP API. Default is {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.http_api.raw_api.allow_query" scp-bool="true"
scp-desc="Whether allow query by HTTP API. Default is {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.http_api.raw_api.allow_update" scp-bool="true"
scp-desc="Whether allow update by HTTP API. Default is {{false| sc_filter_enabled}}">
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="out">
<a class="accordion-toggle" href="javascript:void(0)">
SRS Global Config
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-directive scd-data="global.listen" scd-span="span3"
scd-desc="RTMP listen port, format is &lt;[address:]port&gt;[,&lt;addressN&gt;portN]"
scd-array="true" scd-default="1935"
scd-submit="submit(global.listen)">
</tr>
<tr sc-directive scd-data="global.pid" scd-span="span3"
scd-desc="The pid file for SRS. Default is ./objs/srs.pid"
scd-default="./objs/srs.pid"
scd-submit="submit(global.pid)">
</tr>
<tr sc-directive scd-data="global.chunk_size" scd-span="span3"
scd-desc="RTMP chunk size. Default is 60000"
scd-default="60000"
scd-submit="submit(global.chunk_size)">
</tr>
<tr sc-directive scd-data="global.ff_log_dir" scd-span="span4"
scd-desc="FFMPEG log directory, use /dev/null to disable log. Default is ./objs"
scd-default="./objs"
scd-submit="submit(global.ff_log_dir)">
</tr>
<tr sc-directive scd-data="global.srs_log_tank" scd-span="span3"
scd-desc="System log store. Default is file"
scd-default="file", scd-select="file,console"
scd-submit="submit(global.srs_log_tank)">
</tr>
<tr sc-directive scd-data="global.srs_log_level" scd-span="span3"
scd-desc="System log level. Default is trace"
scd-default="trace" scd-select="verbose,info,trace,warn,error"
scd-submit="submit(global.srs_log_level)">
</tr>
<tr sc-directive scd-data="global.srs_log_file" scd-span="span4"
scd-desc="When log store is file, the log path. Default is ./objs/srs.log"
scd-default="./objs/srs.log"
scd-submit="submit(global.srs_log_file)"
ng-if="global.srs_log_tank.value == 'file'">
</tr>
<tr sc-directive scd-data="global.max_connections" scd-span="span3"
scd-desc="System maximum connections. Default is 1000"
scd-default="1000"
scd-submit="submit(global.max_connections)">
</tr>
<tr sc-pretty2 scp-data="global.daemon" scp-bool="true"
scp-desc="Whether start SRS as daemon. Default is {{true| sc_filter_yesno}}">
</tr>
<tr sc-directive scd-data="global.utc_time" scd-span="span4"
scd-desc="Whether use UTC time. Default is {{false| sc_filter_yesno}}"
scd-default="false" scd-bool="true"
scd-submit="submit(global.utc_time)">
</tr>
<tr sc-directive scd-data="global.pithy_print_ms" scd-span="span3"
scd-desc="Pithy log print interval in ms. Default is 10000"
scd-default="10000" scd-select="1000,3000,5000,10000,30000"
scd-submit="submit(global.pithy_print_ms)">
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="out">
<a class="accordion-toggle" href="javascript:void(0)">
System Vhosts, total {{global.nb_vhosts.value}} vhosts configed.
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>
<a href="javscript:void(0)" ng-click="new_vhost()" class="icon-plus"></a>
</th>
<th>Name</th>
<th title="Whether Enabled">E</th>
<th title="Whether Origin Server">O</th>
<th title="DVR Configed">D</th>
<th title="HTTP Server Configed">H</th>
<th title="HTTP FLV Configed">FLV</th>
<th title="HLS Configed">HLS</th>
<th title="HDS Configed">HDS</th>
<th title="HTTP Callback">CB</th>
<th ng-show="false">Exec</th>
<th ng-show="false">BWT</th>
<th title="Forwarder">F</th>
<th title="Security Configed">S</th>
<th ng-show="false">Referer</th>
<th ng-show="false">MR</th>
<th ng-show="false">RTC</th>
<th ng-show="false">GOP</th>
<th ng-show="false">TcpNoDelay</th>
<th ng-show="false">MixCorrect</th>
<th ng-show="false">TimeJitter</th>
<th ng-show="false">ATC</th>
<th title="Transcode Configied">T</th>
<th title="Ingester Configed">I</th>
<th>
<a href="javascript:void(0)" ng-click="new_vhost()">Add</a>
</th>
</tr>
<tr ng-repeat="vhost in global.vhosts">
<td><a href="#/vhosts/{{vhost.id}}" ng-show="vhost.id">{{vhost.id}}</a><span ng-show="!vhost.id">No Stream</span> </td>
<td colspan="{{vhost.editable?13:1}}">
<span ng-show="!vhost.editable">{{vhost.name}}</span>
<span ng-show="vhost.editable">
<input class="text span5" ng-model="vhost.name"><br/>
Please input vhost name.
</span>
</td>
<td ng-show="!vhost.editable">{{vhost.enabled| sc_filter_yesno}}</td>
<td ng-show="!vhost.editable">{{vhost.origin| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.dvr| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.http_static| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.http_remux| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.hls| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.hds| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.http_hooks| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.exec| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.bandcheck| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.forward| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.security| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.refer| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.mr| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.min_latency| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.gop_cache| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.tcp_nodelay| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.mix_correct| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.time_jitter| sc_filter_yn}}</td>
<td ng-show="false && !vhost.editable">{{vhost.atc| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.transcode| sc_filter_yn}}</td>
<td ng-show="!vhost.editable">{{vhost.ingest| sc_filter_yn}}</td>
<td>
<span ng-show="vhost.editable">
<!-- vhost exists in server -->
<span ng-show="vhost.enabled != undefined">
<a bravo-popover href="javascript:void(0)"
data-content="Please confirm submit vhost {{vhost.name}}?" data-title="Confirm" data-placement="left"
bravo-popover-confirm="update_vhost(vhost)">
Submit
</a>
<a href="javascript:void(0)" ng-click="cancel_vhost(vhost)">Cancel</a>
</span>
<!-- vhost in adding. -->
<span ng-show="vhost.enabled == undefined">
<a bravo-popover href="javascript:void(0)"
data-content="Please confirm submit vhost{{vhost.name}}?" data-title="Confirm" data-placement="left"
bravo-popover-confirm="add_vhost(vhost)">
Submit
</a>
<a href="javascript:void(0)" ng-click="abort_vhost(vhost)">Cancel</a>
</span>
</span>
<span ng-show="!vhost.editable">
<span ng-show="vhost.enabled">
<span>Update</span>
<a bravo-popover href="javascript:void(0)"
data-content="Please confirm to disable {{vhost.name}}?" data-title="Confirm" data-placement="left"
bravo-popover-confirm="disable_vhost(vhost)">
Disable
</a>
<span>Delete</span>
</span>
<span ng-show="!vhost.enabled">
<a href="javascript:void(0)" ng-click="edit_vhost(vhost)">编辑</a>
<a bravo-popover href="javascript:void(0)"
data-content="Please confirm to enable {{vhost.name}}?" data-title="Confirm" data-placement="left"
bravo-popover-confirm="enable_vhost(vhost)">
Enable
</a>
<a bravo-popover href="javascript:void(0)"
data-content="Please confirm to delete {{vhost.name}}?" data-title="Confirm" data-placement="left"
bravo-popover-confirm="delete_vhost(vhost)">
Delete
</a>
</span>
<a href="#/configs/{{vhost.name}}" ng-show="!vhost.editable">Detail</a>
</span>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="hide">
<a class="accordion-toggle" href="javascript:void(0)">
Config KAFKA
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.kafka.enabled" scp-bool="true"
scp-desc="Whether enable KAFKA. Default is {{false| sc_filter_enabled}}">
</tr>
<tr sc-pretty2 scp-data="global.kafka.brokers"
scp-desc="The broker list, format is &lt;ip:port&gt;">
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="hide">
<a class="accordion-toggle" href="javascript:void(0)">
SRS Heartbeat
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.heartbeat.enabled"
scp-desc="Whether enable heartbet. Default is {{false| sc_filter_enabled}}"
scd-bool="true">
</tr>
<tr sc-pretty2 scp-data="global.heartbeat.interval"
scp-desc="The interval in seconds. Default is 9.9"
ng-show="global.heartbeat.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.heartbeat.url"
scp-desc="The report url in json. Default is http://127.0.0.1:8085/api/v1/servers"
ng-show="global.heartbeat.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.heartbeat.device_id"
scp-desc="The device ID."
ng-show="global.heartbeat.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.heartbeat.smmaries"
scp-desc="Whether report summaries. Default is {{false| sc_filter_yesno}}"
scd-bool="true"
scd-submit="submit(global.heartbeat.summaries)"
ng-show="global.heartbeat.enabled.value">
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="hide">
<a class="accordion-toggle" href="javascript:void(0)">
STAT Hardward Config
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.stats.network"
scp-desc="The stat network index. Default is 0">
</tr>
<tr sc-pretty2 scp-data="global.stats.disk"
scp-desc="The stat disk name."
scd-array="true">
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="hide">
<a class="accordion-toggle" href="javascript:void(0)">
HTTP Server Config
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.http_server.enabled"
scp-desc="Whether enable HTTP Server. Default is {{false| sc_filter_enabled}}"
scd-bool="true">
</tr>
<tr sc-pretty2 scp-data="global.http_server.listen"
scp-desc="The HTTP Server listen port, format is &lt;[address:]port&gt;. Default is 8080"
ng-show="global.http_server.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.http_server.dir"
scp-desc="The HTTP Server root directory. Default is ./objs/nginx/html"
ng-show="global.http_server.enabled.value">
</tr>
</table>
</div>
</div>
</div>
<div class="accordion-group" ng-show="support_raw_api">
<div class="accordion-heading" sc-collapse="hide">
<a class="accordion-toggle" href="javascript:void(0)">
Stream Caster Config
</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<td>Opt</td>
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.enabled"
scp-desc="Whether enable StreamCaster. Default is {{false| sc_filter_enabled}}"
scd-bool="true">
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.caster"
scp-desc="The type of StreamCaster. For example, mpegts_over_udp,rtsp,flv"
scd-select="mpegts_over_udp,rtsp,flv"
ng-show="global.stream_caster.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.output"
scp-desc="The output RTMP url."
ng-show="global.stream_caster.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.listen"
scp-desc="The listen port, format is &lt;port&gt;"
ng-show="global.stream_caster.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.rtp_port_min"
scp-desc="The RTP min port."
ng-show="global.stream_caster.enabled.value && global.stream_caster.caster.value == 'rtsp'">
</tr>
<tr sc-pretty2 scp-data="global.stream_caster.rtp_port_max"
scp-desc="The RTP max port."
ng-show="global.stream_caster.enabled.value && global.stream_caster.caster.value == 'rtsp'">
</tr>
</table>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,40 @@
<div>
<div class="accordion">
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" href="javascript:void(0)">
连接到您的SRS服务器
</a>
</div>
<div class="accordion-body collapse in">
<div class="accordion-inner">
<div class="form-horizontal">
<div class="control-group">
<label class="control-label" for="sscProtocol">协议</label>
<div class="controls">
<input type="text" id="sscProtocol" placeholder="SRS API Protocol, http or https" ng-model="server.schema">
</div>
</div>
<div class="control-group">
<label class="control-label" for="sscServer">服务器IP</label>
<div class="controls">
<input type="text" id="sscServer" placeholder="SRS API Server IP" ng-model="server.ip">
</div>
</div>
<div class="control-group">
<label class="control-label" for="sscPort">API端口</label>
<div class="controls">
<input type="text" id="sscPort" placeholder="SRS API Server Port" ng-model="server.port">
</div>
</div>
<div class="control-group">
<div class="controls">
<button type="submit" class="btn" ng-click="connect()">连接到SRS</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,40 @@
<div>
<div class="accordion">
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" href="javascript:void(0)">
Connect to your SRS
</a>
</div>
<div class="accordion-body collapse in">
<div class="accordion-inner">
<div class="form-horizontal">
<div class="control-group">
<label class="control-label" for="sscProtocol">Protocol</label>
<div class="controls">
<input type="text" id="sscProtocol" placeholder="SRS API Protocol, http or https" ng-model="server.schema">
</div>
</div>
<div class="control-group">
<label class="control-label" for="sscServer">Server IP</label>
<div class="controls">
<input type="text" id="sscServer" placeholder="SRS API Server IP" ng-model="server.ip">
</div>
</div>
<div class="control-group">
<label class="control-label" for="sscPort">SRS API Port</label>
<div class="controls">
<input type="text" id="sscPort" placeholder="SRS API Server Port" ng-model="server.port">
</div>
</div>
<div class="control-group">
<div class="controls">
<button type="submit" class="btn" ng-click="connect()">Connect</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,62 @@
<div>
<div class="accordion">
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" href="javascript:void(0)">
Vhost({{global.vid.value}})录制(DVR)配置
</a>
</div>
<div class="accordion-body collapse in">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.name" scp-link="#/vhosts/{{global.id.value}}"
scp-desc="录制的流所属的Vhost的名称">
</tr>
<tr sc-pretty2 scp-data="global.app"
scp-desc="录制的流所属的App的名称">
</tr>
<tr sc-pretty2 scp-data="global.stream" scp-link="#/streams/{{global.sid.value}}"
scp-desc="录制的流的Stream地址即流名称">
</tr>
<tr sc-directive scd-data="global.dvr.apply" scd-span="span4"
scd-desc="是否开始录制该流。"
scd-default="false" scd-bool="true"
scd-submit="dvr(global.dvr.apply)"
ng-show="global.dvr.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.dvr.enabled"
scp-desc="是否对Vhost启用DVR配置。默认: {{false| sc_filter_yesno}}"
scp-bool="true">
</tr>
<tr sc-pretty2 scp-data="global.dvr.dvr_plan"
scp-desc="录制计划session按会话segment分段append单文件。默认: session"
ng-show="global.dvr.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.dvr.dvr_path"
scp-desc="录制文件的路径模板。默认: ./objs/nginx/html/[app]/[stream].[timestamp].flv"
ng-show="global.dvr.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.dvr.dvr_duration"
scp-desc="单位:秒。录制文件的时长。默认: 30"
ng-show="global.dvr.enabled.value && global.dvr.dvr_plan && global.dvr.dvr_plan.value == 'segment'">
</tr>
<tr sc-pretty2 scp-data="global.dvr.dvr_wait_keyframe"
scp-desc="是否等待关键帧。默认: {{true| sc_filter_yesno}}"
ng-show="global.dvr.enabled.value && global.dvr.dvr_plan && global.dvr.dvr_plan.value == 'segment'">
</tr>
<tr sc-pretty2 scp-data="global.dvr.time_jitter"
scp-desc="录制的时间戳校正算法。full完全zero置零off关闭。默认: full"
ng-show="global.dvr.enabled.value">
</tr>
</table>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,62 @@
<div>
<div class="accordion">
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" href="javascript:void(0)">
Vhost({{global.vid.value}}) DVR Config
</a>
</div>
<div class="accordion-body collapse in">
<div class="accordion-inner">
<table class="table table-striped table-hover table-bordered">
<tr>
<th>Key</th>
<th>Value</th>
<th>Description</th>
<th>Opt</th>
</tr>
<tr sc-pretty2 scp-data="global.name" scp-link="#/vhosts/{{global.id.value}}"
scp-desc="The vhost name to DVR.">
</tr>
<tr sc-pretty2 scp-data="global.app"
scp-desc="The app name to DVR.">
</tr>
<tr sc-pretty2 scp-data="global.stream" scp-link="#/streams/{{global.sid.value}}"
scp-desc="The stream url to DVR.">
</tr>
<tr sc-directive scd-data="global.dvr.apply" scd-span="span4"
scd-desc="Whether record this stream."
scd-default="false" scd-bool="true"
scd-submit="dvr(global.dvr.apply)"
ng-show="global.dvr.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.dvr.enabled"
scp-desc="Whether enable DVR for vhost. Default is {{false| sc_filter_yesno}}"
scp-bool="true">
</tr>
<tr sc-pretty2 scp-data="global.dvr.dvr_plan"
scp-desc="The DVR plan, can be session, segment or append. Default is session."
ng-show="global.dvr.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.dvr.dvr_path"
scp-desc="The DVR file path template. Default is ./objs/nginx/html/[app]/[stream].[timestamp].flv"
ng-show="global.dvr.enabled.value">
</tr>
<tr sc-pretty2 scp-data="global.dvr.dvr_duration"
scp-desc="The DVR file duration in seconds. Default is 30"
ng-show="global.dvr.enabled.value && global.dvr.dvr_plan && global.dvr.dvr_plan.value == 'segment'">
</tr>
<tr sc-pretty2 scp-data="global.dvr.dvr_wait_keyframe"
scp-desc="Whether wait for keyframe when DVR. Default is {{true| sc_filter_yesno}}"
ng-show="global.dvr.enabled.value && global.dvr.dvr_plan && global.dvr.dvr_plan.value == 'segment'">
</tr>
<tr sc-pretty2 scp-data="global.dvr.time_jitter"
scp-desc="The time jitter algorithm for DVR, can be full, zero or off. Default is full."
ng-show="global.dvr.enabled.value">
</tr>
</table>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1 @@
DVR信息请在每个Stream中单独配置, 请前往<a href="javascript:void(0)" ng-click="gogogo('/streams/')">Streams</a>配置。

View file

@ -0,0 +1 @@
Please config DVR in each stream by <a href="javascript:void(0)" ng-click="gogogo('/streams/')">here</a>.

View file

@ -0,0 +1,27 @@
<div>
<div class="accordion">
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" href="javascript:void(0)">
视频流-{{stream.id}}
</a>
</div>
<div id="collapseOne" class="accordion-body collapse in">
<div class="accordion-inner" ng-if="stream">
<p>ID: {{stream.id}}</p>
<p>Name: {{stream.name}}</p>
<td><a href="javascript:void(0)" ng-click="gogogo('/vhosts/' + stream.vhost)">{{owner.name}}</a></td>
<p>Publishing: {{stream.publish.active| sc_filter_has_stream}}</p>
<p>Clients: {{stream.clients}}人</p>
<p>Recv: {{stream.kbps.recv_30s| sc_filter_bitrate_k}}</p>
<p>Send: {{stream.kbps.send_30s| sc_filter_bitrate_k}}</p>
<p ng-if="stream.video">Video: <span>{{stream.video| sc_filter_video}}</span></p>
<p ng-if="stream.audio">Audio: <span>{{stream.audio| sc_filter_audio}}</span></p>
<p>管理: <a ng-href="{{stream| sc_filter_preview_url}}" target="_blank">预览</a></p>
<p ng-show="stream.publish.active">管理: <a ng-click="kickoff(stream)" href="javascript:void(0)">踢流</a></p>
<p ng-show="support_raw_api">管理: <a ng-click="dvr(stream)" href="javascript:void(0)">录制</a> </p>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,27 @@
<div>
<div class="accordion">
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" href="javascript:void(0)">
Stream-{{stream.id}}
</a>
</div>
<div id="collapseOne" class="accordion-body collapse in">
<div class="accordion-inner" ng-if="stream">
<p>ID: {{stream.id}}</p>
<p>Name: {{stream.name}}</p>
<td><a href="javascript:void(0)" ng-click="gogogo('/vhosts/' + stream.vhost)">{{owner.name}}</a></td>
<p>Publishing: {{stream.publish.active| sc_filter_has_stream}}</p>
<p>Clients: {{stream.clients}} Clients</p>
<p>Recv: {{stream.kbps.recv_30s| sc_filter_bitrate_k}}</p>
<p>Send: {{stream.kbps.send_30s| sc_filter_bitrate_k}}</p>
<p ng-if="stream.video">Video: <span>{{stream.video| sc_filter_video}}</span></p>
<p ng-if="stream.audio">Audio: <span>{{stream.audio| sc_filter_audio}}</span></p>
<p>Manage: <a ng-href="{{stream| sc_filter_preview_url}}" target="_blank">Preview</a></p>
<p ng-show="stream.publish.active">Manage: <a ng-click="kickoff(stream)" href="javascript:void(0)">Kickoff</a></p>
<p ng-show="support_raw_api">Manage: <a ng-click="dvr(stream)" href="javascript:void(0)">Record</a> </p>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,47 @@
<div>
<div class="accordion">
<div class="accordion-group">
<div class="accordion-heading" sc-collapse="in">
<a class="accordion-toggle" href="javascript:void(0)">
视频流(Streams)列表
</a>
</div>
<div id="collapseOne" class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-bordered">
<tr>
<th>ID</th>
<th>流名称</th>
<th>Vhost</th>
<th>状态</th>
<th>在线人数</th>
<th>入口带宽</th>
<th>出口带宽</th>
<th>视频信息</th>
<th>音频信息</th>
<th>管理</th>
</tr>
<tr ng-repeat="stream in streams">
<td><a href="javascript:void(0)" ng-click="gogogo('/streams/' + stream.id)">{{stream.id}}</a></td>
<td>{{stream.name| sc_filter_less}}</td>
<td><a href="javascript:void(0)" ng-click="gogogo('/vhosts/' + stream.vhost)">{{stream.owner.name}}</a></td>
<td>{{stream.publish.active| sc_filter_has_stream}}</td>
<td>{{stream.clients}}人</td>
<td>{{stream.kbps.recv_30s| sc_filter_bitrate_k}}</td>
<td>{{stream.kbps.send_30s| sc_filter_bitrate_k}}</td>
<td><span ng-if="stream.video">{{stream.video| sc_filter_video}}</span></td>
<td><span ng-if="stream.audio">{{stream.audio| sc_filter_audio}}</span></td>
<td>
<a ng-href="{{stream| sc_filter_preview_url}}" target="_blank">预览</a>
<a ng-show="stream.publish.active" ng-click="kickoff(stream)" href="javascript:void(0)">踢流</a>
<span ng-show="support_raw_api">
<a href="javascript:void(0)" ng-click="dvr(stream)">录制</a>
</span>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,47 @@
<div>
<div class="accordion">
<div class="accordion-group">
<div class="accordion-heading" sc-collapse="in">
<a class="accordion-toggle" href="javascript:void(0)">
System Streams
</a>
</div>
<div id="collapseOne" class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-bordered">
<tr>
<th>ID</th>
<th>Name</th>
<th>Vhost</th>
<th>Status</th>
<th>Clients</th>
<th>Inbound</th>
<th>Outbound</th>
<th>Video</th>
<th>Audio</th>
<th>Manage</th>
</tr>
<tr ng-repeat="stream in streams">
<td><a href="javascript:void(0)" ng-click="gogogo('/streams/' + stream.id)">{{stream.id}}</a></td>
<td>{{stream.name| sc_filter_less}}</td>
<td><a href="javascript:void(0)" ng-click="gogogo('/vhosts/' + stream.vhost)">{{stream.owner.name}}</a></td>
<td>{{stream.publish.active| sc_filter_has_stream}}</td>
<td>{{stream.clients}}人</td>
<td>{{stream.kbps.recv_30s| sc_filter_bitrate_k}}</td>
<td>{{stream.kbps.send_30s| sc_filter_bitrate_k}}</td>
<td><span ng-if="stream.video">{{stream.video| sc_filter_video}}</span></td>
<td><span ng-if="stream.audio">{{stream.audio| sc_filter_audio}}</span></td>
<td>
<a ng-href="{{stream| sc_filter_preview_url}}" target="_blank">Preview</a>
<a ng-show="stream.publish.active" ng-click="kickoff(stream)" href="javascript:void(0)">Kickoff</a>
<span ng-show="support_raw_api">
<a href="javascript:void(0)" ng-click="dvr(stream)">Record</a>
</span>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,137 @@
<div>
<div class="accordion">
<div class="accordion-group">
<div class="accordion-heading" sc-collapse="in">
<a class="accordion-toggle" href="javascript:void(0)">
服务器基本信息
</a>
</div>
<div id="collapseOne" class="accordion-body collapse">
<div class="accordion-inner">
<div class="row">
<div class="span3">
<table class="table table-bordered" ng-if="server">
<tr>
<th colspan="2">SRS/{{server.version}}</th>
</tr>
<tr>
<td>运行</td>
<td>{{server.srs_uptime| sc_filter_time}}</td>
</tr>
<tr>
<td>CPU</td>
<td>{{server.cpu_percent| sc_filter_percentf}} / {{system.cpus_online| sc_filter_percentf}}</td>
</tr>
<tr>
<td>内存</td>
<td>
{{server.mem_percent| sc_filter_percentf2}}
{{server.mem_kbyte| sc_filter_filesize_k2}} / {{system.mem_ram_kbyte| sc_filter_filesize_k2}}
</td>
</tr>
<tr>
<td>网络</td>
<td>
<span title="SRS的连接数">{{system.conn_srs}}</span> /
<span title="SRS的入网带宽即客户端上传带宽">{{kbps.in.srs| sc_filter_bitrate_k2}}</span> /
<span title="SRS的出网带宽即客户端下载带宽">{{kbps.out.srs| sc_filter_bitrate_k2}}</span>
</td>
</tr>
</table>
</div>
<div class="span3">
<table class="table table-bordered" ng-if="system">
<tr>
<th colspan="2">OS信息</th>
</tr>
<tr>
<td>运行</td>
<td>{{system.uptime| sc_filter_time}}</td>
</tr>
<tr>
<td>CPU</td>
<td>{{system.cpu_percent * system.cpus_online| sc_filter_percentf}} / {{system.cpus_online| sc_filter_percentf}}</td>
</tr>
<tr>
<td>内存</td>
<td>
{{system.mem_ram_percent| sc_filter_percentf2}}
{{system.mem_ram_kbyte * system.mem_ram_percent| sc_filter_filesize_k2}} / {{system.mem_ram_kbyte| sc_filter_filesize_k2}}
</td>
</tr>
<tr>
<td>负载</td>
<td>{{system.load_1m| sc_filter_number}} / {{system.load_5m| sc_filter_number}} / {{system.load_15m| sc_filter_number}}</td>
</tr>
</table>
</div>
<div class="span3">
<table class="table table-bordered" ng-if="system">
<tr>
<th colspan="2">负载信息</th>
</tr>
<tr>
<td>外网</td>
<td>
<span title="系统外网的入网带宽,即客户端上传带宽">{{kbps.in.sys| sc_filter_bitrate_k2}}</span> /
<span title="系统外网的出网带宽,即客户端下载带宽">{{kbps.out.sys| sc_filter_bitrate_k2}}</span>
</td>
</tr>
<tr>
<td>内网</td>
<td>
<span title="系统内网的入网带宽,即客户端上传带宽">{{kbps.in.inner| sc_filter_bitrate_k2}}</span> /
<span title="系统内网的出网带宽,即客户端下载带宽">{{kbps.out.inner| sc_filter_bitrate_k2}}</span>
</td>
</tr>
<tr>
<td>连接</td>
<td>
<span title="系统总连接数">{{system.conn_sys}}</span>
<span title="系统ESTABLISHED状态的连接数">{{system.conn_sys_et}}</span>
<span title="系统TIME_WAIT状态的连接数">{{system.conn_sys_tw}}</span>
<span title="系统UDP绑定端口">{{system.conn_sys_udp}}</span>
</td>
</tr>
<tr>
<td>磁盘</td>
<td>
<span title="IO繁忙度">{{system.disk_busy_percent| sc_filter_percentf2}}</span>
<span title="磁盘读取速度Bps">{{system.disk_read_KBps| sc_filter_filerate_k2}}</span>
<span title="磁盘写入速度Bps">{{system.disk_write_KBps| sc_filter_filerate_k2}}</span>
</td>
</tr>
</table>
</div>
<div class="span2">
<table class="table table-bordered" ng-if="system">
<tr>
<th colspan="2">其他信息</th>
</tr>
<tr>
<td title="系统CPU信息">CPU</td>
<td>
<span title="CPU核心数">{{system.cpus}}</span> /
<span title="在线CPU核心数">{{system.cpus_online}}</span>
</td>
</tr>
<tr>
<td title="SRS的PID">PID</td>
<td>{{server.pid}}</td>
</tr>
<tr>
<td title="SRS的父PID">PPID</td>
<td>{{server.ppid}}</td>
</tr>
<tr>
<td title="SRS API是否有效">Ready</td>
<td>{{global.ok| sc_filter_yesno}}</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,137 @@
<div>
<div class="accordion">
<div class="accordion-group">
<div class="accordion-heading" sc-collapse="in">
<a class="accordion-toggle" href="javascript:void(0)">
SRS Summary
</a>
</div>
<div id="collapseOne" class="accordion-body collapse">
<div class="accordion-inner">
<div class="row">
<div class="span3">
<table class="table table-bordered" ng-if="server">
<tr>
<th colspan="2">SRS/{{server.version}}</th>
</tr>
<tr>
<td>Alive</td>
<td>{{server.srs_uptime| sc_filter_time}}</td>
</tr>
<tr>
<td>CPU</td>
<td>{{server.cpu_percent| sc_filter_percentf}} / {{system.cpus_online| sc_filter_percentf}}</td>
</tr>
<tr>
<td>Memory</td>
<td>
{{server.mem_percent| sc_filter_percentf2}}
{{server.mem_kbyte| sc_filter_filesize_k2}} / {{system.mem_ram_kbyte| sc_filter_filesize_k2}}
</td>
</tr>
<tr>
<td>Network</td>
<td>
<span title="SRS Connections">{{system.conn_srs}}</span> /
<span title="SRS Inbound Bandwidth">{{kbps.in.srs| sc_filter_bitrate_k2}}</span> /
<span title="SRS Outbound Bandwidth">{{kbps.out.srs| sc_filter_bitrate_k2}}</span>
</td>
</tr>
</table>
</div>
<div class="span3">
<table class="table table-bordered" ng-if="system">
<tr>
<th colspan="2">OS System</th>
</tr>
<tr>
<td>Alive</td>
<td>{{system.uptime| sc_filter_time}}</td>
</tr>
<tr>
<td>CPU</td>
<td>{{system.cpu_percent * system.cpus_online| sc_filter_percentf}} / {{system.cpus_online| sc_filter_percentf}}</td>
</tr>
<tr>
<td>Memory</td>
<td>
{{system.mem_ram_percent| sc_filter_percentf2}}
{{system.mem_ram_kbyte * system.mem_ram_percent| sc_filter_filesize_k2}} / {{system.mem_ram_kbyte| sc_filter_filesize_k2}}
</td>
</tr>
<tr>
<td>Load</td>
<td>{{system.load_1m| sc_filter_number}} / {{system.load_5m| sc_filter_number}} / {{system.load_15m| sc_filter_number}}</td>
</tr>
</table>
</div>
<div class="span3">
<table class="table table-bordered" ng-if="system">
<tr>
<th colspan="2">IO Load</th>
</tr>
<tr>
<td>Internet</td>
<td>
<span title="Internet Inbound Bandwidth">{{kbps.in.sys| sc_filter_bitrate_k2}}</span> /
<span title="Internet Outbound Bandwidth">{{kbps.out.sys| sc_filter_bitrate_k2}}</span>
</td>
</tr>
<tr>
<td>Intranet</td>
<td>
<span title="Intranet Inbound Bandwidth">{{kbps.in.inner| sc_filter_bitrate_k2}}</span> /
<span title="Intranet Outbound Bandwidth">{{kbps.out.inner| sc_filter_bitrate_k2}}</span>
</td>
</tr>
<tr>
<td title="Connections">Conns</td>
<td>
<span title="System">{{system.conn_sys}}</span>
<span title="System ESTABLISHED">{{system.conn_sys_et}}</span>
<span title="System TIME_WAIT">{{system.conn_sys_tw}}</span>
<span title="System UDP Bind Ports">{{system.conn_sys_udp}}</span>
</td>
</tr>
<tr>
<td>Disk</td>
<td>
<span title="IO Busy">{{system.disk_busy_percent| sc_filter_percentf2}}</span>
<span title="Disk Read in Bps">{{system.disk_read_KBps| sc_filter_filerate_k2}}</span>
<span title="Disk Write in Bps">{{system.disk_write_KBps| sc_filter_filerate_k2}}</span>
</td>
</tr>
</table>
</div>
<div class="span2">
<table class="table table-bordered" ng-if="system">
<tr>
<th colspan="2">Others</th>
</tr>
<tr>
<td title="System CPU Usage">CPU</td>
<td>
<span title="Number of CPU">{{system.cpus}}</span> /
<span title="Online CPUs">{{system.cpus_online}}</span>
</td>
</tr>
<tr>
<td title="PID of SRS">PID</td>
<td>{{server.pid}}</td>
</tr>
<tr>
<td title="PID of SRS's Parent">PPID</td>
<td>{{server.ppid}}</td>
</tr>
<tr>
<td title="Whether SRS API Ready?">Ready</td>
<td>{{global.ok| sc_filter_yesno}}</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,23 @@
<div>
<div class="accordion">
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" href="javascript:void(0)">
虚拟主机-{{vhost.id}}
</a>
</div>
<div id="collapseOne" class="accordion-body collapse in">
<div class="accordion-inner">
<p>ID: {{vhost.id}}</p>
<p>Name: {{vhost.name}}</p>
<p>Enabled: {{vhost.enabled| sc_filter_enabled}}</p>
<p>Streams: {{vhost.streams}}人</p>
<p>Clients: {{vhost.clients}}人</p>
<p>Recv: {{vhost.kbps.recv_30s| sc_filter_bitrate_k}}</p>
<p>Send: {{vhost.kbps.send_30s| sc_filter_bitrate_k}}</p>
<p>HLS: {{vhost.hls.enabled| sc_filter_enabled}}</p>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,23 @@
<div>
<div class="accordion">
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" href="javascript:void(0)">
Vhost-{{vhost.id}}
</a>
</div>
<div id="collapseOne" class="accordion-body collapse in">
<div class="accordion-inner">
<p>ID: {{vhost.id}}</p>
<p>Name: {{vhost.name}}</p>
<p>Enabled: {{vhost.enabled| sc_filter_enabled}}</p>
<p>Streams: {{vhost.streams}} Clients</p>
<p>Clients: {{vhost.clients}} Clients</p>
<p>Recv: {{vhost.kbps.recv_30s| sc_filter_bitrate_k}}</p>
<p>Send: {{vhost.kbps.send_30s| sc_filter_bitrate_k}}</p>
<p>HLS: {{vhost.hls.enabled| sc_filter_enabled}}</p>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,37 @@
<div>
<div class="accordion">
<div class="accordion-group">
<div class="accordion-heading" sc-collapse="in">
<a class="accordion-toggle" href="javascript:void(0)">
虚拟主机(Vhosts)列表
</a>
</div>
<div id="collapseOne" class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-bordered">
<tr>
<th>ID</th>
<th>主机名称</th>
<th>状态</th>
<th>在线流</th>
<th>在线人数</th>
<th>入口带宽</th>
<th>出口带宽</th>
<th>HLS</th>
</tr>
<tr ng-repeat="vhost in vhosts">
<td><a href="javascript:void(0)" ng-click="gogogo('/vhosts/' + vhost.id)">{{vhost.id}}</a></td>
<td>{{vhost.name}}</td>
<td>{{vhost.enabled| sc_filter_enabled}}</td>
<td>{{vhost.streams}}个</td>
<td>{{vhost.clients}}人</td>
<td>{{vhost.kbps.recv_30s| sc_filter_bitrate_k}}</td>
<td>{{vhost.kbps.send_30s| sc_filter_bitrate_k}}</td>
<td>{{vhost.hls.enabled| sc_filter_enabled}}</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,37 @@
<div>
<div class="accordion">
<div class="accordion-group">
<div class="accordion-heading" sc-collapse="in">
<a class="accordion-toggle" href="javascript:void(0)">
System Vhosts
</a>
</div>
<div id="collapseOne" class="accordion-body collapse">
<div class="accordion-inner">
<table class="table table-striped table-bordered">
<tr>
<th>ID</th>
<th>Name</th>
<th>Status</th>
<th>Streams</th>
<th>Clients</th>
<th>Inbound</th>
<th>Outbound</th>
<th>HLS</th>
</tr>
<tr ng-repeat="vhost in vhosts">
<td><a href="javascript:void(0)" ng-click="gogogo('/vhosts/' + vhost.id)">{{vhost.id}}</a></td>
<td>{{vhost.name}}</td>
<td>{{vhost.enabled| sc_filter_enabled}}</td>
<td>{{vhost.streams}}个</td>
<td>{{vhost.clients}}人</td>
<td>{{vhost.kbps.recv_30s| sc_filter_bitrate_k}}</td>
<td>{{vhost.kbps.send_30s| sc_filter_bitrate_k}}</td>
<td>{{vhost.hls.enabled| sc_filter_enabled}}</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>

View file

@ -20,16 +20,19 @@
update_nav();
var query = parse_query_string();
var url = "srs_player.html?vhost=" + srs_get_player_vhost();
var params = [];
for (var key in query.user_query) {
if (key == "vhost") {
continue;
}
url += "&" + key + "=" + query[key];
params.push(key + "=" + query[key]);
}
window.location.href = url;
var url = "srs_player.html";
if (params.length) {
url += '?' + params.join('&');
}
setTimeout(function () {
window.location.href = url;
}, 0);
});
</script>
</head>

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,76 +0,0 @@
"undefined"==typeof jwplayer&&(jwplayer=function(f){if(jwplayer.api)return jwplayer.api.selectPlayer(f)},jwplayer.version="6.4.3359",jwplayer.vid=document.createElement("video"),jwplayer.audio=document.createElement("audio"),jwplayer.source=document.createElement("source"),function(f){function a(g){return function(){return c(g)}}var l=document,e=window,j=navigator,b=f.utils=function(){};b.exists=function(g){switch(typeof g){case "string":return 0<g.length;case "object":return null!==g;case "undefined":return!1}return!0};
b.styleDimension=function(g){return g+(0<g.toString().indexOf("%")?"":"px")};b.getAbsolutePath=function(g,a){b.exists(a)||(a=l.location.href);if(b.exists(g)){var c;if(b.exists(g)){c=g.indexOf("://");var j=g.indexOf("?");c=0<c&&(0>j||j>c)}else c=void 0;if(c)return g;c=a.substring(0,a.indexOf("://")+3);var j=a.substring(c.length,a.indexOf("/",c.length+1)),d;0===g.indexOf("/")?d=g.split("/"):(d=a.split("?")[0],d=d.substring(c.length+j.length+1,d.lastIndexOf("/")),d=d.split("/").concat(g.split("/")));
for(var h=[],e=0;e<d.length;e++)d[e]&&(b.exists(d[e])&&"."!=d[e])&&(".."==d[e]?h.pop():h.push(d[e]));return c+j+"/"+h.join("/")}};b.extend=function(){var a=b.extend.arguments;if(1<a.length){for(var c=1;c<a.length;c++)b.foreach(a[c],function(c,d){try{b.exists(d)&&(a[0][c]=d)}catch(j){}});return a[0]}return null};b.log=function(a,b){"undefined"!=typeof console&&"undefined"!=typeof console.log&&(b?console.log(a,b):console.log(a))};var c=b.userAgentMatch=function(a){return null!==j.userAgent.toLowerCase().match(a)};
b.isIE=a(/msie/i);b.isFF=a(/firefox/i);b.isChrome=a(/chrome/i);b.isIOS=a(/iP(hone|ad|od)/i);b.isIPod=a(/iP(hone|od)/i);b.isIPad=a(/iPad/i);b.isSafari602=a(/Macintosh.*Mac OS X 10_8.*6\.0\.\d* Safari/i);b.isAndroid=function(a){return a?c(RegExp("android.*"+a,"i")):c(/android/i)};b.isMobile=function(){return b.isIOS()||b.isAndroid()};b.saveCookie=function(a,b){l.cookie="jwplayer."+a+"\x3d"+b+"; path\x3d/"};b.getCookies=function(){for(var a={},b=l.cookie.split("; "),c=0;c<b.length;c++){var d=b[c].split("\x3d");
0==d[0].indexOf("jwplayer.")&&(a[d[0].substring(9,d[0].length)]=d[1])}return a};b.typeOf=function(a){var b=typeof a;return"object"===b?!a?"null":a instanceof Array?"array":b:b};b.translateEventResponse=function(a,c){var d=b.extend({},c);a==f.events.JWPLAYER_FULLSCREEN&&!d.fullscreen?(d.fullscreen="true"==d.message?!0:!1,delete d.message):"object"==typeof d.data?(d=b.extend(d,d.data),delete d.data):"object"==typeof d.metadata&&b.deepReplaceKeyName(d.metadata,["__dot__","__spc__","__dsh__","__default__"],
["."," ","-","default"]);b.foreach(["position","duration","offset"],function(a,b){d[b]&&(d[b]=Math.round(1E3*d[b])/1E3)});return d};b.flashVersion=function(){if(b.isAndroid())return 0;var a=j.plugins,d;try{if("undefined"!==a&&(d=a["Shockwave Flash"]))return parseInt(d.description.replace(/\D+(\d+)\..*/,"$1"))}catch(c){}if("undefined"!=typeof e.ActiveXObject)try{if(d=new ActiveXObject("ShockwaveFlash.ShockwaveFlash"))return parseInt(d.GetVariable("$version").split(" ")[1].split(",")[0])}catch(f){}return 0};
b.getScriptPath=function(a){for(var b=l.getElementsByTagName("script"),d=0;d<b.length;d++){var c=b[d].src;if(c&&0<=c.indexOf(a))return c.substr(0,c.indexOf(a))}return""};b.deepReplaceKeyName=function(a,d,c){switch(f.utils.typeOf(a)){case "array":for(var j=0;j<a.length;j++)a[j]=f.utils.deepReplaceKeyName(a[j],d,c);break;case "object":b.foreach(a,function(b,h){var j;if(d instanceof Array&&c instanceof Array){if(d.length!=c.length)return;j=d}else j=[d];for(var e=b,l=0;l<j.length;l++)e=e.replace(RegExp(d[l],
"g"),c[l]);a[e]=f.utils.deepReplaceKeyName(h,d,c);b!=e&&delete a[b]})}return a};var d=b.pluginPathType={ABSOLUTE:0,RELATIVE:1,CDN:2};b.getPluginPathType=function(a){if("string"==typeof a){a=a.split("?")[0];var c=a.indexOf("://");if(0<c)return d.ABSOLUTE;var j=a.indexOf("/");a=b.extension(a);return 0>c&&0>j&&(!a||!isNaN(a))?d.CDN:d.RELATIVE}};b.getPluginName=function(a){return a.replace(/^(.*\/)?([^-]*)-?.*\.(swf|js)$/,"$2")};b.getPluginVersion=function(a){return a.replace(/[^-]*-?([^\.]*).*$/,"$1")};
b.isYouTube=function(a){return-1<a.indexOf("youtube.com")||-1<a.indexOf("youtu.be")};b.isRtmp=function(a,b){return 0==a.indexOf("rtmp")||"rtmp"==b};b.foreach=function(a,b){var d,c;for(d in a)a.hasOwnProperty(d)&&(c=a[d],b(d,c))};b.isHTTPS=function(){return 0==e.location.href.indexOf("https")};b.repo=function(){var a=""+f.version.split(/\W/).splice(0,2).join("/")+"/";try{b.isHTTPS()&&(a=a.replace("http://","https://ssl."))}catch(d){}return a}}(jwplayer),function(f){var a="video/",
l=f.foreach,e={mp4:a+"mp4",vorbis:"audio/ogg",ogg:a+"ogg",webm:a+"webm",aac:"audio/mp4",mp3:"audio/mpeg",hls:"application/vnd.apple.mpegurl"},j={mp4:e.mp4,f4v:e.mp4,m4v:e.mp4,mov:e.mp4,m4a:e.aac,f4a:e.aac,aac:e.aac,mp3:e.mp3,ogv:e.ogg,ogg:e.vorbis,oga:e.vorbis,webm:e.webm,m3u8:e.hls,hls:e.hls},a="video",a={flv:a,f4v:a,mov:a,m4a:a,m4v:a,mp4:a,aac:a,f4a:a,mp3:"sound",smil:"rtmp",m3u8:"hls",hls:"hls"},b=f.extensionmap={};l(j,function(a,d){b[a]={html5:d}});l(a,function(a,d){b[a]||(b[a]={});b[a].flash=
d});b.types=e;b.mimeType=function(a){var b;l(e,function(j,e){!b&&e==a&&(b=j)});return b};b.extType=function(a){return b.mimeType(j[a])}}(jwplayer.utils),function(f){var a=f.loaderstatus={NEW:0,LOADING:1,ERROR:2,COMPLETE:3},l=document;f.scriptloader=function(e){function j(){c=a.ERROR;g.sendEvent(d.ERROR)}function b(){c=a.COMPLETE;g.sendEvent(d.COMPLETE)}var c=a.NEW,d=jwplayer.events,g=new d.eventdispatcher;f.extend(this,g);this.load=function(){var g=f.scriptloader.loaders[e];if(g&&(g.getStatus()==
a.NEW||g.getStatus()==a.LOADING))g.addEventListener(d.ERROR,j),g.addEventListener(d.COMPLETE,b);else if(f.scriptloader.loaders[e]=this,c==a.NEW){c=a.LOADING;var m=l.createElement("script");m.addEventListener?(m.onload=b,m.onerror=j):m.readyState&&(m.onreadystatechange=function(){("loaded"==m.readyState||"complete"==m.readyState)&&b()});l.getElementsByTagName("head")[0].appendChild(m);m.src=e}};this.getStatus=function(){return c}};f.scriptloader.loaders={}}(jwplayer.utils),function(f){f.trim=function(a){return a.replace(/^\s*/,
"").replace(/\s*$/,"")};f.pad=function(a,f,e){for(e||(e="0");a.length<f;)a=e+a;return a};f.xmlAttribute=function(a,f){for(var e=0;e<a.attributes.length;e++)if(a.attributes[e].name&&a.attributes[e].name.toLowerCase()==f.toLowerCase())return a.attributes[e].value.toString();return""};f.extension=function(a){if(!a||"rtmp"==a.substr(0,4))return"";a=a.substring(a.lastIndexOf("/")+1,a.length).split("?")[0].split("#")[0];if(-1<a.lastIndexOf("."))return a.substr(a.lastIndexOf(".")+1,a.length).toLowerCase()};
f.stringToColor=function(a){a=a.replace(/(#|0x)?([0-9A-F]{3,6})$/gi,"$2");3==a.length&&(a=a.charAt(0)+a.charAt(0)+a.charAt(1)+a.charAt(1)+a.charAt(2)+a.charAt(2));return parseInt(a,16)}}(jwplayer.utils),function(f){f.key=function(a){var l,e,j;this.edition=function(){return j&&j.getTime()<(new Date).getTime()?"invalid":l};this.token=function(){return e};f.exists(a)||(a="");try{a=f.tea.decrypt(a,"36QXq4W@GSBV^teR");var b=a.split("/");(l=b[0])||(l="free");e=b[1];b[2]&&0<parseInt(b[2])&&(j=new Date,j.setTime(String(b[2])))}catch(c){l=
"invalid"}}}(jwplayer.utils),function(f){var a=f.tea={};a.encrypt=function(j,b){if(0==j.length)return"";var c=a.strToLongs(e.encode(j));1>=c.length&&(c[1]=0);for(var d=a.strToLongs(e.encode(b).slice(0,16)),g=c.length,f=c[g-1],m=c[0],n,k=Math.floor(6+52/g),h=0;0<k--;){h+=2654435769;n=h>>>2&3;for(var r=0;r<g;r++)m=c[(r+1)%g],f=(f>>>5^m<<2)+(m>>>3^f<<4)^(h^m)+(d[r&3^n]^f),f=c[r]+=f}c=a.longsToStr(c);return l.encode(c)};a.decrypt=function(j,b){if(0==j.length)return"";for(var c=a.strToLongs(l.decode(j)),
d=a.strToLongs(e.encode(b).slice(0,16)),g=c.length,f=c[g-1],m=c[0],n,k=2654435769*Math.floor(6+52/g);0!=k;){n=k>>>2&3;for(var h=g-1;0<=h;h--)f=c[0<h?h-1:g-1],f=(f>>>5^m<<2)+(m>>>3^f<<4)^(k^m)+(d[h&3^n]^f),m=c[h]-=f;k-=2654435769}c=a.longsToStr(c);c=c.replace(/\0+$/,"");return e.decode(c)};a.strToLongs=function(a){for(var b=Array(Math.ceil(a.length/4)),c=0;c<b.length;c++)b[c]=a.charCodeAt(4*c)+(a.charCodeAt(4*c+1)<<8)+(a.charCodeAt(4*c+2)<<16)+(a.charCodeAt(4*c+3)<<24);return b};a.longsToStr=function(a){for(var b=
Array(a.length),c=0;c<a.length;c++)b[c]=String.fromCharCode(a[c]&255,a[c]>>>8&255,a[c]>>>16&255,a[c]>>>24&255);return b.join("")};var l={code:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\x3d",encode:function(a,b){var c,d,g,f,m=[],n="",k,h,r=l.code;h=("undefined"==typeof b?0:b)?e.encode(a):a;k=h.length%3;if(0<k)for(;3>k++;)n+="\x3d",h+="\x00";for(k=0;k<h.length;k+=3)c=h.charCodeAt(k),d=h.charCodeAt(k+1),g=h.charCodeAt(k+2),f=c<<16|d<<8|g,c=f>>18&63,d=f>>12&63,g=f>>6&63,f&=63,m[k/
3]=r.charAt(c)+r.charAt(d)+r.charAt(g)+r.charAt(f);m=m.join("");return m=m.slice(0,m.length-n.length)+n},decode:function(a,b){b="undefined"==typeof b?!1:b;var c,d,g,f,m,n=[],k,h=l.code;k=b?e.decode(a):a;for(var r=0;r<k.length;r+=4)c=h.indexOf(k.charAt(r)),d=h.indexOf(k.charAt(r+1)),f=h.indexOf(k.charAt(r+2)),m=h.indexOf(k.charAt(r+3)),g=c<<18|d<<12|f<<6|m,c=g>>>16&255,d=g>>>8&255,g&=255,n[r/4]=String.fromCharCode(c,d,g),64==m&&(n[r/4]=String.fromCharCode(c,d)),64==f&&(n[r/4]=String.fromCharCode(c));
f=n.join("");return b?e.decode(f):f}},e={encode:function(a){a=a.replace(/[\u0080-\u07ff]/g,function(a){a=a.charCodeAt(0);return String.fromCharCode(192|a>>6,128|a&63)});return a=a.replace(/[\u0800-\uffff]/g,function(a){a=a.charCodeAt(0);return String.fromCharCode(224|a>>12,128|a>>6&63,128|a&63)})},decode:function(a){a=a.replace(/[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g,function(a){a=(a.charCodeAt(0)&15)<<12|(a.charCodeAt(1)&63)<<6|a.charCodeAt(2)&63;return String.fromCharCode(a)});return a=
a.replace(/[\u00c0-\u00df][\u0080-\u00bf]/g,function(a){a=(a.charCodeAt(0)&31)<<6|a.charCodeAt(1)&63;return String.fromCharCode(a)})}}}(jwplayer.utils),function(f){f.events={COMPLETE:"COMPLETE",ERROR:"ERROR",API_READY:"jwplayerAPIReady",JWPLAYER_READY:"jwplayerReady",JWPLAYER_FULLSCREEN:"jwplayerFullscreen",JWPLAYER_RESIZE:"jwplayerResize",JWPLAYER_ERROR:"jwplayerError",JWPLAYER_MEDIA_BEFOREPLAY:"jwplayerMediaBeforePlay",JWPLAYER_MEDIA_BEFORECOMPLETE:"jwplayerMediaBeforeComplete",JWPLAYER_COMPONENT_SHOW:"jwplayerComponentShow",
JWPLAYER_COMPONENT_HIDE:"jwplayerComponentHide",JWPLAYER_MEDIA_BUFFER:"jwplayerMediaBuffer",JWPLAYER_MEDIA_BUFFER_FULL:"jwplayerMediaBufferFull",JWPLAYER_MEDIA_ERROR:"jwplayerMediaError",JWPLAYER_MEDIA_LOADED:"jwplayerMediaLoaded",JWPLAYER_MEDIA_COMPLETE:"jwplayerMediaComplete",JWPLAYER_MEDIA_SEEK:"jwplayerMediaSeek",JWPLAYER_MEDIA_TIME:"jwplayerMediaTime",JWPLAYER_MEDIA_VOLUME:"jwplayerMediaVolume",JWPLAYER_MEDIA_META:"jwplayerMediaMeta",JWPLAYER_MEDIA_MUTE:"jwplayerMediaMute",JWPLAYER_MEDIA_LEVELS:"jwplayerMediaLevels",
JWPLAYER_MEDIA_LEVEL_CHANGED:"jwplayerMediaLevelChanged",JWPLAYER_CAPTIONS_CHANGED:"jwplayerCaptionsChanged",JWPLAYER_CAPTIONS_LIST:"jwplayerCaptionsList",JWPLAYER_PLAYER_STATE:"jwplayerPlayerState",state:{BUFFERING:"BUFFERING",IDLE:"IDLE",PAUSED:"PAUSED",PLAYING:"PLAYING"},JWPLAYER_PLAYLIST_LOADED:"jwplayerPlaylistLoaded",JWPLAYER_PLAYLIST_ITEM:"jwplayerPlaylistItem",JWPLAYER_PLAYLIST_COMPLETE:"jwplayerPlaylistComplete",JWPLAYER_DISPLAY_CLICK:"jwplayerViewClick",JWPLAYER_CONTROLS:"jwplayerViewControls",
JWPLAYER_INSTREAM_CLICK:"jwplayerInstreamClicked",JWPLAYER_INSTREAM_DESTROYED:"jwplayerInstreamDestroyed"}}(jwplayer),function(f){var a=jwplayer.utils;f.eventdispatcher=function(f,e){var j,b;this.resetEventListeners=function(){j={};b=[]};this.resetEventListeners();this.addEventListener=function(b,d,g){try{a.exists(j[b])||(j[b]=[]),"string"==a.typeOf(d)&&(d=(new Function("return "+d))()),j[b].push({listener:d,count:g})}catch(e){a.log("error",e)}return!1};this.removeEventListener=function(b,d){if(j[b]){try{for(var g=
0;g<j[b].length;g++)if(j[b][g].listener.toString()==d.toString()){j[b].splice(g,1);break}}catch(e){a.log("error",e)}return!1}};this.addGlobalListener=function(c,d){try{"string"==a.typeOf(c)&&(c=(new Function("return "+c))()),b.push({listener:c,count:d})}catch(g){a.log("error",g)}return!1};this.removeGlobalListener=function(c){if(c){try{for(var d=0;d<b.length;d++)if(b[d].listener.toString()==c.toString()){b.splice(d,1);break}}catch(g){a.log("error",g)}return!1}};this.sendEvent=function(c,d){a.exists(d)||
(d={});a.extend(d,{id:f,version:jwplayer.version,type:c});e&&a.log(c,d);if("undefined"!=a.typeOf(j[c]))for(var g=0;g<j[c].length;g++){try{j[c][g].listener(d)}catch(q){a.log("There was an error while handling a listener: "+q.toString(),j[c][g].listener)}j[c][g]&&(1===j[c][g].count?delete j[c][g]:0<j[c][g].count&&(j[c][g].count-=1))}for(g=0;g<b.length;g++){try{b[g].listener(d)}catch(m){a.log("There was an error while handling a listener: "+m.toString(),b[g].listener)}b[g]&&(1===b[g].count?delete b[g]:
0<b[g].count&&(b[g].count-=1))}}}}(jwplayer.events),function(f){var a={},l={};f.plugins=function(){};f.plugins.loadPlugins=function(e,j){l[e]=new f.plugins.pluginloader(new f.plugins.model(a),j);return l[e]};f.plugins.registerPlugin=function(e,j,b,c){var d=f.utils.getPluginName(e);a[d]||(a[d]=new f.plugins.plugin(e));a[d].registerPlugin(e,j,b,c)}}(jwplayer),function(f){f.plugins.model=function(a){this.addPlugin=function(l){var e=f.utils.getPluginName(l);a[e]||(a[e]=new f.plugins.plugin(l));return a[e]};
this.getPlugins=function(){return a}}}(jwplayer),function(f){var a=jwplayer.utils,l=jwplayer.events;f.pluginmodes={FLASH:0,JAVASCRIPT:1,HYBRID:2};f.plugin=function(e){function j(){switch(a.getPluginPathType(e)){case a.pluginPathType.ABSOLUTE:return e;case a.pluginPathType.RELATIVE:return a.getAbsolutePath(e,window.location.href)}}function b(){n=setTimeout(function(){d=a.loaderstatus.COMPLETE;k.sendEvent(l.COMPLETE)},1E3)}function c(){d=a.loaderstatus.ERROR;k.sendEvent(l.ERROR)}var d=a.loaderstatus.NEW,
g,q,m,n,k=new l.eventdispatcher;a.extend(this,k);this.load=function(){if(d==a.loaderstatus.NEW)if(0<e.lastIndexOf(".swf"))g=e,d=a.loaderstatus.COMPLETE,k.sendEvent(l.COMPLETE);else if(a.getPluginPathType(e)==a.pluginPathType.CDN)d=a.loaderstatus.COMPLETE,k.sendEvent(l.COMPLETE);else{d=a.loaderstatus.LOADING;var h=new a.scriptloader(j());h.addEventListener(l.COMPLETE,b);h.addEventListener(l.ERROR,c);h.load()}};this.registerPlugin=function(b,c,e,j){n&&(clearTimeout(n),n=void 0);m=c;e&&j?(g=j,q=e):"string"==
typeof e?g=e:"function"==typeof e?q=e:!e&&!j&&(g=b);d=a.loaderstatus.COMPLETE;k.sendEvent(l.COMPLETE)};this.getStatus=function(){return d};this.getPluginName=function(){return a.getPluginName(e)};this.getFlashPath=function(){if(g)switch(a.getPluginPathType(g)){case a.pluginPathType.ABSOLUTE:return g;case a.pluginPathType.RELATIVE:return 0<e.lastIndexOf(".swf")?a.getAbsolutePath(g,window.location.href):a.getAbsolutePath(g,j())}return null};this.getJS=function(){return q};this.getTarget=function(){return m};
this.getPluginmode=function(){if("undefined"!=typeof g&&"undefined"!=typeof q)return f.pluginmodes.HYBRID;if("undefined"!=typeof g)return f.pluginmodes.FLASH;if("undefined"!=typeof q)return f.pluginmodes.JAVASCRIPT};this.getNewInstance=function(a,b,d){return new q(a,b,d)};this.getURL=function(){return e}}}(jwplayer.plugins),function(f){var a=f.utils,l=f.events,e=a.foreach;f.plugins.pluginloader=function(j,b){function c(){m?h.sendEvent(l.ERROR,{message:n}):q||(q=!0,g=a.loaderstatus.COMPLETE,h.sendEvent(l.COMPLETE))}
function d(){k||c();if(!q&&!m){var b=0,d=j.getPlugins();a.foreach(k,function(g){g=a.getPluginName(g);var e=d[g];g=e.getJS();var h=e.getTarget(),e=e.getStatus();if(e==a.loaderstatus.LOADING||e==a.loaderstatus.NEW)b++;else if(g&&(!h||parseFloat(h)>parseFloat(f.version)))m=!0,n="Incompatible player version",c()});0==b&&c()}}var g=a.loaderstatus.NEW,q=!1,m=!1,n,k=b,h=new l.eventdispatcher;a.extend(this,h);this.setupPlugins=function(b,d,c){var g={length:0,plugins:{}},h=0,f={},r=j.getPlugins();e(d.plugins,
function(e,j){var k=a.getPluginName(e),l=r[k],m=l.getFlashPath(),n=l.getJS(),q=l.getURL();m&&(g.plugins[m]=a.extend({},j),g.plugins[m].pluginmode=l.getPluginmode(),g.length++);try{if(n&&d.plugins&&d.plugins[q]){var v=document.createElement("div");v.id=b.id+"_"+k;v.style.position="absolute";v.style.top=0;v.style.zIndex=h+10;f[k]=l.getNewInstance(b,a.extend({},d.plugins[q]),v);h++;b.onReady(c(f[k],v,!0));b.onResize(c(f[k],v))}}catch(B){a.log("ERROR: Failed to load "+k+".")}});b.plugins=f;return g};
this.load=function(){if(!(a.exists(b)&&"object"!=a.typeOf(b))){g=a.loaderstatus.LOADING;e(b,function(b){a.exists(b)&&(b=j.addPlugin(b),b.addEventListener(l.COMPLETE,d),b.addEventListener(l.ERROR,r))});var c=j.getPlugins();e(c,function(a,b){b.load()})}d()};var r=this.pluginFailed=function(){m||(m=!0,n="File not found",c())};this.getStatus=function(){return g}}}(jwplayer),function(f){f.playlist=function(a){var l=[];if("array"==f.utils.typeOf(a))for(var e=0;e<a.length;e++)l.push(new f.playlist.item(a[e]));
else l.push(new f.playlist.item(a));return l}}(jwplayer),function(f){var a=f.item=function(l){var e=jwplayer.utils,j=e.extend({},a.defaults,l);j.tracks=e.exists(l.tracks)?l.tracks:[];0==j.sources.length&&(j.sources=[new f.source(j)]);for(var b=0;b<j.sources.length;b++){var c=j.sources[b]["default"];j.sources[b]["default"]=c?"true"==c.toString():!1;j.sources[b]=new f.source(j.sources[b])}if(j.captions&&!e.exists(l.tracks)){for(l=0;l<j.captions.length;l++)j.tracks.push(j.captions[l]);delete j.captions}for(b=
0;b<j.tracks.length;b++)j.tracks[b]=new f.track(j.tracks[b]);return j};a.defaults={description:"",image:"",mediaid:"",title:"",sources:[],tracks:[]}}(jwplayer.playlist),function(f){var a=jwplayer.utils,l={file:void 0,label:void 0,type:void 0,"default":void 0};f.source=function(e){var j=a.extend({},l);a.foreach(l,function(b){a.exists(e[b])&&(j[b]=e[b],delete e[b])});j.type&&0<j.type.indexOf("/")&&(j.type=a.extensionmap.mimeType(j.type));"m3u8"==j.type&&(j.type="hls");"smil"==j.type&&(j.type="rtmp");
return j}}(jwplayer.playlist),function(f){var a=jwplayer.utils,l={file:void 0,label:void 0,kind:"captions","default":!1};f.track=function(e){var j=a.extend({},l);e||(e={});a.foreach(l,function(b){a.exists(e[b])&&(j[b]=e[b],delete e[b])});return j}}(jwplayer.playlist),function(f){var a=f.utils,l=f.events,e=document,j=f.embed=function(b){function c(b,d){a.foreach(d,function(a,d){"function"==typeof b[a]&&b[a].call(b,d)})}function d(a){q(n,t+a.message)}function g(){q(n,t+"No playable sources found")}
function q(b,d){if(m.fallback){var c=b.style;c.backgroundColor="#000";c.color="#FFF";c.width=a.styleDimension(m.width);c.height=a.styleDimension(m.height);c.display="table";c.opacity=1;var c=document.createElement("p"),g=c.style;g.verticalAlign="middle";g.textAlign="center";g.display="table-cell";g.font="15px/20px Arial, Helvetica, sans-serif";c.innerHTML=d.replace(":",":\x3cbr\x3e");b.innerHTML="";b.appendChild(c)}}var m=new j.config(b.config),n,k,h,r=m.width,p=m.height,t="Error loading player: ",
s=f.plugins.loadPlugins(b.id,m.plugins);m.fallbackDiv&&(h=m.fallbackDiv,delete m.fallbackDiv);m.id=b.id;k=e.getElementById(b.id);m.aspectratio?b.config.aspectratio=m.aspectratio:delete b.config.aspectratio;n=e.createElement("div");n.id=k.id;n.style.width=0<r.toString().indexOf("%")?r:r+"px";n.style.height=0<p.toString().indexOf("%")?p:p+"px";k.parentNode.replaceChild(n,k);f.embed.errorScreen=q;s.addEventListener(l.COMPLETE,function(){if("array"==a.typeOf(m.playlist)&&2>m.playlist.length&&(0==m.playlist.length||
!m.playlist[0].sources||0==m.playlist[0].sources.length))g();else if(s.getStatus()==a.loaderstatus.COMPLETE){for(var e=0;e<m.modes.length;e++)if(m.modes[e].type&&j[m.modes[e].type]){var f=a.extend({},m),r=new j[m.modes[e].type](n,m.modes[e],f,s,b);if(r.supportsConfig())return r.addEventListener(l.ERROR,d),r.embed(),c(b,f.events),b}m.fallback?(a.log("No suitable players found and fallback enabled"),new j.download(n,m,g)):(a.log("No suitable players found and fallback disabled"),n.parentNode.replaceChild(h,
n))}});s.addEventListener(l.ERROR,function(a){q(n,"Could not load plugins: "+a.message)});s.load();return b}}(jwplayer),function(f){function a(a){if(a.playlist)for(var c=0;c<a.playlist.length;c++)a.playlist[c]=new j(a.playlist[c]);else{var d={};e.foreach(j.defaults,function(c){l(a,d,c)});d.sources||(a.levels?(d.sources=a.levels,delete a.levels):(c={},l(a,c,"file"),l(a,c,"type"),d.sources=c.file?[c]:[]));a.playlist=[new j(d)]}}function l(a,c,d){e.exists(a[d])&&(c[d]=a[d],delete a[d])}var e=f.utils,
j=f.playlist.item;(f.embed.config=function(b){var c={fallback:!0,height:270,primary:"html5",width:480,base:b.base?b.base:e.getScriptPath("jwplayer.js"),aspectratio:""};b=e.extend(c,f.defaults,b);var c={type:"html5",src:b.base+"jwplayer.html5.js"},d={type:"flash",src:b.base+"jwplayer.flash.swf"};b.modes="flash"==b.primary?[d,c]:[c,d];b.listbar&&(b.playlistsize=b.listbar.size,b.playlistposition=b.listbar.position);b.flashplayer&&(d.src=b.flashplayer);b.html5player&&(c.src=b.html5player);a(b);d=b.aspectratio;
if("string"!=typeof d||!e.exists(d))c=0;else{var g=d.indexOf(":");-1==g?c=0:(c=parseFloat(d.substr(0,g)),d=parseFloat(d.substr(g+1)),c=0>=c||0>=d?0:100*(d/c)+"%")}-1==b.width.toString().indexOf("%")?delete b.aspectratio:c?b.aspectratio=c:delete b.aspectratio;return b}).addConfig=function(b,c){a(c);return e.extend(b,c)}}(jwplayer),function(f){var a=f.utils,l=document;f.embed.download=function(e,f,b){function c(b,d){for(var c=l.querySelectorAll(b),g=0;g<c.length;g++)a.foreach(d,function(a,b){c[g].style[a]=
b})}function d(a,b,d){a=l.createElement(a);b&&(a.className="jwdownload"+b);d&&d.appendChild(a);return a}var g=a.extend({},f),q=g.width?g.width:480,m=g.height?g.height:320,n;f=f.logo?f.logo:{prefix:a.repo(),file:"logo.png",margin:10};var k,h,r,g=g.playlist,p,t=["mp4","aac","mp3"];if(g&&g.length){p=g[0];n=p.sources;for(g=0;g<n.length;g++){var s=n[g],u=s.type?s.type:a.extensionmap.extType(a.extension(s.file));s.file&&a.foreach(t,function(b){u==t[b]?(k=s.file,h=p.image):a.isYouTube(s.file)&&(r=s.file)})}k?
(n=k,b=h,e&&(g=d("a","display",e),d("div","icon",g),d("div","logo",g),n&&g.setAttribute("href",a.getAbsolutePath(n))),g="#"+e.id+" .jwdownload",e.style.width="",e.style.height="",c(g+"display",{width:a.styleDimension(Math.max(320,q)),height:a.styleDimension(Math.max(180,m)),background:"black center no-repeat "+(b?"url("+b+")":""),backgroundSize:"contain",position:"relative",border:"none",display:"block"}),c(g+"display div",{position:"absolute",width:"100%",height:"100%"}),c(g+"logo",{top:f.margin+
"px",right:f.margin+"px",background:"top right no-repeat url("+f.prefix+f.file+")"}),c(g+"icon",{background:"center no-repeat url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAgNJREFUeNrs28lqwkAYB/CZqNVDDj2r6FN41QeIy8Fe+gj6BL275Q08u9FbT8ZdwVfotSBYEPUkxFOoks4EKiJdaDuTjMn3wWBO0V/+sySR8SNSqVRKIR8qaXHkzlqS9jCfzzWcTCYp9hF5o+59sVjsiRzcegSckFzcjT+ruN80TeSlAjCAAXzdJSGPFXRpAAMYwACGZQkSdhG4WCzehMNhqV6vG6vVSrirKVEw66YoSqDb7cqlUilE8JjHd/y1MQefVzqdDmiaJpfLZWHgXMHn8F6vJ1cqlVAkEsGuAn83J4gAd2RZymQygX6/L1erVQt+9ZPWb+CDwcCC2zXGJaewl/DhcHhK3DVj+KfKZrMWvFarcYNLomAv4aPRSFZVlTlcSPA5fDweW/BoNIqFnKV53JvncjkLns/n/cLdS+92O7RYLLgsKfv9/t8XlDn4eDyiw+HA9Jyz2eyt0+kY2+3WFC5hluej0Ha7zQQq9PPwdDq1Et1sNsx/nFBgCqWJ8oAK1aUptNVqcYWewE4nahfU0YQnk4ntUEfGMIU2m01HoLaCKbTRaDgKtaVLk9tBYaBcE/6Artdr4RZ5TB6/dC+9iIe/WgAMYADDpAUJAxjAAAYwgGFZgoS/AtNNTF7Z2bL0BYPBV3Jw5xFwwWcYxgtBP5OkE8i9G7aWGOOCruvauwADALMLMEbKf4SdAAAAAElFTkSuQmCC)"})):
r?(f=r,e=d("embed","",e),e.src="http://www.youtube.com/v/"+/v[=\/](\w*)|\/(\w+)$|^(\w+)$/i.exec(f).slice(1).join(""),e.type="application/x-shockwave-flash",e.width=q,e.height=m):b()}}}(jwplayer),function(f){var a=f.utils,l=f.events,e={};(f.embed.flash=function(j,b,c,d,g){function q(a,b,d){var c=document.createElement("param");c.setAttribute("name",b);c.setAttribute("value",d);a.appendChild(c)}function m(a,b,d){return function(){try{d&&document.getElementById(g.id+"_wrapper").appendChild(b);var c=
document.getElementById(g.id).getPluginConfig("display");"function"==typeof a.resize&&a.resize(c.width,c.height);b.style.left=c.x;b.style.top=c.h}catch(e){}}}function n(b){if(!b)return{};var d={},c=[];a.foreach(b,function(b,g){var e=a.getPluginName(b);c.push(b);a.foreach(g,function(a,b){d[e+"."+a]=b})});d.plugins=c.join(",");return d}var k=new f.events.eventdispatcher,h=a.flashVersion();a.extend(this,k);this.embed=function(){c.id=g.id;if(10>h)return k.sendEvent(l.ERROR,{message:"Flash version must be 10.0 or greater"}),
!1;var f,p,t=g.config.listbar,s=a.extend({},c);if(j.id+"_wrapper"==j.parentNode.id)f=document.getElementById(j.id+"_wrapper");else{f=document.createElement("div");p=document.createElement("div");p.style.display="none";p.id=j.id+"_aspect";f.id=j.id+"_wrapper";f.style.position="relative";f.style.display="block";f.style.width=a.styleDimension(s.width);f.style.height=a.styleDimension(s.height);if(g.config.aspectratio){var u=parseFloat(g.config.aspectratio);p.style.display="block";p.style.marginTop=g.config.aspectratio;
f.style.height="auto";f.style.display="inline-block";t&&("bottom"==t.position?p.style.paddingBottom=t.size+"px":"right"==t.position&&(p.style.marginBottom=-1*t.size*(u/100)+"px"))}j.parentNode.replaceChild(f,j);f.appendChild(j);f.appendChild(p)}f=d.setupPlugins(g,s,m);0<f.length?a.extend(s,n(f.plugins)):delete s.plugins;"undefined"!=typeof s["dock.position"]&&"false"==s["dock.position"].toString().toLowerCase()&&(s.dock=s["dock.position"],delete s["dock.position"]);f=s.wmode?s.wmode:s.height&&40>=
s.height?"transparent":"opaque";p="height width modes events primary base fallback volume".split(" ");for(t=0;t<p.length;t++)delete s[p[t]];p=a.getCookies();a.foreach(p,function(a,b){"undefined"==typeof s[a]&&(s[a]=b)});p=window.location.href.split("/");p.splice(p.length-1,1);p=p.join("/");s.base=p+"/";e[j.id]=s;a.isIE()?(p='\x3cobject classid\x3d"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" " width\x3d"100%" height\x3d"100%"id\x3d"'+j.id+'" name\x3d"'+j.id+'" tabindex\x3d0""\x3e',p+='\x3cparam name\x3d"movie" value\x3d"'+
b.src+'"\x3e',p+='\x3cparam name\x3d"allowfullscreen" value\x3d"true"\x3e\x3cparam name\x3d"allowscriptaccess" value\x3d"always"\x3e',p+='\x3cparam name\x3d"seamlesstabbing" value\x3d"true"\x3e',p+='\x3cparam name\x3d"wmode" value\x3d"'+f+'"\x3e',p+='\x3cparam name\x3d"bgcolor" value\x3d"#000000"\x3e',p+="\x3c/object\x3e",j.outerHTML=p,f=document.getElementById(j.id)):(p=document.createElement("object"),p.setAttribute("type","application/x-shockwave-flash"),p.setAttribute("data",b.src),p.setAttribute("width",
"100%"),p.setAttribute("height","100%"),p.setAttribute("bgcolor","#000000"),p.setAttribute("id",j.id),p.setAttribute("name",j.id),p.setAttribute("tabindex",0),q(p,"allowfullscreen","true"),q(p,"allowscriptaccess","always"),q(p,"seamlesstabbing","true"),q(p,"wmode",f),j.parentNode.replaceChild(p,j),f=p);g.config.aspectratio&&(f.style.position="absolute");g.container=f;g.setPlayer(f,"flash")};this.supportsConfig=function(){if(h)if(c){if("string"==a.typeOf(c.playlist))return!0;try{var b=c.playlist[0].sources;
if("undefined"==typeof b)return!0;for(var d=0;d<b.length;d++){var g;if(g=b[d].file){var e=b[d].file,f=b[d].type;if(a.isYouTube(e)||a.isRtmp(e,f)||"hls"==f)g=!0;else{var j=a.extensionmap[f?f:a.extension(e)];g=!j?!1:!!j.flash}}if(g)return!0}}catch(k){}}else return!0;return!1}}).getVars=function(a){return e[a]}}(jwplayer),function(f){var a=f.utils,l=a.extensionmap,e=f.events;f.embed.html5=function(j,b,c,d,g){function q(a,b,d){return function(){try{var c=document.querySelector("#"+j.id+" .jwmain");d&&
c.appendChild(b);"function"==typeof a.resize&&(a.resize(c.clientWidth,c.clientHeight),setTimeout(function(){a.resize(c.clientWidth,c.clientHeight)},400));b.left=c.style.left;b.top=c.style.top}catch(g){}}}function m(a){n.sendEvent(a.type,{message:"HTML5 player not found"})}var n=this,k=new e.eventdispatcher;a.extend(n,k);n.embed=function(){if(f.html5){d.setupPlugins(g,c,q);j.innerHTML="";var h=f.utils.extend({},c);delete h.volume;h=new f.html5.player(h);g.container=document.getElementById(g.id);g.setPlayer(h,
"html5")}else h=new a.scriptloader(b.src),h.addEventListener(e.ERROR,m),h.addEventListener(e.COMPLETE,n.embed),h.load()};n.supportsConfig=function(){if(f.vid.canPlayType)try{if("string"==a.typeOf(c.playlist))return!0;for(var b=c.playlist[0].sources,d=0;d<b.length;d++){var g;var e=b[d].file,j=b[d].type;if(null!==navigator.userAgent.match(/BlackBerry/i)||a.isAndroid()&&("m3u"==a.extension(e)||"m3u8"==a.extension(e))||a.isRtmp(e,j))g=!1;else{var k=l[j?j:a.extension(e)],m;if(!k||k.flash&&!k.html5)m=!1;
else{var n=k.html5,q=f.vid;if(n)try{m=q.canPlayType(n)?!0:!1}catch(z){m=!1}else m=!0}g=m}if(g)return!0}}catch(A){}return!1}}}(jwplayer),function(f){var a=f.embed,l=f.utils,e=l.extend(function(e){var b=l.repo(),c=l.extend({},f.defaults),d=l.extend({},c,e.config),g=e.config,q=d.plugins,m=d.analytics,n=b+"jwpsrv.js",k=b+"sharing.js",h=b+"related.js",r=b+"gapro.js",c=f.key?f.key:c.key,p="premium"/*(new f.utils.key(c)).edition()*/,q=q?q:{}; /*alert(c);*/ "ads"==p&&d.advertising&&(d.advertising.client.match(".js$|.swf$")?q[d.advertising.client]=
d.advertising:q[b+d.advertising.client+".js"]=d.advertising);delete g.advertising;g.key=c;d.analytics&&(d.analytics.client&&d.analytics.client.match(".js$|.swf$"))&&(n=d.analytics.client);delete g.analytics;if("free"==p||!m||!1!==m.enabled)q[n]=m?m:{};delete q.sharing;delete q.related;if("premium"==p||"ads"==p)d.sharing&&(d.sharing.client&&d.sharing.client.match(".js$|.swf$")&&(k=d.sharing.client),q[k]=d.sharing),d.related&&(d.related.client&&d.related.client.match(".js$|.swf$")&&(h=d.related.client),
q[h]=d.related),d.ga&&(d.ga.client&&d.ga.client.match(".js$|.swf$")&&(r=d.ga.client),q[r]=d.ga),d.skin&&(g.skin=d.skin.replace(/^(beelden|bekle|five|glow|modieus|roundster|stormtrooper|vapor)$/i,l.repo()+"skins/$1.xml"));g.plugins=q;return new a(e)},a);f.embed=e}(jwplayer),function(f){var a=[],l=f.utils,e=f.events,j=e.state,b=document,c=f.api=function(a){function g(a,b){return function(d){return b(a,d)}}function q(a,b){p[a]||(p[a]=[],n(e.JWPLAYER_PLAYER_STATE,function(b){var d=b.newstate;b=b.oldstate;
if(d==a){var c=p[d];if(c)for(var g=0;g<c.length;g++)"function"==typeof c[g]&&c[g].call(this,{oldstate:b,newstate:d})}}));p[a].push(b);return h}function m(a,b){try{a.jwAddEventListener(b,'function(dat) { jwplayer("'+h.id+'").dispatchEvent("'+b+'", dat); }')}catch(d){l.log("Could not add internal listener")}}function n(a,b){r[a]||(r[a]=[],t&&s&&m(t,a));r[a].push(b);return h}function k(){if(s){for(var a=arguments[0],b=[],d=1;d<arguments.length;d++)b.push(arguments[d]);if("undefined"!=typeof t&&"function"==
typeof t[a])switch(b.length){case 4:return t[a](b[0],b[1],b[2],b[3]);case 3:return t[a](b[0],b[1],b[2]);case 2:return t[a](b[0],b[1]);case 1:return t[a](b[0]);default:return t[a]()}return null}u.push(arguments)}var h=this,r={},p={},t=void 0,s=!1,u=[],w=void 0,x={},y={};h.container=a;h.id=a.id;h.getBuffer=function(){return k("jwGetBuffer")};h.getContainer=function(){return h.container};h.addButton=function(a,b,d,c){try{y[c]=d,k("jwDockAddButton",a,b,"jwplayer('"+h.id+"').callback('"+c+"')",c)}catch(g){l.log("Could not add dock button"+
g.message)}};h.removeButton=function(a){k("jwDockRemoveButton",a)};h.callback=function(a){if(y[a])y[a]()};h.forceState=function(a){k("jwForceState",a);return h};h.releaseState=function(){return k("jwReleaseState")};h.getDuration=function(){return k("jwGetDuration")};h.getFullscreen=function(){return k("jwGetFullscreen")};h.getStretching=function(){return k("jwGetStretching")};h.getHeight=function(){return k("jwGetHeight")};h.getLockState=function(){return k("jwGetLockState")};h.getMeta=function(){return h.getItemMeta()};
h.getMute=function(){return k("jwGetMute")};h.getPlaylist=function(){var a=k("jwGetPlaylist");"flash"==h.renderingMode&&l.deepReplaceKeyName(a,["__dot__","__spc__","__dsh__","__default__"],["."," ","-","default"]);return a};h.getPlaylistItem=function(a){l.exists(a)||(a=h.getCurrentItem());return h.getPlaylist()[a]};h.getPosition=function(){return k("jwGetPosition")};h.getRenderingMode=function(){return h.renderingMode};h.getState=function(){return k("jwGetState")};h.getVolume=function(){return k("jwGetVolume")};
h.getWidth=function(){return k("jwGetWidth")};h.setFullscreen=function(a){l.exists(a)?k("jwSetFullscreen",a):k("jwSetFullscreen",!k("jwGetFullscreen"));return h};h.setStretching=function(a){k("jwSetStretching",a);return h};h.setMute=function(a){l.exists(a)?k("jwSetMute",a):k("jwSetMute",!k("jwGetMute"));return h};h.lock=function(){return h};h.unlock=function(){return h};h.load=function(a){k("jwLoad",a);return h};h.playlistItem=function(a){k("jwPlaylistItem",parseInt(a));return h};h.playlistPrev=function(){k("jwPlaylistPrev");
return h};h.playlistNext=function(){k("jwPlaylistNext");return h};h.resize=function(a,d){if("flash"!=h.renderingMode){var c=document.getElementById(h.id);c.className=c.className.replace(/\s+aspectMode/,"");c.style.display="block";k("jwResize",a,d)}else{var c=b.getElementById(h.id+"_wrapper"),g=b.getElementById(h.id+"_aspect");g&&(g.style.display="none");c&&(c.style.display="block",c.style.width=l.styleDimension(a),c.style.height=l.styleDimension(d))}return h};h.play=function(a){"undefined"==typeof a?
(a=h.getState(),a==j.PLAYING||a==j.BUFFERING?k("jwPause"):k("jwPlay")):k("jwPlay",a);return h};h.pause=function(a){"undefined"==typeof a?(a=h.getState(),a==j.PLAYING||a==j.BUFFERING?k("jwPause"):k("jwPlay")):k("jwPause",a);return h};h.stop=function(){k("jwStop");return h};h.seek=function(a){k("jwSeek",a);return h};h.setVolume=function(a){k("jwSetVolume",a);return h};h.loadInstream=function(a,b){return w=new c.instream(this,t,a,b)};h.getQualityLevels=function(){return k("jwGetQualityLevels")};h.getCurrentQuality=
function(){return k("jwGetCurrentQuality")};h.setCurrentQuality=function(a){k("jwSetCurrentQuality",a)};h.getCaptionsList=function(){return k("jwGetCaptionsList")};h.getCurrentCaptions=function(){return k("jwGetCurrentCaptions")};h.setCurrentCaptions=function(a){k("jwSetCurrentCaptions",a)};h.getControls=function(){return k("jwGetControls")};h.getSafeRegion=function(){return k("jwGetSafeRegion")};h.setControls=function(a){k("jwSetControls",a)};h.destroyPlayer=function(){k("jwPlayerDestroy")};var z=
{onBufferChange:e.JWPLAYER_MEDIA_BUFFER,onBufferFull:e.JWPLAYER_MEDIA_BUFFER_FULL,onError:e.JWPLAYER_ERROR,onFullscreen:e.JWPLAYER_FULLSCREEN,onMeta:e.JWPLAYER_MEDIA_META,onMute:e.JWPLAYER_MEDIA_MUTE,onPlaylist:e.JWPLAYER_PLAYLIST_LOADED,onPlaylistItem:e.JWPLAYER_PLAYLIST_ITEM,onPlaylistComplete:e.JWPLAYER_PLAYLIST_COMPLETE,onReady:e.API_READY,onResize:e.JWPLAYER_RESIZE,onComplete:e.JWPLAYER_MEDIA_COMPLETE,onSeek:e.JWPLAYER_MEDIA_SEEK,onTime:e.JWPLAYER_MEDIA_TIME,onVolume:e.JWPLAYER_MEDIA_VOLUME,
onBeforePlay:e.JWPLAYER_MEDIA_BEFOREPLAY,onBeforeComplete:e.JWPLAYER_MEDIA_BEFORECOMPLETE,onDisplayClick:e.JWPLAYER_DISPLAY_CLICK,onControls:e.JWPLAYER_CONTROLS,onQualityLevels:e.JWPLAYER_MEDIA_LEVELS,onQualityChange:e.JWPLAYER_MEDIA_LEVEL_CHANGED,onCaptionsList:e.JWPLAYER_CAPTIONS_LIST,onCaptionsChange:e.JWPLAYER_CAPTIONS_CHANGED};l.foreach(z,function(a){h[a]=g(z[a],n)});var A={onBuffer:j.BUFFERING,onPause:j.PAUSED,onPlay:j.PLAYING,onIdle:j.IDLE};l.foreach(A,function(a){h[a]=g(A[a],q)});h.remove=
function(){if(!s)throw"Cannot call remove() before player is ready";u=[];c.destroyPlayer(this.id)};h.setup=function(a){if(f.embed){var d=b.getElementById(h.id);d&&(a.fallbackDiv=d);d=h;u=[];c.destroyPlayer(d.id);d=f(h.id);d.config=a;return new f.embed(d)}return h};h.registerPlugin=function(a,b,d,c){f.plugins.registerPlugin(a,b,d,c)};h.setPlayer=function(a,b){t=a;h.renderingMode=b};h.detachMedia=function(){if("html5"==h.renderingMode)return k("jwDetachMedia")};h.attachMedia=function(a){if("html5"==
h.renderingMode)return k("jwAttachMedia",a)};h.dispatchEvent=function(a,b){if(r[a])for(var d=l.translateEventResponse(a,b),c=0;c<r[a].length;c++)if("function"==typeof r[a][c])try{a==e.JWPLAYER_PLAYLIST_LOADED&&l.deepReplaceKeyName(d.playlist,["__dot__","__spc__","__dsh__","__default__"],["."," ","-","default"]),r[a][c].call(this,d)}catch(g){l.log("There was an error calling back an event handler")}};h.dispatchInstreamEvent=function(a){w&&w.dispatchEvent(a,arguments)};h.callInternal=k;h.playerReady=
function(a){s=!0;t||h.setPlayer(b.getElementById(a.id));h.container=b.getElementById(h.id);l.foreach(r,function(a){m(t,a)});n(e.JWPLAYER_PLAYLIST_ITEM,function(){x={}});n(e.JWPLAYER_MEDIA_META,function(a){l.extend(x,a.metadata)});for(h.dispatchEvent(e.API_READY);0<u.length;)k.apply(this,u.shift())};h.getItemMeta=function(){return x};h.getCurrentItem=function(){return k("jwGetPlaylistIndex")};return h};c.selectPlayer=function(d){var g;l.exists(d)||(d=0);d.nodeType?g=d:"string"==typeof d&&(g=b.getElementById(d));
return g?(d=c.playerById(g.id))?d:c.addPlayer(new c(g)):"number"==typeof d?a[d]:null};c.playerById=function(b){for(var c=0;c<a.length;c++)if(a[c].id==b)return a[c];return null};c.addPlayer=function(b){for(var c=0;c<a.length;c++)if(a[c]==b)return b;a.push(b);return b};c.destroyPlayer=function(d){for(var c=-1,e,f=0;f<a.length;f++)a[f].id==d&&(c=f,e=a[f]);0<=c&&(d=e.id,f=b.getElementById(d+("flash"==e.renderingMode?"_wrapper":"")),l.clearCss&&l.clearCss("#"+d),f&&("html5"==e.renderingMode&&e.destroyPlayer(),
e=b.createElement("div"),e.id=d,f.parentNode.replaceChild(e,f)),a.splice(c,1));return null};f.playerReady=function(a){var b=f.api.playerById(a.id);b?b.playerReady(a):f.api.selectPlayer(a.id).playerReady(a)}}(jwplayer),function(f){var a=f.events,l=f.utils,e=a.state;f.api.instream=function(f,b,c,d){function g(a,b){k[a]||(k[a]=[],n.jwInstreamAddEventListener(a,'function(dat) { jwplayer("'+m.id+'").dispatchInstreamEvent("'+a+'", dat); }'));k[a].push(b);return this}function q(b,c){h[b]||(h[b]=[],g(a.JWPLAYER_PLAYER_STATE,
function(a){var c=a.newstate,d=a.oldstate;if(c==b){var e=h[c];if(e)for(var f=0;f<e.length;f++)"function"==typeof e[f]&&e[f].call(this,{oldstate:d,newstate:c,type:a.type})}}));h[b].push(c);return this}var m=f,n=b,k={},h={};this.dispatchEvent=function(a,b){if(k[a])for(var c=l.translateEventResponse(a,b[1]),d=0;d<k[a].length;d++)"function"==typeof k[a][d]&&k[a][d].call(this,c)};this.onError=function(b){return g(a.JWPLAYER_ERROR,b)};this.onFullscreen=function(b){return g(a.JWPLAYER_FULLSCREEN,b)};this.onMeta=
function(b){return g(a.JWPLAYER_MEDIA_META,b)};this.onMute=function(b){return g(a.JWPLAYER_MEDIA_MUTE,b)};this.onComplete=function(b){return g(a.JWPLAYER_MEDIA_COMPLETE,b)};this.onTime=function(b){return g(a.JWPLAYER_MEDIA_TIME,b)};this.onBuffer=function(a){return q(e.BUFFERING,a)};this.onPause=function(a){return q(e.PAUSED,a)};this.onPlay=function(a){return q(e.PLAYING,a)};this.onIdle=function(a){return q(e.IDLE,a)};this.onClick=function(b){return g(a.JWPLAYER_INSTREAM_CLICK,b)};this.onInstreamDestroyed=
function(b){return g(a.JWPLAYER_INSTREAM_DESTROYED,b)};this.play=function(a){n.jwInstreamPlay(a)};this.pause=function(a){n.jwInstreamPause(a)};this.destroy=function(){n.jwInstreamDestroy()};m.callInternal("jwLoadInstream",c,d?d:{})}}(jwplayer),function(f){var a=f.api,l=a.selectPlayer;a.selectPlayer=function(a){return(a=l(a))?a:{registerPlugin:function(a,b,c){f.plugins.registerPlugin(a,b,c)}}}}(jwplayer));

View file

@ -12,13 +12,6 @@ function srs_get_player_modal() { return 740; }
function srs_get_player_width() { return srs_get_player_modal() - 30; }
function srs_get_player_height() { return srs_get_player_width() * 9 / 19; }
// get the default vhost for players.
function srs_get_player_vhost() { return "players"; }
// the api server port, for chat room.
function srs_get_api_server_port() { return 8085; }
// the srs http server port
function srs_get_srs_http_server_port() { return 8080; }
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
@ -29,35 +22,24 @@ function srs_get_srs_http_server_port() { return 8080; }
function update_nav() {
$("#srs_index").attr("href", "index.html" + window.location.search);
$("#nav_srs_player").attr("href", "srs_player.html" + window.location.search);
$("#nav_rtc_player").attr("href", "rtc_player.html" + window.location.search);
$("#nav_rtc_publisher").attr("href", "rtc_publisher.html" + window.location.search);
$("#nav_srs_publisher").attr("href", "srs_publisher.html" + window.location.search);
$("#nav_srs_chat").attr("href", "srs_chat.html" + window.location.search);
$("#nav_srs_bwt").attr("href", "srs_bwt.html" + window.location.search);
$("#nav_jwplayer6").attr("href", "jwplayer6.html" + window.location.search);
$("#nav_osmf").attr("href", "osmf.html" + window.location.search);
$("#nav_vlc").attr("href", "vlc.html" + window.location.search);
}
// Special extra params, such as auth_key.
function user_extra_params(query, params) {
var queries = params || [];
var server = (query.server == undefined)? window.location.hostname:query.server;
var vhost = (query.vhost == undefined)? window.location.hostname:query.vhost;
// Note that ossrs.net provides only web service,
// that is migrating to r.ossrs.net
if (vhost == "ossrs.net") {
vhost = "r.ossrs.net";
}
if (server == "ossrs.net") {
server = "r.ossrs.net";
}
for (var key in query.user_query) {
if (key == 'app' || key == 'autostart' || key == 'dir'
|| key == 'filename' || key == 'host' || key == 'hostname'
|| key == 'http_port' || key == 'pathname' || key == 'port'
|| key == 'server' || key == 'stream' || key == 'buffer'
|| key == 'schema' || key == 'vhost'
if (key === 'app' || key === 'autostart' || key === 'dir'
|| key === 'filename' || key === 'host' || key === 'hostname'
|| key === 'http_port' || key === 'pathname' || key === 'port'
|| key === 'server' || key === 'stream' || key === 'buffer'
|| key === 'schema' || key === 'vhost' || key === 'api'
) {
continue;
}
@ -70,75 +52,42 @@ function user_extra_params(query, params) {
return queries;
}
function is_default_port(schema, port) {
return (schema === 'http' && port === 80)
|| (schema === 'https' && port === 443)
|| (schema === 'webrtc' && port === 1985)
|| (schema === 'rtmp' && port === 1935);
}
/**
@param server the ip of server. default to window.location.hostname
@param vhost the vhost of rtmp. default to window.location.hostname
@param port the port of rtmp. default to 1935
@param app the app of rtmp. default to live.
@param stream the stream of rtmp. default to livestream.
@param vhost the vhost of HTTP-FLV. default to window.location.hostname
@param port the port of HTTP-FLV. default to 1935
@param app the app of HTTP-FLV. default to live.
@param stream the stream of HTTP-FLV. default to livestream.flv
*/
function build_default_rtmp_url() {
function build_default_flv_url() {
var query = parse_query_string();
var schema = (!query.schema)? "rtmp":query.schema;
var schema = (!query.schema)? "http":query.schema;
var server = (!query.server)? window.location.hostname:query.server;
var port = (!query.port)? schema=="http"?80:1935:query.port;
var port = (!query.port)? (schema==="http"? 8080:1935) : Number(query.port);
var vhost = (!query.vhost)? window.location.hostname:query.vhost;
var app = (!query.app)? "live":query.app;
var stream = (!query.stream)? "livestream":query.stream;
// Note that ossrs.net provides only web service,
// that is migrating to r.ossrs.net
if (vhost == "ossrs.net") {
vhost = "r.ossrs.net";
}
if (server == "ossrs.net") {
server = "r.ossrs.net";
}
var stream = (!query.stream)? "livestream.flv":query.stream;
var queries = [];
if (server != vhost && vhost != "__defaultVhost__") {
if (server !== vhost && vhost !== "__defaultVhost__") {
queries.push("vhost=" + vhost);
}
queries = user_extra_params(query, queries);
var uri = schema + "://" + server + ":" + port + "/" + app + "/" + stream + "?" + queries.join('&');
while (uri.indexOf("?") == uri.length - 1) {
uri = uri.substr(0, uri.length - 1);
var uri = schema + "://" + server;
if (!is_default_port(schema, port)) {
uri += ":" + port;
}
return uri;
}
// for the chat to init the publish url.
function build_default_publish_rtmp_url() {
var query = parse_query_string();
var schema = (!query.schema)? "rtmp":query.schema;
var server = (!query.server)? window.location.hostname:query.server;
var port = (!query.port)? schema=="http"?80:1935:query.port;
var vhost = (!query.vhost)? window.location.hostname:query.vhost;
var app = (!query.app)? "live":query.app;
var stream = (!query.stream)? "demo":query.stream;
// Note that ossrs.net provides only web service,
// that is migrating to r.ossrs.net
if (vhost == "ossrs.net") {
vhost = "r.ossrs.net";
}
if (server == "ossrs.net") {
server = "r.ossrs.net";
}
var queries = [];
if (server != vhost && vhost != "__defaultVhost__") {
queries.push("vhost=" + vhost);
}
if (query.shp_identify) {
queries.push("shp_identify=" + query.shp_identify);
}
var uri = schema + "://" + server + ":" + port + "/" + app + "/" + stream + "?" + queries.join('&');
while (uri.indexOf("?") == uri.length - 1) {
uri += "/" + app + "/" + stream + "?" + queries.join('&');
while (uri.indexOf("?") === uri.length - 1) {
uri = uri.substr(0, uri.length - 1);
}
@ -148,96 +97,73 @@ function build_default_publish_rtmp_url() {
function build_default_bandwidth_rtmp_url() {
var query = parse_query_string();
var schema = 'rtmp';
var server = (!query.server)? window.location.hostname:query.server;
var port = (!query.port)? 1935:query.port;
var vhost = "bandcheck.srs.com";
var app = (!query.app)? "app":query.app;
var key = (!query.key)? "35c9b402c12a7246868752e2878f7e0e":query.key;
// Note that ossrs.net provides only web service,
// that is migrating to r.ossrs.net
if (vhost == "ossrs.net") {
vhost = "r.ossrs.net";
}
if (server == "ossrs.net") {
server = "r.ossrs.net";
var uri = schema + "://" + server;
if (!is_default_port(schema, port)) {
uri += ":" + port;
}
uri += "/" + app + "?key=" + key + "&vhost=" + vhost;
return "rtmp://" + server + ":" + port + "/" + app + "?key=" + key + "&vhost=" + vhost;
return uri;
}
/**
@param server the ip of server. default to window.location.hostname
@param vhost the vhost of hls. default to window.location.hostname
@param hls_vhost the vhost of hls. override the server if specified.
@param hls_port the port of hls. default to window.location.port
@param app the app of hls. default to live.
@param stream the stream of hls. default to livestream.
*/
function build_default_hls_url() {
var query = parse_query_string();
// Note that ossrs.net provides only web service,
// that is migrating to r.ossrs.net
if (query.hls_vhost == "ossrs.net") {
query.hls_vhost = "r.ossrs.net";
function build_default_rtc_url(query) {
// Use target to overwrite server, vhost and eip.
console.log('?target=x.x.x.x to overwrite server, vhost and eip.');
if (query.target) {
query.server = query.vhost = query.eip = query.target;
query.user_query.eip = query.target;
delete query.target;
}
// for http, use hls_vhost to override server if specified.
var server = window.location.hostname;
if (query.server != undefined) {
server = query.server;
} else if (query.hls_vhost != undefined) {
server = query.hls_vhost;
}
var port = (!query.hls_port)? window.location.port:query.hls_port;
var server = (!query.server)? window.location.hostname:query.server;
var vhost = (!query.vhost)? window.location.hostname:query.vhost;
var app = (!query.app)? "live":query.app;
var stream = (!query.stream)? "demo":query.stream;
var stream = (!query.stream)? "livestream":query.stream;
var api = query.api? ':'+query.api : '';
if (!port) {
port = 8080;
var queries = [];
if (server !== vhost && vhost !== "__defaultVhost__") {
queries.push("vhost=" + vhost);
}
if (stream.indexOf(".flv") >= 0) {
return "http://" + server + ":" + port + "/" + app + "/" + stream;
if (query.schema && window.location.protocol !== query.schema + ':') {
queries.push('schema=' + query.schema);
}
return "http://" + server + ":" + port + "/" + app + "/" + stream + ".m3u8";
}
queries = user_extra_params(query, queries);
var uri = "webrtc://" + server + api + "/" + app + "/" + stream + "?" + queries.join('&');
while (uri.lastIndexOf("?") === uri.length - 1) {
uri = uri.substr(0, uri.length - 1);
}
return uri;
};
/**
* initialize the page.
* @param rtmp_url the div id contains the rtmp stream url to play
* @param flv_url the div id contains the flv stream url to play
* @param hls_url the div id contains the hls stream url to play
* @param modal_player the div id contains the modal player
*/
function srs_init_rtmp(rtmp_url, modal_player) {
srs_init(rtmp_url, null, modal_player);
}
function srs_init_hls(hls_url, modal_player) {
srs_init(null, hls_url, modal_player);
}
function srs_init(rtmp_url, hls_url, modal_player) {
function srs_init_flv(flv_url, modal_player) {
update_nav();
if (rtmp_url) {
$(rtmp_url).val(build_default_rtmp_url());
}
if (hls_url) {
$(hls_url).val(build_default_hls_url());
if (flv_url) {
$(flv_url).val(build_default_flv_url());
}
if (modal_player) {
$(modal_player).width(srs_get_player_modal() + "px");
$(modal_player).css("margin-left", "-" + srs_get_player_modal() / 2 +"px");
}
}
// for the chat to init the publish url.
function srs_init_publish(rtmp_url) {
function srs_init_rtc(id, query) {
update_nav();
if (rtmp_url) {
$(rtmp_url).val(build_default_publish_rtmp_url());
}
$(id).val(build_default_rtc_url(query));
}
// for bw to init url
// url: scheme://host:port/path?query#fragment

View file

@ -3,9 +3,9 @@
/**
* common utilities
* depends: jquery1.10
* https://code.csdn.net/snippets/147103
* https://gitee.com/winlinvip/codes/rpn0c2ewbomj81augzk4y59
* @see: http://blog.csdn.net/win_lin/article/details/17994347
* v 1.0.17
* v 1.0.23
*/
/**
@ -236,9 +236,9 @@ function parse_query_string(){
// parse the query string.
var query_string = String(window.location.search).replace(" ", "").split("?")[1];
if(query_string == undefined){
if(query_string === undefined){
query_string = String(window.location.hash).replace(" ", "").split("#")[1];
if(query_string == undefined){
if(query_string === undefined){
return obj;
}
}
@ -252,7 +252,7 @@ function __fill_query(query_string, obj) {
// pure user query object.
obj.user_query = {};
if (query_string.length == 0) {
if (query_string.length === 0) {
return;
}
@ -293,7 +293,9 @@ function __fill_query(query_string, obj) {
function parse_rtmp_url(rtmp_url) {
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
var a = document.createElement("a");
a.href = rtmp_url.replace("rtmp://", "http://");
a.href = rtmp_url.replace("rtmp://", "http://")
.replace("webrtc://", "http://")
.replace("rtc://", "http://");
var vhost = a.hostname;
var app = a.pathname.substr(1, a.pathname.lastIndexOf("/") - 1);
@ -315,7 +317,7 @@ function parse_rtmp_url(rtmp_url) {
// when vhost equals to server, and server is ip,
// the vhost is __defaultVhost__
if (a.hostname == vhost) {
if (a.hostname === vhost) {
var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
if (re.test(a.hostname)) {
vhost = "__defaultVhost__";
@ -327,7 +329,17 @@ function parse_rtmp_url(rtmp_url) {
if (rtmp_url.indexOf("://") > 0) {
schema = rtmp_url.substr(0, rtmp_url.indexOf("://"));
}
var port = (a.port == "")? (schema=="http"?"80":"1935"):a.port;
var port = a.port;
if (!port) {
if (schema === 'http') {
port = 80;
} else if (schema === 'https') {
port = 443;
} else if (schema === 'rtmp') {
port = 1935;
}
}
var ret = {
url: rtmp_url,
@ -337,6 +349,20 @@ function parse_rtmp_url(rtmp_url) {
};
__fill_query(a.search, ret);
// For webrtc API, we use 443 if page is https, or schema specified it.
if (!ret.port) {
if (schema === 'webrtc' || schema === 'rtc') {
if (ret.user_query.schema === 'https') {
ret.port = 443;
} else if (window.location.href.indexOf('https://') === 0) {
ret.port = 443;
} else {
// For WebRTC, SRS use 1985 as default API port.
ret.port = 1985;
}
}
}
return ret;
}
@ -658,10 +684,3 @@ AsyncRefresh2.prototype.request = function(timeout) {
}, timeout);
}
// other components.
/**
* jquery/bootstrap pager.
* depends: jquery1.10, boostrap2
* https://code.csdn.net/snippets/146160
* @see: http://blog.csdn.net/win_lin/article/details/17628631
*/

View file

@ -1,149 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>SRS</title>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="js/bootstrap.min.js"></script>
<script type="text/javascript" src="js/srs.page.js"></script>
<script type="text/javascript" src="js/srs.log.js"></script>
<script type="text/javascript" src="js/srs.player.js"></script>
<script type="text/javascript" src="js/srs.publisher.js"></script>
<script type="text/javascript" src="js/srs.utility.js"></script>
<script type="text/javascript" src="js/winlin.utility.js"></script>
<style>
body{
padding-top: 55px;
}
#main_modal {
width: 700px;
margin-left: -350px;
}
</style>
<script type="text/javascript" src="js/jwplayer.js" ></script>
<script type='text/javascript'>jwplayer.key = 'N8zhkmYvvRwOhz4aTGkySoEri4x+9pQwR7GHIQ=='; </script>
<script type="text/javascript">
/****
* The parameters for this page:
* schema, the protocol schema, rtmp or http.
* server, the ip of the url.
* port, the rtmp port of url.
* vhost, the vhost of url, can equals to server.
* app, the app of url.
* stream, the stream of url, can endwith .flv or .mp4 or nothing for RTMP.
* autostart, whether auto play the stream.
* Additional params:
* hls_vhost, the vhost for hls.
* hls_port, the port for hls play.
* hls_autostart, whether auto play the hls stream.
*/
var _player = null;
var _url = null;
$(function(){
// get the vhost and port to set the default url.
// for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo
// url set to: rtmp://demo:1935/live/livestream
srs_init("#txt_rtmp_url", "#txt_hls_url", "#main_modal");
$("#main_modal").on("hide", function(){
$("#div_container").remove();
_player.stop();
});
$("#main_modal").on("show", function(){
$("#div_container").remove();
var div_container = $("<div/>");
$(div_container).attr("id", "div_container");
$("#player").append(div_container);
var player = $("<div/>");
$(player).attr("id", "player_id");
$(div_container).append(player);
var conf = {
file: _url,
width: srs_get_player_width(),
height: srs_get_player_height(),
autostart: true,
analytics: { enabled: false}
};
_player = jwplayer('player_id').setup(conf);
});
$("#btn_play_rtmp").click(function(){
_url = $("#txt_rtmp_url").val();
$("#main_modal").modal({show:true, keyboard:false});
});
$("#btn_play_hls").click(function(){
_url = $("#txt_hls_url").val();
$("#main_modal").modal({show:true, keyboard:false});
});
var query = parse_query_string();
if (query.hls_autostart == "true") {
_url = $("#txt_hls_url").val();
$("#main_modal").modal({show:true, keyboard:false});
} else if (query.rtmp_autostart == "true") {
_url = $("#txt_rtmp_url").val();
$("#main_modal").modal({show:true, keyboard:false});
}
});
</script>
</head>
<body>
<img src='https://ossrs.net:8443/gif/v1/sls.gif?site=ossrs.net&path=/player/jwplayer'/>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a id="srs_index" class="brand" href="#">SRS</a>
<div class="nav-collapse collapse">
<ul class="nav">
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
<li class="active"><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
</ul>
</div>
</div>
</div>
</div>
<div class="container">
<div class="alert alert-info fade in">
<button type="button" class="close" data-dismiss="alert">×</button>
<strong><span>Usage:</span></strong> <span>输入地址后点击播放按钮</span>
</div>
<div class="form-inline">
URL:
<input type="text" id="txt_rtmp_url" class="input-xxlarge" value=""></input>
<button class="btn btn-primary" id="btn_play_rtmp">播放RTMP</button>
</div>
<hr/>
<div class="form-inline">
URL:
<input type="text" id="txt_hls_url" class="input-xxlarge" value=""></input>
<button class="btn btn-primary" id="btn_play_hls"> 播放HLS </button>
</div>
<div id="main_modal" class="modal hide fade">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3>JWPlayer6</h3>
</div>
<div class="modal-body" id="player">
</div>
<div class="modal-footer">
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true"> 关闭 </button>
</div>
</div>
<hr>
<footer>
<p><a href="https://github.com/ossrs/srs">SRS Team &copy; 2013</a></p>
</footer>
</div>
</body>

View file

@ -1,125 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>SRS</title>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
<style>
body{
padding-top: 55px;
}
#main_modal {
width: 700px;
margin-left: -350px;
}
</style>
</head>
<body>
<img src='https://ossrs.net:8443/gif/v1/sls.gif?site=ossrs.net&path=/player/osmf'/>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a id="srs_index" class="brand" href="#">SRS</a>
<div class="nav-collapse collapse">
<ul class="nav">
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
<li class="active"><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
</ul>
</div>
</div>
</div>
</div>
<div class="container">
<div class="alert alert-info fade in">
<button type="button" class="close" data-dismiss="alert">×</button>
<strong><span>Usage:</span></strong> <span>输入地址后点击播放按钮</span>
</div>
<div class="form-inline">
URL:
<input type="text" id="txt_url" class="input-xxlarge" value=""></input>
<button class="btn btn-primary" id="btn_play">播放视频</button>
</div>
<div id="main_modal" class="modal hide fade">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3>AdobeOSMF</h3>
</div>
<div class="modal-body" id="player">
</div>
<div class="modal-footer">
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true"> 关闭 </button>
</div>
</div>
<hr>
<footer>
<p><a href="https://github.com/ossrs/srs">SRS Team &copy; 2013</a></p>
</footer>
</div>
</body>
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="js/bootstrap.min.js"></script>
<script type="text/javascript" src="js/swfobject.js"></script>
<script type="text/javascript" src="js/srs.page.js"></script>
<script type="text/javascript" src="js/srs.log.js"></script>
<script type="text/javascript" src="js/srs.player.js"></script>
<script type="text/javascript" src="js/srs.publisher.js"></script>
<script type="text/javascript" src="js/srs.utility.js"></script>
<script type="text/javascript" src="js/winlin.utility.js"></script>
<script type="text/javascript">
function osmf_play(url) {
$("#div_container").remove();
var div_container = $("<div/>");
$(div_container).attr("id", "div_container");
$("#player").append(div_container);
var player = $("<div/>");
$(player).attr("id", "player_id");
$(div_container).append(player);
var flashvars = {};
flashvars.src = url;
flashvars.streamType = "live"; // live or recorded
flashvars.autoPlay = true;
flashvars.controlBarAutoHide = false;
flashvars.scaleMode = "stretch";
flashvars.bufferTime = 0.8;
var params = {};
params.allowFullScreen = true;
var attributes = {};
swfobject.embedSWF(
"js/StrobeMediaPlayback.swf", "player_id",
srs_get_player_width(), srs_get_player_height(),
"11.1", "js/AdobeFlashPlayerInstall.swf",
flashvars, params, attributes
);
}
$(function(){
// get the vhost and port to set the default url.
// for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo
// url set to: rtmp://demo:1935/live/livestream
srs_init_rtmp("#txt_url", "#main_modal");
$("#main_modal").on("hide", function(){
osmf_play("http://localhost");
$("#div_container").remove();
});
$("#main_modal").on("show", function(){
var url = $("#txt_url").val();
osmf_play(url);
});
$("#btn_play").click(function(){
$("#main_modal").modal({show:true, keyboard:false});
});
});
</script>
</html>

View file

@ -0,0 +1,330 @@
<!DOCTYPE html>
<html>
<head>
<title>SRS</title>
<meta charset="utf-8">
<style>
body{
padding-top: 55px;
}
</style>
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="js/adapter-7.4.0.min.js"></script>
<script type="text/javascript" src="js/winlin.utility.js"></script>
<script type="text/javascript" src="js/srs.page.js"></script>
</head>
<body>
<img src='https://ossrs.net/gif/v1/sls.gif?site=ossrs.net&path=/player/rtcplayer'/>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a id="srs_index" class="brand" href="https://github.com/ossrs/srs">SRS</a>
<div class="nav-collapse collapse">
<ul class="nav">
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
<li class="active"><a id="nav_rtc_player" href="rtc_player.html">RTC播放器</a></li>
<li><a id="nav_rtc_publisher" href="rtc_publisher.html">RTC推流</a></li>
<li><a href="http://ossrs.net/srs.release/releases/app.html">iOS/Andriod</a></li>
<!--<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>-->
<!--<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>-->
<!--<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>-->
<!--<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>-->
<li><a id="nav_gb28181" href="srs_gb28181.html">GB28181</a></li>
<li>
<a href="https://github.com/ossrs/srs">
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/ossrs/srs?style=social">
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="container">
<div class="form-inline">
URL:
<input type="text" id="txt_url" class="input-xxlarge" value="">
<button class="btn btn-primary" id="btn_play">播放视频</button>
</div>
<label></label>
<video id="rtc_media_player" controls autoplay></video>
<label></label>
SessionID: <span id='sessionid'></span>
<label></label>
Simulator: <a href='#' id='simulator-drop'>Drop</a>
<footer>
<p></p>
<p><a href="https://github.com/ossrs/srs">SRS Team &copy; 2020</a></p>
</footer>
</div>
<script type="text/javascript">
$(function(){
// Async-await-promise based SRS RTC Player.
function SrsRtcPlayerAsync() {
var self = {};
// @see https://github.com/rtcdn/rtcdn-draft
// @url The WebRTC url to play with, for example:
// webrtc://r.ossrs.net/live/livestream
// or specifies the API port:
// webrtc://r.ossrs.net:11985/live/livestream
// or autostart the play:
// webrtc://r.ossrs.net/live/livestream?autostart=true
// or change the app from live to myapp:
// webrtc://r.ossrs.net:11985/myapp/livestream
// or change the stream from livestream to mystream:
// webrtc://r.ossrs.net:11985/live/mystream
// or set the api server to myapi.domain.com:
// webrtc://myapi.domain.com/live/livestream
// or set the candidate(ip) of answer:
// webrtc://r.ossrs.net/live/livestream?eip=39.107.238.185
// or force to access https API:
// webrtc://r.ossrs.net/live/livestream?schema=https
// or use plaintext, without SRTP:
// webrtc://r.ossrs.net/live/livestream?encrypt=false
// or any other information, will pass-by in the query:
// webrtc://r.ossrs.net/live/livestream?vhost=xxx
// webrtc://r.ossrs.net/live/livestream?token=xxx
self.play = async function(url) {
var conf = self.__internal.prepareUrl(url);
self.pc.addTransceiver("audio", {direction: "recvonly"});
self.pc.addTransceiver("video", {direction: "recvonly"});
var offer = await self.pc.createOffer();
await self.pc.setLocalDescription(offer);
var session = await new Promise(function(resolve, reject) {
// @see https://github.com/rtcdn/rtcdn-draft
var data = {
api: conf.apiUrl, streamurl: conf.streamUrl, clientip: null, sdp: offer.sdp
};
console.log("Generated offer: ", data);
$.ajax({
type: "POST", url: conf.apiUrl, data: JSON.stringify(data),
contentType:'application/json', dataType: 'json'
}).done(function(data) {
console.log("Got answer: ", data);
if (data.code) {
reject(data); return;
}
resolve(data);
}).fail(function(reason){
reject(reason);
});
});
await self.pc.setRemoteDescription(
new RTCSessionDescription({type: 'answer', sdp: session.sdp})
);
return session;
};
// Close the publisher.
self.close = function() {
self.pc.close();
};
// The callback when got remote stream.
self.onaddstream = function (event) {};
// Internal APIs.
self.__internal = {
defaultPath: '/rtc/v1/play/',
prepareUrl: function (webrtcUrl) {
var urlObject = self.__internal.parse(webrtcUrl);
// If user specifies the schema, use it as API schema.
var schema = urlObject.user_query.schema;
schema = schema ? schema + ':' : window.location.protocol;
var port = urlObject.port || 1985;
if (schema === 'https:') {
port = urlObject.port || 443;
}
// @see https://github.com/rtcdn/rtcdn-draft
var api = urlObject.user_query.play || self.__internal.defaultPath;
if (api.lastIndexOf('/') !== api.length - 1) {
api += '/';
}
apiUrl = schema + '//' + urlObject.server + ':' + port + api;
for (var key in urlObject.user_query) {
if (key !== 'api' && key !== 'play') {
apiUrl += '&' + key + '=' + urlObject.user_query[key];
}
}
// Replace /rtc/v1/play/&k=v to /rtc/v1/play/?k=v
var apiUrl = apiUrl.replace(api + '&', api + '?');
var streamUrl = urlObject.url;
return {apiUrl: apiUrl, streamUrl: streamUrl, schema: schema, urlObject: urlObject, port: port};
},
parse: function (url) {
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
var a = document.createElement("a");
a.href = url.replace("rtmp://", "http://")
.replace("webrtc://", "http://")
.replace("rtc://", "http://");
var vhost = a.hostname;
var app = a.pathname.substr(1, a.pathname.lastIndexOf("/") - 1);
var stream = a.pathname.substr(a.pathname.lastIndexOf("/") + 1);
// parse the vhost in the params of app, that srs supports.
app = app.replace("...vhost...", "?vhost=");
if (app.indexOf("?") >= 0) {
var params = app.substr(app.indexOf("?"));
app = app.substr(0, app.indexOf("?"));
if (params.indexOf("vhost=") > 0) {
vhost = params.substr(params.indexOf("vhost=") + "vhost=".length);
if (vhost.indexOf("&") > 0) {
vhost = vhost.substr(0, vhost.indexOf("&"));
}
}
}
// when vhost equals to server, and server is ip,
// the vhost is __defaultVhost__
if (a.hostname === vhost) {
var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
if (re.test(a.hostname)) {
vhost = "__defaultVhost__";
}
}
// parse the schema
var schema = "rtmp";
if (url.indexOf("://") > 0) {
schema = url.substr(0, url.indexOf("://"));
}
var port = a.port;
if (!port) {
if (schema === 'http') {
port = 80;
} else if (schema === 'https') {
port = 443;
} else if (schema === 'rtmp') {
port = 1935;
}
}
var ret = {
url: url,
schema: schema,
server: a.hostname, port: port,
vhost: vhost, app: app, stream: stream
};
self.__internal.fill_query(a.search, ret);
// For webrtc API, we use 443 if page is https, or schema specified it.
if (!ret.port) {
if (schema === 'webrtc' || schema === 'rtc') {
if (ret.user_query.schema === 'https') {
ret.port = 443;
} else if (window.location.href.indexOf('https://') === 0) {
ret.port = 443;
} else {
// For WebRTC, SRS use 1985 as default API port.
ret.port = 1985;
}
}
}
return ret;
},
fill_query: function (query_string, obj) {
// pure user query object.
obj.user_query = {};
if (query_string.length === 0) {
return;
}
// split again for angularjs.
if (query_string.indexOf("?") >= 0) {
query_string = query_string.split("?")[1];
}
var queries = query_string.split("&");
for (var i = 0; i < queries.length; i++) {
var elem = queries[i];
var query = elem.split("=");
obj[query[0]] = query[1];
obj.user_query[query[0]] = query[1];
}
// alias domain for vhost.
if (obj.domain) {
obj.vhost = obj.domain;
}
}
};
self.pc = new RTCPeerConnection(null);
self.pc.onaddstream = function (event) {
if (self.onaddstream) {
self.onaddstream(event);
}
};
return self;
}
var sdk = null; // Global handler to do cleanup when replaying.
var startPlay = function() {
$('#rtc_media_player').show();
// Close PC when user replay.
if (sdk) {
sdk.close();
}
sdk = new SrsRtcPlayerAsync();
sdk.onaddstream = function (event) {
console.log('Start play, event: ', event);
$('#rtc_media_player').prop('srcObject', event.stream);
};
// For example:
// webrtc://r.ossrs.net/live/livestream
var url = $("#txt_url").val();
sdk.play(url).then(function(session){
$('#sessionid').html(session.sessionid);
$('#simulator-drop').attr('href', session.simulator + '?drop=1&username=' + session.sessionid);
}).catch(function (reason) {
sdk.close();
$('#rtc_media_player').hide();
console.error(reason);
});
};
$('#rtc_media_player').hide();
var query = parse_query_string();
srs_init_rtc("#txt_url", query);
$("#btn_play").click(function() {
$('#rtc_media_player').prop('muted', false);
startPlay();
});
if (query.autostart === 'true') {
$('#rtc_media_player').prop('muted', true);
console.warn('For autostart, we should mute it, see https://www.jianshu.com/p/c3c6944eed5a ' +
'or https://developers.google.com/web/updates/2017/09/autoplay-policy-changes#audiovideo_elements');
startPlay();
}
});
</script>
</body>
</html>

View file

@ -0,0 +1,335 @@
<!DOCTYPE html>
<html>
<head>
<title>SRS</title>
<meta charset="utf-8">
<style>
body{
padding-top: 55px;
}
</style>
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="js/adapter-7.4.0.min.js"></script>
<script type="text/javascript" src="js/winlin.utility.js"></script>
<script type="text/javascript" src="js/srs.page.js"></script>
</head>
<body>
<img src='https://ossrs.net/gif/v1/sls.gif?site=ossrs.net&path=/player/rtcpublisher'/>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a id="srs_index" class="brand" href="https://github.com/ossrs/srs">SRS</a>
<div class="nav-collapse collapse">
<ul class="nav">
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
<li><a id="nav_rtc_player" href="rtc_player.html">RTC播放器</a></li>
<li class="active"><a id="nav_rtc_publisher" href="rtc_publisher.html">RTC推流</a></li>
<li><a href="http://ossrs.net/srs.release/releases/app.html">iOS/Andriod</a></li>
<!--<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>-->
<!--<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>-->
<!--<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>-->
<!--<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>-->
<li><a id="nav_gb28181" href="srs_gb28181.html">GB28181</a></li>
<li>
<a href="https://github.com/ossrs/srs">
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/ossrs/srs?style=social">
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="container">
<div class="form-inline">
URL:
<input type="text" id="txt_url" class="input-xxlarge" value="">
<button class="btn btn-primary" id="btn_publish">开始推流</button>
</div>
<label></label>
<video id="rtc_media_player" width="320" autoplay muted></video>
<label></label>
SessionID: <span id='sessionid'></span>
<label></label>
Simulator: <a href='#' id='simulator-drop'>Drop</a>
<footer>
<p></p>
<p><a href="https://github.com/ossrs/srs">SRS Team &copy; 2020</a></p>
</footer>
</div>
<script type="text/javascript">
var pc = null; // Global handler to do cleanup when replaying.
$(function(){
// Async-awat-prmise based SRS RTC Publisher.
function SrsRtcPublisherAsync() {
var self = {};
// @see https://github.com/rtcdn/rtcdn-draft
// @url The WebRTC url to play with, for example:
// webrtc://r.ossrs.net/live/livestream
// or specifies the API port:
// webrtc://r.ossrs.net:11985/live/livestream
// or autostart the publish:
// webrtc://r.ossrs.net/live/livestream?autostart=true
// or change the app from live to myapp:
// webrtc://r.ossrs.net:11985/myapp/livestream
// or change the stream from livestream to mystream:
// webrtc://r.ossrs.net:11985/live/mystream
// or set the api server to myapi.domain.com:
// webrtc://myapi.domain.com/live/livestream
// or set the candidate(ip) of answer:
// webrtc://r.ossrs.net/live/livestream?eip=39.107.238.185
// or force to access https API:
// webrtc://r.ossrs.net/live/livestream?schema=https
// or use plaintext, without SRTP:
// webrtc://r.ossrs.net/live/livestream?encrypt=false
// or any other information, will pass-by in the query:
// webrtc://r.ossrs.net/live/livestream?vhost=xxx
// webrtc://r.ossrs.net/live/livestream?token=xxx
self.publish = async function (url) {
var conf = self.__internal.prepareUrl(url);
self.pc.addTransceiver("audio", {direction: "sendonly"});
self.pc.addTransceiver("video", {direction: "sendonly"});
var stream = await navigator.mediaDevices.getUserMedia(
{audio: true, video: {height: {max: 320}}}
);
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
stream.getTracks().forEach(function (track) {
self.pc.addTrack(track);
});
var offer = await self.pc.createOffer();
await self.pc.setLocalDescription(offer);
var session = await new Promise(function (resolve, reject) {
// @see https://github.com/rtcdn/rtcdn-draft
var data = {
api: conf.apiUrl, streamurl: conf.streamUrl, clientip: null, sdp: offer.sdp
};
console.log("Generated offer: ", data);
$.ajax({
type: "POST", url: conf.apiUrl, data: JSON.stringify(data),
contentType: 'application/json', dataType: 'json'
}).done(function (data) {
console.log("Got answer: ", data);
if (data.code) {
reject(data);
return;
}
resolve(data);
}).fail(function (reason) {
reject(reason);
});
});
await self.pc.setRemoteDescription(
new RTCSessionDescription({type: 'answer', sdp: session.sdp})
);
session.simulator = conf.schema + '//' + conf.urlObject.server + ':' + conf.port + '/rtc/v1/nack/';
// Notify about local stream when success.
self.onaddstream && self.onaddstream({stream: stream});
return session;
};
// Close the publisher.
self.close = function () {
self.pc.close();
};
// The callback when got local stream.
self.onaddstream = function (event) {
};
// Internal APIs.
self.__internal = {
defaultPath: '/rtc/v1/publish/',
prepareUrl: function (webrtcUrl) {
var urlObject = self.__internal.parse(webrtcUrl);
// If user specifies the schema, use it as API schema.
var schema = urlObject.user_query.schema;
schema = schema ? schema + ':' : window.location.protocol;
var port = urlObject.port || 1985;
if (schema === 'https:') {
port = urlObject.port || 443;
}
// @see https://github.com/rtcdn/rtcdn-draft
var api = urlObject.user_query.play || self.__internal.defaultPath;
if (api.lastIndexOf('/') !== api.length - 1) {
api += '/';
}
apiUrl = schema + '//' + urlObject.server + ':' + port + api;
for (var key in urlObject.user_query) {
if (key !== 'api' && key !== 'play') {
apiUrl += '&' + key + '=' + urlObject.user_query[key];
}
}
// Replace /rtc/v1/play/&k=v to /rtc/v1/play/?k=v
var apiUrl = apiUrl.replace(api + '&', api + '?');
var streamUrl = urlObject.url;
return {apiUrl: apiUrl, streamUrl: streamUrl, schema: schema, urlObject: urlObject, port: port};
},
parse: function (url) {
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
var a = document.createElement("a");
a.href = url.replace("rtmp://", "http://")
.replace("webrtc://", "http://")
.replace("rtc://", "http://");
var vhost = a.hostname;
var app = a.pathname.substr(1, a.pathname.lastIndexOf("/") - 1);
var stream = a.pathname.substr(a.pathname.lastIndexOf("/") + 1);
// parse the vhost in the params of app, that srs supports.
app = app.replace("...vhost...", "?vhost=");
if (app.indexOf("?") >= 0) {
var params = app.substr(app.indexOf("?"));
app = app.substr(0, app.indexOf("?"));
if (params.indexOf("vhost=") > 0) {
vhost = params.substr(params.indexOf("vhost=") + "vhost=".length);
if (vhost.indexOf("&") > 0) {
vhost = vhost.substr(0, vhost.indexOf("&"));
}
}
}
// when vhost equals to server, and server is ip,
// the vhost is __defaultVhost__
if (a.hostname === vhost) {
var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
if (re.test(a.hostname)) {
vhost = "__defaultVhost__";
}
}
// parse the schema
var schema = "rtmp";
if (url.indexOf("://") > 0) {
schema = url.substr(0, url.indexOf("://"));
}
var port = a.port;
if (!port) {
if (schema === 'http') {
port = 80;
} else if (schema === 'https') {
port = 443;
} else if (schema === 'rtmp') {
port = 1935;
}
}
var ret = {
url: url,
schema: schema,
server: a.hostname, port: port,
vhost: vhost, app: app, stream: stream
};
self.__internal.fill_query(a.search, ret);
// For webrtc API, we use 443 if page is https, or schema specified it.
if (!ret.port) {
if (schema === 'webrtc' || schema === 'rtc') {
if (ret.user_query.schema === 'https') {
ret.port = 443;
} else if (window.location.href.indexOf('https://') === 0) {
ret.port = 443;
} else {
// For WebRTC, SRS use 1985 as default API port.
ret.port = 1985;
}
}
}
return ret;
},
fill_query: function (query_string, obj) {
// pure user query object.
obj.user_query = {};
if (query_string.length === 0) {
return;
}
// split again for angularjs.
if (query_string.indexOf("?") >= 0) {
query_string = query_string.split("?")[1];
}
var queries = query_string.split("&");
for (var i = 0; i < queries.length; i++) {
var elem = queries[i];
var query = elem.split("=");
obj[query[0]] = query[1];
obj.user_query[query[0]] = query[1];
}
// alias domain for vhost.
if (obj.domain) {
obj.vhost = obj.domain;
}
}
};
self.pc = new RTCPeerConnection(null);
return self;
}
var sdk = null; // Global handler to do cleanup when republishing.
var startPublish = function() {
$('#rtc_media_player').show();
// Close PC when user replay.
if (sdk) {
sdk.close();
}
sdk = new SrsRtcPublisherAsync();
sdk.onaddstream = function (event) {
console.log('Start publish, event: ', event);
$('#rtc_media_player').prop('srcObject', event.stream);
};
// For example:
// webrtc://r.ossrs.net/live/livestream
var url = $("#txt_url").val();
sdk.publish(url).then(function(session){
$('#sessionid').html(session.sessionid);
$('#simulator-drop').attr('href', session.simulator + '?drop=1&username=' + session.sessionid);
}).catch(function (reason) {
sdk.close();
$('#rtc_media_player').hide();
console.error(reason);
});
};
$('#rtc_media_player').hide();
var query = parse_query_string();
srs_init_rtc("#txt_url", query);
$("#btn_publish").click(startPublish);
if (query.autostart === 'true') {
startPublish();
}
});
</script>
</body>
</html>
</html>

View file

@ -15,7 +15,7 @@
</style>
</head>
<body>
<img src='https://ossrs.net:8443/gif/v1/sls.gif?site=ossrs.net&path=/player/bwt'/>
<img src='https://ossrs.net/gif/v1/sls.gif?site=ossrs.net&path=/player/bwt'/>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
@ -23,12 +23,13 @@
<div class="nav-collapse collapse">
<ul class="nav">
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
<li><a id="nav_rtc_player" href="rtc_player.html">RTC播放器</a></li>
<li><a id="nav_rtc_publisher" href="rtc_publisher.html">RTC推流</a></li>
<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
<li class="active"><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
<!--<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>-->
<!--<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>-->
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
<li><a id="nav_gb28181" href="srs_gb28181.html">SRS-GB28181</a></li>
</ul>
</div>
</div>

View file

@ -14,7 +14,7 @@
</style>
</head>
<body>
<img src='https://ossrs.net:8443/gif/v1/sls.gif?site=ossrs.net&path=/player/chat'/>
<img src='https://ossrs.net/gif/v1/sls.gif?site=ossrs.net&path=/player/chat'/>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
@ -22,12 +22,13 @@
<div class="nav-collapse collapse">
<ul class="nav">
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
<li><a id="nav_rtc_player" href="rtc_player.html">RTC播放器</a></li>
<li><a id="nav_rtc_publisher" href="rtc_publisher.html">RTC推流</a></li>
<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
<li class="active"><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
<!--<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>-->
<!--<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>-->
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
<li><a id="nav_gb28181" href="srs_gb28181.html">SRS-GB28181</a></li>
</ul>
</div>
</div>

File diff suppressed because it is too large Load diff

View file

@ -25,7 +25,7 @@
</style>
</head>
<body>
<img src='https://ossrs.net:8443/gif/v1/sls.gif?site=ossrs.net&path=/player/srsplayer'/>
<img src='https://ossrs.net/gif/v1/sls.gif?site=ossrs.net&path=/player/srsplayer'/>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
@ -33,241 +33,73 @@
<div class="nav-collapse collapse">
<ul class="nav">
<li class="active"><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
<!--<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>-->
<!--<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>-->
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
<li><a id="nav_rtc_player" href="rtc_player.html">RTC播放器</a></li>
<li><a id="nav_rtc_publisher" href="rtc_publisher.html">RTC推流</a></li>
<li><a href="http://ossrs.net/srs.release/releases/app.html">iOS/Andriod</a></li>
<!--<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>-->
<!--<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>-->
<!--<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>-->
<!--li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>-->
<li><a id="nav_gb28181" href="srs_gb28181.html">GB28181</a></li>
<li>
<a href="https://github.com/ossrs/srs">
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/ossrs/srs?style=social">
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="container">
<div name="detect_flash">
<div id="main_flash_alert" class="alert alert-danger fade in hide">
<div id="main_content">
<div id="main_info" class="alert alert-info fade in">
<button type="button" class="close" data-dismiss="alert">×</button>
<strong><p>Usage:</p></strong>
<p>
请点击下面的图标启用Flash
</p>
<p>
若没有见到这个图标Chrome浏览器请打开
<span class="text-info">chrome://settings/content/flash</span> 并修改为"Ask first"。
</p>
<strong><span>Usage:</span></strong> <span>输入HTTP-FLV/HLS地址后点击“播放视频”即可播放视频</span>
</div>
<div id="main_flash_hdr" class="hide">
<div class="form-inline">
<div>
URL:
<input type="text" id="txt_url" class="input-xxlarge" value="">
<button class="btn btn-primary" id="btn_play">播放视频</button>
</div>
<p></p>
<div>
<video id="video_player" width="100%" autoplay controls></video>
</div>
<p>
分享:<a href="#" id="link_url" target="_blank">请右键拷贝此链接</a>
</p>
<div id="main_tips">
<p></p>
<p>
推荐的其他播放器:
<ul>
<li><a href="http://bilibili.github.io/flv.js/demo">flv.js</a>H5/MSE播放HTTP-FLV</li>
<li><a href="https://hls-js.netlify.com/demo">hls.js</a>H5/MSE播放HLS</li>
<li><a href="http://reference.dashif.org/dash.js/nightly/samples/dash-if-reference-player/index.html">dash.js</a>H5/MSE播放MPEG-DASH</li>
</ul>
</p>
</div>
</div>
</div>
<div id="main_content" class="hide">
<div class="alert alert-info fade in">
<div name="detect_flash">
<div id="main_flash_alert" class="alert alert-danger fade in hide">
<button type="button" class="close" data-dismiss="alert">×</button>
<strong><span>Usage:</span></strong> <span>输入RTMP/HTTP-FLV/HLS地址后点击“播放视频”即可播放视频</span>
</div>
<div class="form-inline">
URL:
<input type="text" id="txt_url" class="input-xxlarge" value="">
<button class="btn btn-primary" id="btn_play">播放视频</button>
<button class="btn" id="btn_generate_link">生成链接</button>
<p></p>
<p>
推荐的其他播放器:
<ul>
<li><a href="http://bilibili.github.io/flv.js/demo">flv.js</a>H5/MSE播放HTTP-FLV</li>
<li><a href="https://hls-js.netlify.com/demo">hls.js</a>H5/MSE播放HLS</li>
<li><a href="http://reference.dashif.org/dash.js/nightly/samples/dash-if-reference-player/index.html">dash.js</a>H5/MSE播放MPEG-DASH</li>
</ul>
<a href="https://www.adobe.com/products/flashplayer/end-of-life.html" target="_blank">Flash已死</a>
无法播放RTMP流可用VLC播放器播放。
</p>
<ul>
<li>若希望做低延迟直播3-5秒可用HTTP-FLV播放器用<a href="http://bilibili.github.io/flv.js/demo">flv.js</a>H5/MSE播放HTTP-FLV</li>
<li>若对延迟不敏感5-10秒跨平台比较好可用HLS播放器用<a href="https://hls-js.netlify.com/demo">hls.js</a>H5/MSE播放HLS</li>
<li>若希望超低延迟1秒内只需要支持主流的浏览器可用WebRTC播放器用<a href="rtc_player.html">RTC播放器</a></li>
</ul>
<p>
更多信息,参考<a href="https://mp.weixin.qq.com/s/oYn5q4fF9afaged23Ueudg" target="_blank">没有Flash如何做直播</a>
</p>
</div>
<div id="link_modal" class="modal hide fade">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3><a href="https://github.com/ossrs/srs">SRS Link Generator</a></h3>
</div>
<div class="modal-body">
<div class="form-horizontal">
<div class="control-group">
<label class="control-label" for="link_server">服务器地址</label>
<div class="controls">
<span id="link_server" class="span4 uneditable-input"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="link_port">服务器端口</label>
<div class="controls">
<span id="link_port" class="span2 uneditable-input"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="link_vhost">RTMP Vhost</label>
<div class="controls">
<span id="link_vhost" class="span4 uneditable-input"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="link_app">RTMP App</label>
<div class="controls">
<span id="link_app" class="span4 uneditable-input"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="link_stream">RTMP Stream</label>
<div class="controls">
<span id="link_stream" class="span4 uneditable-input"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="link_rtmp">RTMP地址</label>
<div class="controls">
<span id="link_rtmp" class="span4 uneditable-input"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="link_url">播放链接地址</label>
<div class="controls">
<div style="margin-top:5px;"><a href="#" id="link_url" target="_blank">请右键拷贝此链接地址.</a></div>
</div>
</div>
</div>
</div>
<div class="modal-footer"></div>
</div>
<div id="main_modal" class="modal hide fade">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3><a href="https://github.com/ossrs/srs">SrsPlayer</a></h3>
</div>
<div class="modal-body">
<div id="player"></div>
<div class="progress progress-striped active" id="pb_buffer_bg">
<div class="bar" style="width: 0%;" id="pb_buffer"></div>
</div>
</div>
<div class="modal-footer" id="my_modal_footer">
<div>
<div class="btn-group dropup">
<button class="btn dropdown-toggle" data-toggle="dropdown">
全屏比例大小<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a id="btn_fs_size_screen_100" href="#">屏幕大小(100%)</a></li>
<li><a id="btn_fs_size_screen_75" href="#">屏幕大小(75%)</a></li>
<li><a id="btn_fs_size_screen_50" href="#">屏幕大小(50%)</a></li>
<li><a id="btn_fs_size_video_100" href="#">视频大小(100%)</a></li>
<li><a id="btn_fs_size_video_75" href="#">视频大小(75%)</a></li>
<li><a id="btn_fs_size_video_50" href="#">视频大小(50%)</a></li>
</ul>
</div>
<div class="btn-group dropup">
<button class="btn dropdown-toggle" data-toggle="dropdown">显示比例<span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a id="btn_dar_original" href="#">视频原始比例</a></li>
<li><a id="btn_dar_21_9" href="#">宽屏影院(21:9)</a></li>
<li><a id="btn_dar_16_9" href="#">宽屏电视(16:9)</a></li>
<li><a id="btn_dar_4_3" href="#">窄屏(4:3)</a></li>
<li><a id="btn_dar_fill" href="#">填充(容器比例)</a></li>
</ul>
</div>
<div class="btn-group dropup">
<button class="btn dropdown-toggle" data-toggle="dropdown">缓冲区大小<span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a id="btn_bt_0_1" href="#">0.1秒(实时)</a></li>
<li><a id="btn_bt_0_2" href="#">0.2秒(实时)</a></li>
<li><a id="btn_bt_0_3" href="#">0.3秒(实时)</a></li>
<li><a id="btn_bt_0_5" href="#">0.5秒(实时)</a></li>
<li><a id="btn_bt_0_8" href="#">0.8秒(会议)</a></li>
<li><a id="btn_bt_1_0" href="#">1秒(低延迟)</a></li>
<li><a id="btn_bt_2_0" href="#">2秒(较低延时)</a></li>
<li><a id="btn_bt_3_0" href="#">3秒(流畅播放)</a></li>
<li><a id="btn_bt_4_0" href="#">4秒(流畅播放)</a></li>
<li><a id="btn_bt_5_0" href="#">5秒(网速较低)</a></li>
<li><a id="btn_bt_6_0" href="#">6秒(网速较低)</a></li>
<li><a id="btn_bt_8_0" href="#">8秒(网速较低)</a></li>
<li><a id="btn_bt_10_0" href="#">10秒(无所谓延迟)</a></li>
<li><a id="btn_bt_15_0" href="#">15秒(无所谓延迟)</a></li>
<li><a id="btn_bt_20_0" href="#">20秒(无所谓延迟)</a></li>
<li><a id="btn_bt_30_0" href="#">30秒(流畅第一)</a></li>
</ul>
</div>
<div class="btn-group dropup">
<button class="btn dropdown-toggle" data-toggle="dropdown">最大缓冲区<span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a id="btn_mbt_0_6" href="#">0.6秒(实时)</a></li>
<li><a id="btn_mbt_0_9" href="#">0.9秒(实时)</a></li>
<li><a id="btn_mbt_1_2" href="#">1.2秒(实时)</a></li>
<li><a id="btn_mbt_1_5" href="#">1.5秒(实时)</a></li>
<li><a id="btn_mbt_2_4" href="#">2.4秒(会议)</a></li>
<li><a id="btn_mbt_3_0" href="#">3秒(低延迟)</a></li>
<li><a id="btn_mbt_6_0" href="#">6秒(较低延时)</a></li>
<li><a id="btn_mbt_9_0" href="#">9秒(流畅播放)</a></li>
<li><a id="btn_mbt_12_0" href="#">12秒(流畅播放)</a></li>
<li><a id="btn_mbt_15_0" href="#">15秒(网速较低)</a></li>
<li><a id="btn_mbt_18_0" href="#">18秒(网速较低)</a></li>
<li><a id="btn_mbt_24_0" href="#">24秒(网速较低)</a></li>
<li><a id="btn_mbt_30_0" href="#">30秒(无所谓延迟)</a></li>
<li><a id="btn_mbt_45_0" href="#">45秒(无所谓延迟)</a></li>
<li><a id="btn_mbt_60_0" href="#">60秒(无所谓延迟)</a></li>
<li><a id="btn_mbt_90_0" href="#">90秒(流畅第一)</a></li>
</ul>
</div>
<div class="btn-group dropup">
<a id="btn_fullscreen" class="btn">全屏</a>
</div>
<div class="btn-group dropup">
<button id="btn_pause" class="btn">暂停播放</button>
<button id="btn_resume" class="btn hide">继续播放</button>
</div>
<div class="btn-group dropup">
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">关闭播放器</button>
</div>
</div>
<div class="hide" id="fullscreen_tips">
<font color="red">点击视频</font>进入全屏模式~<br/>
由于安全原因Flash全屏无法使用JS触发
</div>
<div>
<div class="input-prepend div_play_time" title="BufferLength/BufferTime/MaxBufferTime">
<span class="add-on">@B</span>
<input class="span2" style="width:80px" id="txt_buffer" type="text" placeholder="0/0/0s">
</div>
<div class="input-prepend div_play_time" title="视频的播放流畅度">
<span class="add-on">@F</span>
<input class="span2" style="width:57px" id="txt_fluency" type="text" placeholder="100%">
</div>
<div class="input-prepend div_play_time" title="视频总共卡顿次数">
<span class="add-on">@E</span>
<input class="span2" style="width:45px" id="txt_empty_count" type="text" placeholder="0">
</div>
<div class="input-prepend div_play_time" title="视频当前的帧率FPS">
<span class="add-on">@F</span>
<input class="span2" style="width:55px" id="txt_fps" type="text" placeholder="fps">
</div>
<div class="input-prepend div_play_time" title="视频当前的码率(视频+音频)单位Kbps">
<span class="add-on">@B</span>
<input class="span2" style="width:55px" id="txt_bitrate" type="text" placeholder="kbps">
</div>
<div class="input-prepend div_play_time" title="播放时长,格式:天 时:分:秒">
<span class="add-on">@T</span>
<input class="span2" style="width:85px" id="txt_time" type="text" placeholder="天 时:分:秒">
</div>
</div>
<div style="margin-top:-12px;">
<span id="debug_info"></span>
URL: <a href="#" id="player_url"></a>
<div class="input-prepend div_play_time" title="当前时间:年-月-日 时:分:秒">
<span class="add-on">@N</span>
<input class="span2" style="width:135px" id="player_clock" type="text" placeholder="年-月-日 时:分:秒">
</div>
</div>
<div>
<div class="input-prepend" title="首播时间,点播放到开始播放的时间,秒">
<span class="add-on">@PST</span>
<input class="span1" style="width:60px" id="txt_pst" type="text" placeholder="N秒">
</div>
</div>
</div>
</div>
</div>
@ -279,439 +111,154 @@
</body>
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="js/bootstrap.min.js"></script>
<script type="text/javascript" src="js/swfobject.js"></script>
<script type="text/javascript" src="js/flv-1.5.0.min.js"></script>
<script type="text/javascript" src="js/hls-0.14.17.min.js"></script>
<script type="text/javascript" src="js/json2.js"></script>
<script type="text/javascript" src="js/srs.page.js"></script>
<script type="text/javascript" src="js/srs.log.js"></script>
<script type="text/javascript" src="js/srs.player.js"></script>
<script type="text/javascript" src="js/srs.publisher.js"></script>
<script type="text/javascript" src="js/srs.utility.js"></script>
<script type="text/javascript" src="js/winlin.utility.js"></script>
<script type="text/javascript">
var __on_flash_ready = null;
var flvPlayer = null;
var hlsPlayer = null;
$(function(){
// 探测Flash是否正常启用。
$('#main_flash_hdr').html(
'\
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="100%" height="100%"> \
<param name="movie" value="srs_player/release/srs_player.swf"> \
<param name="quality" value="autohigh"> \
<param name="swliveconnect" value="true"> \
<param name="allowScriptAccess" value="always"> \
<param name="bgcolor" value="#0"> \
<param name="allowFullScreen" value="true"> \
<param name="wmode" value="opaque"> \
<param name="FlashVars" value="log=1"> \
<param name="flashvars" value="id=1&on_player_ready=__on_flash_ready"> \
<embed src="srs_player/release/srs_player.swf" width="100%" height="100%" \
quality="autohigh" bgcolor="#0" align="middle" allowfullscreen="true" allowscriptaccess="always" \
type="application/x-shockwave-flash" swliveconnect="true" wmode="opaque" \
flashvars="id=1&on_player_ready=__on_flash_ready" \
pluginspage="http://www.macromedia.com/go/getflashplayer"> \
</object> \
'
);
$('#main_flash_hdr').show();
var stopPlayers = function () {
if (flvPlayer) {
flvPlayer.destroy();
flvPlayer = null;
}
if (hlsPlayer) {
hlsPlayer.destroy();
hlsPlayer = null;
}
};
var showFlashHdr = setTimeout(function(){
$('#main_flash_alert').show();
}, 300);
var hide_for_error = function () {
$('#main_flash_alert').show();
$('#main_info').hide();
$('#main_tips').hide();
$('#video_player').hide();
//$('#btn_play').hide();
__on_flash_ready = function (id) {
clearTimeout(showFlashHdr);
stopPlayers();
};
$('#main_flash_alert').hide();
$('#main_flash_hdr').hide();
$('#main_content').show();
var show_for_ok = function () {
$('#main_flash_alert').hide();
$('#main_info').show();
$('#main_tips').show();
$('#video_player').show();
//$('#btn_play').show();
};
autoLoadPage();
};
});
</script>
<script type="text/javascript">
var srs_player = null;
var url = null;
var apply_url_change = function() {
var r = parse_rtmp_url($("#txt_url").val());
var url = window.location.protocol + "//" + query.host + query.pathname + "?autostart=true"
+ "&app=" + r.app + "&stream=" + r.stream + "&server=" + r.server + "&port=" + r.port;
url += (query.shp_identify) ? "&shp_identify=" + query.shp_identify : '';
url += (r.vhost === "__defaultVhost__") ? "&vhost=" + r.server : "&vhost=" + r.vhost;
url += (r.schema !== "rtmp") ? "&schema=" + r.schema : '';
url += (query.buffer) ? "&buffer=" + query.buffer : '';
url += (query.api_port) ? "&api_port=" + query.api_port : '';
var __active_dar = null;
function select_dar(dar_id, num, den) {
srs_player.set_dar(num, den);
var queries = user_extra_params(query);
queries = user_extra_params(r, queries);
if (__active_dar) {
__active_dar.removeClass("active");
if (queries && queries.length) {
url += '&' + queries.join('&');
}
$("#player_url").text($("#txt_url").val()).attr("href", url);
$("#link_url").attr("href", url);
// For RTMP, not support.
if (r.schema === 'rtmp') {
hide_for_error();
return;
}
__active_dar = $(dar_id).parent();
__active_dar.addClass("active");
}
return r;
};
var __active_size = null;
function select_fs_size(size_id, refer, percent) {
srs_player.set_fs(refer, percent);
var start_play = function (r) {
stopPlayers();
if (!r) return;
if (__active_size) {
__active_size.removeClass("active");
}
__active_size = $(size_id).parent();
__active_size.addClass("active");
}
function select_buffer(buffer_time) {
var bt = buffer_time;
var bt_id = "#btn_bt_" + bt.toFixed(1).replace(".", "_");
select_buffer_time(bt_id, bt);
}
function select_max_buffer(max_buffer_time) {
var mbt = max_buffer_time;
var mbt_id = "#btn_mbt_" + mbt.toFixed(1).replace(".", "_");
select_max_buffer_time(mbt_id, mbt);
}
var __active_bt = null;
function select_buffer_time(bt_id, buffer_time) {
srs_player.set_bt(buffer_time);
if (__active_bt) {
__active_bt.removeClass("active");
}
__active_bt = $(bt_id).parent();
__active_bt.addClass("active");
select_max_buffer(srs_player.max_buffer_time);
}
var __active_mbt = null;
function select_max_buffer_time(mbt_id, max_buffer_time) {
srs_player.set_mbt(max_buffer_time);
if (__active_mbt) {
__active_mbt.removeClass("active");
}
__active_mbt = $(mbt_id).parent();
__active_mbt.addClass("active");
}
/****
* The parameters for this page:
* schema, the protocol schema, rtmp or http.
* server, the ip of the url.
* port, the rtmp port of url.
* vhost, the vhost of url, can equals to server.
* app, the app of url.
* stream, the stream of url, can endwith .flv or .mp4 or nothing for RTMP.
* autostart, whether auto play the stream.
* buffer, the buffer time in seconds.
* extra params:
* shp_identify, hls+ param.
* for example:
* http://localhost:8088/players/srs_player.html?vhost=ossrs.net&app=live&stream=livestream&server=ossrs.net&port=1935&autostart=true&schema=rtmp
* http://localhost:8088/players/srs_player.html?vhost=ossrs.net&app=live&stream=livestream.flv&server=ossrs.net&port=8080&autostart=true&schema=http
*/
var autoLoadPage = function() {
var query = parse_query_string();
// get the vhost and port to set the default url.
// for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo
// url set to: rtmp://demo:1935/live/livestream
srs_init_rtmp("#txt_url", "#main_modal");
// consts for buffer and max buffer.
var bts = [0.1, 0.2, 0.3, 0.5, 0.8, 1, 2, 3, 4, 5, 6, 8, 10, 15, 20, 30];
var mbts = [0.6, 0.9, 1.2, 1.5, 2.4, 3, 6, 9, 12, 15, 18, 24, 30, 45, 60, 90];
// the play startup time.
var pst = new Date();
$("#main_modal").on("show", function(){
if (srs_player) {
// Start play HTTP-FLV.
if (r.stream.indexOf('.flv') > 0) {
if (!flvjs.isSupported()) {
hide_for_error();
return;
}
$("#div_container").remove();
$("#debug_info").text("");
show_for_ok();
var div_container = $("<div/>");
$(div_container).attr("id", "div_container");
$("#player").append(div_container);
var player = $("<div/>");
$(player).attr("id", "player_id");
$(div_container).append(player);
srs_player = new SrsPlayer("player_id", srs_get_player_width(), srs_get_player_height());
srs_player.on_player_ready = function() {
var buffer_time = 0.5;
if (url.indexOf('.m3u8') > 0) {
buffer_time = 2;
}
if (query.buffer) {
for (var i = 0; i < bts.length - 1; i++) {
var cur = bts[i];
var next = bts[i+1];
if (Number(query.buffer) >= cur && Number(query.buffer) < next) {
buffer_time = cur;
break;
}
}
}
select_buffer(buffer_time);
this.play(url);
pst = new Date();
};
srs_player.on_player_status = function(code, desc) {
//console.log("[播放器状态] code=" + code + ", desc=" + desc);
};
srs_player.on_player_metadata = function(metadata) {
$("#btn_dar_original").text("视频原始比例" + "(" + metadata.width + ":" + metadata.height + ")");
if (metadata.ip && metadata.pid && metadata.cid) {
$("#debug_info").text("ID:" + metadata.ip + '/' + metadata.pid + '/' + metadata.cid + '');
}
select_dar("#btn_dar_original", 0, 0);
select_fs_size("#btn_fs_size_screen_100", "screen", 100);
};
srs_player.on_player_timer = function(time, buffer_length, kbps, fps, rtime) {
if (time > 0 && pst) {
var diff = (new Date().getTime() - pst.getTime()) / 1000.0;
$("#txt_pst").val(Number(diff).toFixed(2) + "秒");
pst = null;
}
var buffer = buffer_length / this.max_buffer_time * 100;
$("#pb_buffer").width(Number(buffer).toFixed(1) + "%");
$("#pb_buffer_bg").attr("title",
"缓冲区:" + buffer_length.toFixed(1) + "秒, 最大缓冲区:"
+ this.max_buffer_time.toFixed(1) + "秒, 当前:"
+ buffer.toFixed(1) + "%");
var bts = this.buffer_time >= 1? this.buffer_time.toFixed(0) : this.buffer_time.toFixed(1);
var mbts = this.buffer_time >= 1? this.max_buffer_time.toFixed(0) : this.max_buffer_time.toFixed(1);
$("#txt_buffer").val(buffer_length.toFixed(1) + "/" + bts + "/" + mbts + "s");
$("#txt_bitrate").val(kbps.toFixed(0) + "kbps");
$("#txt_fps").val(fps.toFixed(1) + "fps");
$("#txt_empty_count").val(srs_player.empty_count() + "次");
$("#txt_fluency").val(srs_player.fluency().toFixed(2) + "%");
var time_str = "";
// day
time_str = padding(parseInt(time / 24 / 3600), 2, '0') + " ";
// hour
time = time % (24 * 3600);
time_str += padding(parseInt(time / 3600), 2, '0') + ":";
// minute
time = time % (3600);
time_str += padding(parseInt(time / 60), 2, '0') + ":";
// seconds
time = time % (60);
time_str += padding(parseInt(time), 2, '0');
// show
$("#txt_time").val(time_str);
var clock = new Date().getTime() / 1000;
$("#player_clock").val(absolute_seconds_to_YYYYmmdd(clock) + " " + absolute_seconds_to_HHMMSS(clock));
};
srs_player.start();
});
$("#main_modal").on("hide", function(){
if (srs_player) {
// report the log to backend.
//console.log(srs_player.dump_log());
srs_player.stop();
srs_player = null;
}
});
var apply_url_change = function() {
var rtmp = parse_rtmp_url($("#txt_url").val());
var url = "http://" + query.host + query.pathname + "?"
+ "app=" + rtmp.app + "&stream=" + rtmp.stream
+ "&server=" + rtmp.server + "&port=" + rtmp.port
+ "&autostart=true";
if (query.shp_identify) {
url += "&shp_identify=" + query.shp_identify;
}
if (rtmp.vhost == "__defaultVhost__") {
url += "&vhost=" + rtmp.server;
} else {
url += "&vhost=" + rtmp.vhost;
}
if (rtmp.schema == "http") {
url += "&schema=http";
}
if (query.buffer) {
url += "&buffer=" + query.buffer;
}
var queries = user_extra_params(query);
if (queries && queries.length) {
url += '&' + queries.join('&');
}
$("#player_url").text($("#txt_url").val()).attr("href", url);
$("#link_server").text(rtmp.server);
$("#link_port").text(rtmp.port);
$("#link_vhost").text(rtmp.vhost);
$("#link_app").text(rtmp.app);
$("#link_stream").text(rtmp.stream);
$("#link_rtmp").text($("#txt_url").val());
$("#link_url").attr("href", url);
};
$("#txt_url").change(function(){
apply_url_change();
});
$("#btn_generate_link").click(function(){
$("#link_modal").modal({show:true, keyboard:true});
});
$("#btn_play").click(function(){
url = $("#txt_url").val();
$("#main_modal").modal({show:true, keyboard:true});
});
$("#btn_fullscreen").click(function(){
$("#fullscreen_tips").toggle();
});
$("#btn_pause").click(function() {
$("#btn_resume").toggle();
$("#btn_pause").toggle();
srs_player.pause();
});
$("#btn_resume").click(function(){
$("#btn_resume").toggle();
$("#btn_pause").toggle();
srs_player.resume();
});
if (true) {
$("#srs_publish").click(function () {
url = $("#srs_publish").text();
$("#main_modal").modal({show: true, keyboard: false});
});
$("#srs_publish_ld").click(function () {
url = $("#srs_publish_ld").text();
$("#main_modal").modal({show: true, keyboard: false});
});
$("#srs_publish_sd").click(function () {
url = $("#srs_publish_sd").text();
$("#main_modal").modal({show: true, keyboard: false});
});
$("#srs_publish_fw").click(function () {
url = $("#srs_publish_fw").text();
$("#main_modal").modal({show: true, keyboard: false});
});
$("#srs_publish_fw_ld").click(function () {
url = $("#srs_publish_fw_ld").text();
$("#main_modal").modal({show: true, keyboard: false});
});
$("#srs_publish_fw_sd").click(function () {
url = $("#srs_publish_fw_sd").text();
$("#main_modal").modal({show: true, keyboard: false});
});
flvPlayer = flvjs.createPlayer({type: 'flv', url: r.url});
flvPlayer.attachMediaElement(document.getElementById('video_player'));
flvPlayer.load();
flvPlayer.play();
return;
}
var jwplayer_url = "http://" + query.host + query.dir + "/jwplayer6.html?vhost=demo.srs.com&app=live&hls_autostart=true";
if (true) {
$("#srs_publish_hls").attr("href", jwplayer_url + "&stream=livestream");
$("#srs_publish_ld_hls").attr("href", jwplayer_url + "&stream=livestream_ld");
$("#srs_publish_sd_hls").attr("href", jwplayer_url + "&stream=livestream_sd");
var jwplayer_url = "http://" + query.host + query.dir + "/jwplayer6.html?vhost=demo.srs.com&app=forward/live&hls_autostart=true";
$("#srs_publish_fw_hls").attr("href", jwplayer_url + "&stream=livestream");
$("#srs_publish_fw_ld_hls").attr("href", jwplayer_url + "&stream=livestream_ld");
$("#srs_publish_fw_sd_hls").attr("href", jwplayer_url + "&stream=livestream_sd");
}
if (true) {
$("#btn_dar_original").click(function(){
select_dar("#btn_dar_original", 0, 0);
});
$("#btn_dar_21_9").click(function(){
select_dar("#btn_dar_21_9", 21, 9);
});
$("#btn_dar_16_9").click(function(){
select_dar("#btn_dar_16_9", 16, 9);
});
$("#btn_dar_4_3").click(function(){
select_dar("#btn_dar_4_3", 4, 3);
});
$("#btn_dar_fill").click(function(){
select_dar("#btn_dar_fill", -1, -1);
});
}
if (true) {
$("#btn_fs_size_video_100").click(function(){
select_fs_size("#btn_fs_size_video_100", "video", 100);
});
$("#btn_fs_size_video_75").click(function(){
select_fs_size("#btn_fs_size_video_75", "video", 75);
});
$("#btn_fs_size_video_50").click(function(){
select_fs_size("#btn_fs_size_video_50", "video", 50);
});
$("#btn_fs_size_screen_100").click(function(){
select_fs_size("#btn_fs_size_screen_100", "screen", 100);
});
$("#btn_fs_size_screen_75").click(function(){
select_fs_size("#btn_fs_size_screen_75", "screen", 75);
});
$("#btn_fs_size_screen_50").click(function(){
select_fs_size("#btn_fs_size_screen_50", "screen", 50);
});
}
if (true) {
for (var i = 0; i < bts.length; i++) {
var bt = bts[i];
var bt_id = "#btn_bt_" + bt.toFixed(1).replace(".", "_");
var bt_fun = function(id, v){
$(bt_id).click(function(){
select_buffer_time(id, v);
// remember the chagned buffer.
if (Number(query.buffer) != srs_player.buffer_time) {
query.buffer = srs_player.buffer_time;
apply_url_change();
}
});
};
bt_fun(bt_id, bt);
// Start play HLS.
if (r.stream.indexOf('.m3u8') > 0) {
if (!Hls.isSupported()) {
hide_for_error();
return;
}
}
if (true) {
for (var i = 0; i < mbts.length; i++) {
var mbt = mbts[i];
var mbt_id = "#btn_mbt_" + mbt.toFixed(1).replace(".", "_");
var mbt_fun = function(id, v){
$(mbt_id).click(function(){
select_max_buffer_time(id, v);
});
};
mbt_fun(mbt_id, mbt);
}
show_for_ok();
hlsPlayer = new Hls();
hlsPlayer.loadSource(r.url);
hlsPlayer.attachMedia(document.getElementById('video_player'));
return;
}
var query = parse_query_string();
if (query.autostart == "true") {
url = $("#txt_url").val();
$("#main_modal").modal({show:true, keyboard:false});
}
apply_url_change();
console.error('不支持的URL', r.url, r);
$('#video_player').hide();
};
$("#txt_url").change(function(){
apply_url_change();
});
$("#btn_play").click(function(){
$('#video_player').prop('muted', false);
var r = apply_url_change();
start_play(r);
});
/****
* The parameters for this page:
* schema, the protocol schema, rtmp or http.
* server, the ip of the url.
* port, the rtmp port of url.
* vhost, the vhost of url, can equals to server.
* app, the app of url.
* stream, the stream of url, can endwith .flv or .mp4 or nothing for RTMP.
* autostart, whether auto play the stream.
* buffer, the buffer time in seconds.
* extra params:
* shp_identify, hls+ param.
* for example:
* http://localhost:8088/players/srs_player.html?vhost=ossrs.net&app=live&stream=livestream&server=ossrs.net&port=1935&autostart=true&schema=rtmp
* http://localhost:8088/players/srs_player.html?vhost=ossrs.net&app=live&stream=livestream.flv&server=ossrs.net&port=8080&autostart=true&schema=http
*/
var query = parse_query_string();
// get the vhost and port to set the default url.
// url set to: http://localhost:8080/live/livestream.flv
srs_init_flv("#txt_url");
if (query.autostart === "true") {
$('#video_player').prop('muted', true);
console.warn('For autostart, we should mute it, see https://www.jianshu.com/p/c3c6944eed5a ' +
'or https://developers.google.com/web/updates/2017/09/autoplay-policy-changes#audiovideo_elements');
var r = apply_url_change();
start_play(r);
} else {
$('#video_player').hide();
}
</script>
</html>

View file

@ -1369,7 +1369,7 @@ class SrsTsHanlder implements ISrsTsHandler
private function write_h264_ipb_frame(ibps:ByteArray, frame_type:uint, dts:uint, pts:uint, piece:ByteArray):void
{
// when sps or pps not sent, ignore the packet.
// @see https://github.com/winlinvip/simple-rtmp-server/issues/203
// @see https://github.com/ossrs/srs/issues/203
if (video_sh_tag.length == 0) {
return;
}
@ -2197,7 +2197,7 @@ class SrsRawAacStream
// decode the ADTS.
// @see aac-iso-13818-7.pdf, page 26
// 6.2 Audio Data Transport Stream, ADTS
// @see https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-64145885
// @see https://github.com/ossrs/srs/issues/212#issuecomment-64145885
// byte_alignment()
// adts_fixed_header:
@ -2260,7 +2260,7 @@ class SrsRawAacStream
var channel_configuration:uint = (sfiv >> 6) & 0x07;
/*int8_t original = (sfiv >> 5) & 0x01;*/
/*int8_t home = (sfiv >> 4) & 0x01;*/
//int8_t Emphasis; @remark, Emphasis is removed, @see https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-64154736
//int8_t Emphasis; @remark, Emphasis is removed, @see https://github.com/ossrs/srs/issues/212#issuecomment-64154736
// 4bits left.
// adts_variable_header(), 1.A.2.2.2 Variable Header of ADTS
// copyright_identification_bit 1 bslbf
@ -2357,7 +2357,7 @@ class SrsRawAacStream
var samplingFrequencyIndex:uint = codec.sampling_frequency_index;
// override the aac samplerate by user specified.
// @see https://github.com/winlinvip/simple-rtmp-server/issues/212#issuecomment-64146899
// @see https://github.com/ossrs/srs/issues/212#issuecomment-64146899
switch (codec.sound_rate) {
case SrsConsts.SrsCodecAudioSampleRate11025:
samplingFrequencyIndex = 0x0a; break;
@ -2478,7 +2478,7 @@ class SrsEnum
/**
* the aac profile, for ADTS(HLS/TS)
* @see https://github.com/winlinvip/simple-rtmp-server/issues/310
* @see https://github.com/ossrs/srs/issues/310
*/
class SrsAacProfile extends SrsEnum
{
@ -3697,7 +3697,7 @@ class SrsTsAdaptationField
// @remark, for as, should never shift the Number object.
// @remark, use pcr base and ignore the extension
// @see https://github.com/winlinvip/simple-rtmp-server/issues/250#issuecomment-71349370
// @see https://github.com/ossrs/srs/issues/250#issuecomment-71349370
// first 33bits is pcr base.
program_clock_reference_base = (stream.readUnsignedInt() << 1) & 0x1fffffe;
var ch:uint = stream.readUnsignedByte();
@ -3715,7 +3715,7 @@ class SrsTsAdaptationField
}
// @remark, for as, should never shift the Number object.
// @remark, use pcr base and ignore the extension
// @see https://github.com/winlinvip/simple-rtmp-server/issues/250#issuecomment-71349370
// @see https://github.com/ossrs/srs/issues/250#issuecomment-71349370
// first 33bits is pcr base.
original_program_clock_reference_base = (stream.readUnsignedInt() << 1) & 0x1fffffe;
ch = stream.readUnsignedByte();

View file

@ -0,0 +1,720 @@
<!DOCTYPE html>
<html>
<head>
<title>SRS</title>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
<style>
body{
padding-top: 55px;
}
#my_modal_footer {
margin-top: -20px;
padding-top: 3px;
}
#main_modal {
margin-top: -60px;
}
.div_play_time {
margin-top: 10px;
}
#pb_buffer_bg {
margin-top: -4px;
margin-bottom: 10px;
}
</style>
</head>
<body>
<img src='https://ossrs.net/gif/v1/sls.gif?site=ossrs.net&path=/player/srsplayer'/>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a id="srs_index" class="brand" href="#">SRS</a>
<div class="nav-collapse collapse">
<ul class="nav">
<li class="active"><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
<li><a id="nav_rtc_player" href="rtc_player.html">RTC播放器</a></li>
<li><a id="nav_rtc_publisher" href="rtc_publisher.html">RTC推流</a></li>
<li><a href="http://ossrs.net/srs.release/releases/app.html">iOS/Andriod</a></li>
<!--<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>-->
<!--<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>-->
<!--<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>-->
<!--li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>-->
<li><a id="nav_gb28181" href="srs_gb28181.html">GB28181</a></li>
<li>
<a href="https://github.com/ossrs/srs">
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/ossrs/srs?style=social">
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="container">
<div name="detect_flash">
<div id="main_flash_alert" class="alert alert-danger fade in hide">
<button type="button" class="close" data-dismiss="alert">×</button>
<strong><p>Usage:</p></strong>
<p>
请点击下面的图标启用Flash
</p>
<p>
若没有见到这个图标Chrome浏览器请打开
<span class="text-info">chrome://settings/content/flash</span> 并修改为"Ask first"。
</p>
</div>
<div id="main_flash_hdr" class="hide">
</div>
</div>
<div id="main_content" class="hide">
<div class="alert alert-info fade in">
<button type="button" class="close" data-dismiss="alert">×</button>
<strong><span>Usage:</span></strong> <span>输入RTMP/HTTP-FLV/HLS地址后点击“播放视频”即可播放视频</span>
</div>
<div class="form-inline">
URL:
<input type="text" id="txt_url" class="input-xxlarge" value="">
<button class="btn btn-primary" id="btn_play">播放视频</button>
<button class="btn" id="btn_generate_link">生成链接</button>
<p></p>
<p>
推荐的其他播放器:
<ul>
<li><a href="http://bilibili.github.io/flv.js/demo">flv.js</a>H5/MSE播放HTTP-FLV</li>
<li><a href="https://hls-js.netlify.com/demo">hls.js</a>H5/MSE播放HLS</li>
<li><a href="http://reference.dashif.org/dash.js/nightly/samples/dash-if-reference-player/index.html">dash.js</a>H5/MSE播放MPEG-DASH</li>
</ul>
</p>
</div>
<div id="link_modal" class="modal hide fade">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3><a href="https://github.com/ossrs/srs">SRS Link Generator</a></h3>
</div>
<div class="modal-body">
<div class="form-horizontal">
<div class="control-group">
<label class="control-label" for="link_server">服务器地址</label>
<div class="controls">
<span id="link_server" class="span4 uneditable-input"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="link_port">服务器端口</label>
<div class="controls">
<span id="link_port" class="span2 uneditable-input"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="link_vhost">RTMP Vhost</label>
<div class="controls">
<span id="link_vhost" class="span4 uneditable-input"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="link_app">RTMP App</label>
<div class="controls">
<span id="link_app" class="span4 uneditable-input"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="link_stream">RTMP Stream</label>
<div class="controls">
<span id="link_stream" class="span4 uneditable-input"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="link_rtmp">RTMP地址</label>
<div class="controls">
<span id="link_rtmp" class="span4 uneditable-input"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="link_url">播放链接地址</label>
<div class="controls">
<div style="margin-top:5px;"><a href="#" id="link_url" target="_blank">请右键拷贝此链接地址.</a></div>
</div>
</div>
</div>
</div>
<div class="modal-footer"></div>
</div>
<div id="main_modal" class="modal hide fade">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3><a href="https://github.com/ossrs/srs">SrsPlayer</a></h3>
</div>
<div class="modal-body">
<div id="player"></div>
<div class="progress progress-striped active" id="pb_buffer_bg">
<div class="bar" style="width: 0%;" id="pb_buffer"></div>
</div>
</div>
<div class="modal-footer" id="my_modal_footer">
<div>
<div class="btn-group dropup">
<button class="btn dropdown-toggle" data-toggle="dropdown">
全屏比例大小<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a id="btn_fs_size_screen_100" href="#">屏幕大小(100%)</a></li>
<li><a id="btn_fs_size_screen_75" href="#">屏幕大小(75%)</a></li>
<li><a id="btn_fs_size_screen_50" href="#">屏幕大小(50%)</a></li>
<li><a id="btn_fs_size_video_100" href="#">视频大小(100%)</a></li>
<li><a id="btn_fs_size_video_75" href="#">视频大小(75%)</a></li>
<li><a id="btn_fs_size_video_50" href="#">视频大小(50%)</a></li>
</ul>
</div>
<div class="btn-group dropup">
<button class="btn dropdown-toggle" data-toggle="dropdown">显示比例<span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a id="btn_dar_original" href="#">视频原始比例</a></li>
<li><a id="btn_dar_21_9" href="#">宽屏影院(21:9)</a></li>
<li><a id="btn_dar_16_9" href="#">宽屏电视(16:9)</a></li>
<li><a id="btn_dar_4_3" href="#">窄屏(4:3)</a></li>
<li><a id="btn_dar_fill" href="#">填充(容器比例)</a></li>
</ul>
</div>
<div class="btn-group dropup">
<button class="btn dropdown-toggle" data-toggle="dropdown">缓冲区大小<span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a id="btn_bt_0_1" href="#">0.1秒(实时)</a></li>
<li><a id="btn_bt_0_2" href="#">0.2秒(实时)</a></li>
<li><a id="btn_bt_0_3" href="#">0.3秒(实时)</a></li>
<li><a id="btn_bt_0_5" href="#">0.5秒(实时)</a></li>
<li><a id="btn_bt_0_8" href="#">0.8秒(会议)</a></li>
<li><a id="btn_bt_1_0" href="#">1秒(低延迟)</a></li>
<li><a id="btn_bt_2_0" href="#">2秒(较低延时)</a></li>
<li><a id="btn_bt_3_0" href="#">3秒(流畅播放)</a></li>
<li><a id="btn_bt_4_0" href="#">4秒(流畅播放)</a></li>
<li><a id="btn_bt_5_0" href="#">5秒(网速较低)</a></li>
<li><a id="btn_bt_6_0" href="#">6秒(网速较低)</a></li>
<li><a id="btn_bt_8_0" href="#">8秒(网速较低)</a></li>
<li><a id="btn_bt_10_0" href="#">10秒(无所谓延迟)</a></li>
<li><a id="btn_bt_15_0" href="#">15秒(无所谓延迟)</a></li>
<li><a id="btn_bt_20_0" href="#">20秒(无所谓延迟)</a></li>
<li><a id="btn_bt_30_0" href="#">30秒(流畅第一)</a></li>
</ul>
</div>
<div class="btn-group dropup">
<button class="btn dropdown-toggle" data-toggle="dropdown">最大缓冲区<span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a id="btn_mbt_0_6" href="#">0.6秒(实时)</a></li>
<li><a id="btn_mbt_0_9" href="#">0.9秒(实时)</a></li>
<li><a id="btn_mbt_1_2" href="#">1.2秒(实时)</a></li>
<li><a id="btn_mbt_1_5" href="#">1.5秒(实时)</a></li>
<li><a id="btn_mbt_2_4" href="#">2.4秒(会议)</a></li>
<li><a id="btn_mbt_3_0" href="#">3秒(低延迟)</a></li>
<li><a id="btn_mbt_6_0" href="#">6秒(较低延时)</a></li>
<li><a id="btn_mbt_9_0" href="#">9秒(流畅播放)</a></li>
<li><a id="btn_mbt_12_0" href="#">12秒(流畅播放)</a></li>
<li><a id="btn_mbt_15_0" href="#">15秒(网速较低)</a></li>
<li><a id="btn_mbt_18_0" href="#">18秒(网速较低)</a></li>
<li><a id="btn_mbt_24_0" href="#">24秒(网速较低)</a></li>
<li><a id="btn_mbt_30_0" href="#">30秒(无所谓延迟)</a></li>
<li><a id="btn_mbt_45_0" href="#">45秒(无所谓延迟)</a></li>
<li><a id="btn_mbt_60_0" href="#">60秒(无所谓延迟)</a></li>
<li><a id="btn_mbt_90_0" href="#">90秒(流畅第一)</a></li>
</ul>
</div>
<div class="btn-group dropup">
<a id="btn_fullscreen" class="btn">全屏</a>
</div>
<div class="btn-group dropup">
<button id="btn_pause" class="btn">暂停播放</button>
<button id="btn_resume" class="btn hide">继续播放</button>
</div>
<div class="btn-group dropup">
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">关闭播放器</button>
</div>
</div>
<div class="hide" id="fullscreen_tips">
<font color="red">点击视频</font>进入全屏模式~<br/>
由于安全原因Flash全屏无法使用JS触发
</div>
<div>
<div class="input-prepend div_play_time" title="BufferLength/BufferTime/MaxBufferTime">
<span class="add-on">@B</span>
<input class="span2" style="width:80px" id="txt_buffer" type="text" placeholder="0/0/0s">
</div>
<div class="input-prepend div_play_time" title="视频的播放流畅度">
<span class="add-on">@F</span>
<input class="span2" style="width:57px" id="txt_fluency" type="text" placeholder="100%">
</div>
<div class="input-prepend div_play_time" title="视频总共卡顿次数">
<span class="add-on">@E</span>
<input class="span2" style="width:45px" id="txt_empty_count" type="text" placeholder="0">
</div>
<div class="input-prepend div_play_time" title="视频当前的帧率FPS">
<span class="add-on">@F</span>
<input class="span2" style="width:55px" id="txt_fps" type="text" placeholder="fps">
</div>
<div class="input-prepend div_play_time" title="视频当前的码率(视频+音频)单位Kbps">
<span class="add-on">@B</span>
<input class="span2" style="width:55px" id="txt_bitrate" type="text" placeholder="kbps">
</div>
<div class="input-prepend div_play_time" title="播放时长,格式:天 时:分:秒">
<span class="add-on">@T</span>
<input class="span2" style="width:85px" id="txt_time" type="text" placeholder="天 时:分:秒">
</div>
</div>
<div style="margin-top:-12px;">
<span id="debug_info"></span>
URL: <a href="#" id="player_url"></a>
<div class="input-prepend div_play_time" title="当前时间:年-月-日 时:分:秒">
<span class="add-on">@N</span>
<input class="span2" style="width:135px" id="player_clock" type="text" placeholder="年-月-日 时:分:秒">
</div>
</div>
<div>
<div class="input-prepend" title="首播时间,点播放到开始播放的时间,秒">
<span class="add-on">@PST</span>
<input class="span1" style="width:60px" id="txt_pst" type="text" placeholder="N秒">
</div>
</div>
</div>
</div>
</div>
<footer>
<p></p>
<p><a href="https://github.com/ossrs/srs">SRS Team &copy; 2013</a></p>
</footer>
</div>
</body>
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="js/bootstrap.min.js"></script>
<script type="text/javascript" src="js/swfobject.js"></script>
<script type="text/javascript" src="js/json2.js"></script>
<script type="text/javascript" src="js/srs.page.js"></script>
<script type="text/javascript" src="js/srs.log.js"></script>
<script type="text/javascript" src="js/srs.player.js"></script>
<script type="text/javascript" src="js/srs.publisher.js"></script>
<script type="text/javascript" src="js/srs.utility.js"></script>
<script type="text/javascript" src="js/winlin.utility.js"></script>
<script type="text/javascript">
var __on_flash_ready = null;
$(function(){
// 探测Flash是否正常启用。
$('#main_flash_hdr').html(
'\
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="100%" height="100%"> \
<param name="movie" value="srs_player/release/srs_player.swf"> \
<param name="quality" value="autohigh"> \
<param name="swliveconnect" value="true"> \
<param name="allowScriptAccess" value="always"> \
<param name="bgcolor" value="#0"> \
<param name="allowFullScreen" value="true"> \
<param name="wmode" value="opaque"> \
<param name="FlashVars" value="log=1"> \
<param name="flashvars" value="id=1&on_player_ready=__on_flash_ready"> \
<embed src="srs_player/release/srs_player.swf" width="100%" height="100%" \
quality="autohigh" bgcolor="#0" align="middle" allowfullscreen="true" allowscriptaccess="always" \
type="application/x-shockwave-flash" swliveconnect="true" wmode="opaque" \
flashvars="id=1&on_player_ready=__on_flash_ready" \
pluginspage="http://www.macromedia.com/go/getflashplayer"> \
</object> \
'
);
$('#main_flash_hdr').show();
var showFlashHdr = setTimeout(function(){
$('#main_flash_alert').show();
}, 300);
__on_flash_ready = function (id) {
clearTimeout(showFlashHdr);
$('#main_flash_alert').hide();
$('#main_flash_hdr').hide();
$('#main_content').show();
autoLoadPage();
};
});
</script>
<script type="text/javascript">
var srs_player = null;
var url = null;
var __active_dar = null;
function select_dar(dar_id, num, den) {
srs_player.set_dar(num, den);
if (__active_dar) {
__active_dar.removeClass("active");
}
__active_dar = $(dar_id).parent();
__active_dar.addClass("active");
}
var __active_size = null;
function select_fs_size(size_id, refer, percent) {
srs_player.set_fs(refer, percent);
if (__active_size) {
__active_size.removeClass("active");
}
__active_size = $(size_id).parent();
__active_size.addClass("active");
}
function select_buffer(buffer_time) {
var bt = buffer_time;
var bt_id = "#btn_bt_" + bt.toFixed(1).replace(".", "_");
select_buffer_time(bt_id, bt);
}
function select_max_buffer(max_buffer_time) {
var mbt = max_buffer_time;
var mbt_id = "#btn_mbt_" + mbt.toFixed(1).replace(".", "_");
select_max_buffer_time(mbt_id, mbt);
}
var __active_bt = null;
function select_buffer_time(bt_id, buffer_time) {
srs_player.set_bt(buffer_time);
if (__active_bt) {
__active_bt.removeClass("active");
}
__active_bt = $(bt_id).parent();
__active_bt.addClass("active");
select_max_buffer(srs_player.max_buffer_time);
}
var __active_mbt = null;
function select_max_buffer_time(mbt_id, max_buffer_time) {
srs_player.set_mbt(max_buffer_time);
if (__active_mbt) {
__active_mbt.removeClass("active");
}
__active_mbt = $(mbt_id).parent();
__active_mbt.addClass("active");
}
/****
* The parameters for this page:
* schema, the protocol schema, rtmp or http.
* server, the ip of the url.
* port, the rtmp port of url.
* vhost, the vhost of url, can equals to server.
* app, the app of url.
* stream, the stream of url, can endwith .flv or .mp4 or nothing for RTMP.
* autostart, whether auto play the stream.
* buffer, the buffer time in seconds.
* extra params:
* shp_identify, hls+ param.
* for example:
* http://localhost:8088/players/srs_player.html?vhost=ossrs.net&app=live&stream=livestream&server=ossrs.net&port=1935&autostart=true&schema=rtmp
* http://localhost:8088/players/srs_player.html?vhost=ossrs.net&app=live&stream=livestream.flv&server=ossrs.net&port=8080&autostart=true&schema=http
*/
var autoLoadPage = function() {
var query = parse_query_string();
// get the vhost and port to set the default url.
// url set to: http://localhost:8080/live/livestream.flv
srs_init_flv("#txt_url", "#main_modal");
// consts for buffer and max buffer.
var bts = [0.1, 0.2, 0.3, 0.5, 0.8, 1, 2, 3, 4, 5, 6, 8, 10, 15, 20, 30];
var mbts = [0.6, 0.9, 1.2, 1.5, 2.4, 3, 6, 9, 12, 15, 18, 24, 30, 45, 60, 90];
// the play startup time.
var pst = new Date();
$("#main_modal").on("show", function(){
if (srs_player) {
return;
}
$("#div_container").remove();
$("#debug_info").text("");
var div_container = $("<div/>");
$(div_container).attr("id", "div_container");
$("#player").append(div_container);
var player = $("<div/>");
$(player).attr("id", "player_id");
$(div_container).append(player);
apply_url_change();
srs_player = new SrsPlayer("player_id", srs_get_player_width(), srs_get_player_height());
srs_player.on_player_ready = function() {
var buffer_time = 0.5;
if (url.indexOf('.m3u8') > 0) {
buffer_time = 2;
}
if (query.buffer) {
for (var i = 0; i < bts.length - 1; i++) {
var cur = bts[i];
var next = bts[i+1];
if (Number(query.buffer) >= cur && Number(query.buffer) < next) {
buffer_time = cur;
break;
}
}
}
select_buffer(buffer_time);
this.play(url);
pst = new Date();
};
srs_player.on_player_status = function(code, desc) {
//console.log("[播放器状态] code=" + code + ", desc=" + desc);
};
srs_player.on_player_metadata = function(metadata) {
$("#btn_dar_original").text("视频原始比例" + "(" + metadata.width + ":" + metadata.height + ")");
if (metadata.ip && metadata.pid && metadata.cid) {
$("#debug_info").text("ID:" + metadata.ip + '/' + metadata.pid + '/' + metadata.cid + '');
}
select_dar("#btn_dar_original", 0, 0);
select_fs_size("#btn_fs_size_screen_100", "screen", 100);
};
srs_player.on_player_timer = function(time, buffer_length, kbps, fps, rtime) {
if (time > 0 && pst) {
var diff = (new Date().getTime() - pst.getTime()) / 1000.0;
$("#txt_pst").val(Number(diff).toFixed(2) + "秒");
pst = null;
}
var buffer = buffer_length / this.max_buffer_time * 100;
$("#pb_buffer").width(Number(buffer).toFixed(1) + "%");
$("#pb_buffer_bg").attr("title",
"缓冲区:" + buffer_length.toFixed(1) + "秒, 最大缓冲区:"
+ this.max_buffer_time.toFixed(1) + "秒, 当前:"
+ buffer.toFixed(1) + "%");
var bts = this.buffer_time >= 1? this.buffer_time.toFixed(0) : this.buffer_time.toFixed(1);
var mbts = this.buffer_time >= 1? this.max_buffer_time.toFixed(0) : this.max_buffer_time.toFixed(1);
$("#txt_buffer").val(buffer_length.toFixed(1) + "/" + bts + "/" + mbts + "s");
$("#txt_bitrate").val(kbps.toFixed(0) + "kbps");
$("#txt_fps").val(fps.toFixed(1) + "fps");
$("#txt_empty_count").val(srs_player.empty_count() + "次");
$("#txt_fluency").val(srs_player.fluency().toFixed(2) + "%");
var time_str = "";
// day
time_str = padding(parseInt(time / 24 / 3600), 2, '0') + " ";
// hour
time = time % (24 * 3600);
time_str += padding(parseInt(time / 3600), 2, '0') + ":";
// minute
time = time % (3600);
time_str += padding(parseInt(time / 60), 2, '0') + ":";
// seconds
time = time % (60);
time_str += padding(parseInt(time), 2, '0');
// show
$("#txt_time").val(time_str);
var clock = new Date().getTime() / 1000;
$("#player_clock").val(absolute_seconds_to_YYYYmmdd(clock) + " " + absolute_seconds_to_HHMMSS(clock));
};
srs_player.start();
});
$("#main_modal").on("hide", function(){
if (srs_player) {
// report the log to backend.
//console.log(srs_player.dump_log());
srs_player.stop();
srs_player = null;
}
});
var apply_url_change = function() {
var rtmp = parse_rtmp_url($("#txt_url").val());
var schema = window.location.protocol;
var url = schema + "//" + query.host + query.pathname + "?"
+ "app=" + rtmp.app + "&stream=" + rtmp.stream
+ "&server=" + rtmp.server + "&port=" + rtmp.port
+ "&autostart=true";
if (query.shp_identify) {
url += "&shp_identify=" + query.shp_identify;
}
if (rtmp.vhost === "__defaultVhost__") {
url += "&vhost=" + rtmp.server;
} else {
url += "&vhost=" + rtmp.vhost;
}
if (rtmp.schema !== "rtmp") {
url += "&schema=" + rtmp.schema;
}
if (query.buffer) {
url += "&buffer=" + query.buffer;
}
if (query.api_port) {
url += "&api_port=" + query.api_port;
}
var queries = user_extra_params(query);
queries = user_extra_params(rtmp, queries);
if (queries && queries.length) {
url += '&' + queries.join('&');
}
$("#player_url").text($("#txt_url").val()).attr("href", url);
$("#link_server").text(rtmp.server);
$("#link_port").text(rtmp.port);
$("#link_vhost").text(rtmp.vhost);
$("#link_app").text(rtmp.app);
$("#link_stream").text(rtmp.stream);
$("#link_rtmp").text($("#txt_url").val());
$("#link_url").attr("href", url);
};
$("#txt_url").change(function(){
apply_url_change();
});
$("#btn_generate_link").click(function(){
$("#link_modal").modal({show:true, keyboard:true});
});
$("#btn_play").click(function(){
url = $("#txt_url").val();
$("#main_modal").modal({show:true, keyboard:true});
});
$("#btn_fullscreen").click(function(){
$("#fullscreen_tips").toggle();
});
$("#btn_pause").click(function() {
$("#btn_resume").toggle();
$("#btn_pause").toggle();
srs_player.pause();
});
$("#btn_resume").click(function(){
$("#btn_resume").toggle();
$("#btn_pause").toggle();
srs_player.resume();
});
if (true) {
$("#srs_publish").click(function () {
url = $("#srs_publish").text();
$("#main_modal").modal({show: true, keyboard: false});
});
$("#srs_publish_ld").click(function () {
url = $("#srs_publish_ld").text();
$("#main_modal").modal({show: true, keyboard: false});
});
$("#srs_publish_sd").click(function () {
url = $("#srs_publish_sd").text();
$("#main_modal").modal({show: true, keyboard: false});
});
$("#srs_publish_fw").click(function () {
url = $("#srs_publish_fw").text();
$("#main_modal").modal({show: true, keyboard: false});
});
$("#srs_publish_fw_ld").click(function () {
url = $("#srs_publish_fw_ld").text();
$("#main_modal").modal({show: true, keyboard: false});
});
$("#srs_publish_fw_sd").click(function () {
url = $("#srs_publish_fw_sd").text();
$("#main_modal").modal({show: true, keyboard: false});
});
}
if (true) {
$("#btn_dar_original").click(function(){
select_dar("#btn_dar_original", 0, 0);
});
$("#btn_dar_21_9").click(function(){
select_dar("#btn_dar_21_9", 21, 9);
});
$("#btn_dar_16_9").click(function(){
select_dar("#btn_dar_16_9", 16, 9);
});
$("#btn_dar_4_3").click(function(){
select_dar("#btn_dar_4_3", 4, 3);
});
$("#btn_dar_fill").click(function(){
select_dar("#btn_dar_fill", -1, -1);
});
}
if (true) {
$("#btn_fs_size_video_100").click(function(){
select_fs_size("#btn_fs_size_video_100", "video", 100);
});
$("#btn_fs_size_video_75").click(function(){
select_fs_size("#btn_fs_size_video_75", "video", 75);
});
$("#btn_fs_size_video_50").click(function(){
select_fs_size("#btn_fs_size_video_50", "video", 50);
});
$("#btn_fs_size_screen_100").click(function(){
select_fs_size("#btn_fs_size_screen_100", "screen", 100);
});
$("#btn_fs_size_screen_75").click(function(){
select_fs_size("#btn_fs_size_screen_75", "screen", 75);
});
$("#btn_fs_size_screen_50").click(function(){
select_fs_size("#btn_fs_size_screen_50", "screen", 50);
});
}
if (true) {
for (var i = 0; i < bts.length; i++) {
var bt = bts[i];
var bt_id = "#btn_bt_" + bt.toFixed(1).replace(".", "_");
var bt_fun = function(id, v){
$(bt_id).click(function(){
select_buffer_time(id, v);
// remember the chagned buffer.
if (Number(query.buffer) != srs_player.buffer_time) {
query.buffer = srs_player.buffer_time;
apply_url_change();
}
});
};
bt_fun(bt_id, bt);
}
}
if (true) {
for (var i = 0; i < mbts.length; i++) {
var mbt = mbts[i];
var mbt_id = "#btn_mbt_" + mbt.toFixed(1).replace(".", "_");
var mbt_fun = function(id, v){
$(mbt_id).click(function(){
select_max_buffer_time(id, v);
});
};
mbt_fun(mbt_id, mbt);
}
}
var query = parse_query_string();
if (query.autostart == "true") {
url = $("#txt_url").val();
$("#main_modal").modal({show:true, keyboard:false});
}
apply_url_change();
};
</script>
</html>

View file

@ -14,7 +14,7 @@
</style>
</head>
<body>
<img src='https://ossrs.net:8443/gif/v1/sls.gif?site=ossrs.net&path=/player/obs'/>
<img src='https://ossrs.net/gif/v1/sls.gif?site=ossrs.net&path=/player/obs'/>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
@ -22,12 +22,8 @@
<div class="nav-collapse collapse">
<ul class="nav">
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
<li class="active"><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
<!--<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>-->
<!--<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>-->
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
<li><a id="nav_rtc_player" href="rtc_player.html">RTC播放器</a></li>
<li><a id="nav_rtc_publisher" href="rtc_publisher.html">RTC推流</a></li>
</ul>
</div>
</div>
@ -39,8 +35,8 @@
<button type="button" class="close" data-dismiss="alert">×</button>
<strong><span id="txt_log_title">Warning:</span></strong>
<span id="txt_log_msg">
Flash推流已经很少用建议用<a href="https://obsproject.com/" target="_blank">OBS</a><a href="http://ffmpeg.org/" target="_blank">FFMPEG</a>推流,
如果一定要使用Flash推流请点<a id="https_publisher" href="srs_publisher2.html">这里</a>
Flash推流已经很少用建议用<a href="rtc_publisher.html">RTC推流</a><a href="https://obsproject.com/" target="_blank">OBS</a><a href="http://ffmpeg.org/" target="_blank">FFMPEG</a>推流,
如果一定要使用Flash推流请点<a id="https_publisher" href="srs_publisher_flash.html">这里</a>
</span>
</div>
<hr/>
@ -55,12 +51,12 @@
$(function(){
var l = window.location;
var url = window.location.href;
if (l.hostname !== 'localhost' && l.hostname !== '127.0.0.1' && l.protocol == 'http:') {
if (l.hostname !== 'localhost' && l.hostname !== '127.0.0.1' && l.protocol === 'http:') {
// For flash publisher, must use HTTPS.
url = window.location.href.replace('http:', 'https:');
}
url = url.substr(0, url.lastIndexOf('/')) + '/srs_publisher2.html';
url = url.substr(0, url.lastIndexOf('/')) + '/srs_publisher_flash.html';
$('#https_publisher').attr('href', url);
});
</script>

View file

@ -11,7 +11,7 @@
</style>
</head>
<body>
<img src='https://ossrs.net:8443/gif/v1/sls.gif?site=ossrs.net&path=/player/srspublisher'/>
<img src='https://ossrs.net/gif/v1/sls.gif?site=ossrs.net&path=/player/srspublisher'/>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
@ -19,12 +19,6 @@
<div class="nav-collapse collapse">
<ul class="nav">
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
<li class="active"><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
<!--<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>-->
<!--<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>-->
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
</ul>
</div>
</div>
@ -316,9 +310,8 @@
var query = parse_query_string();
var autoLoadPage = function() {
// get the vhost and port to set the default url.
// for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo
// url set to: rtmp://demo:1935/live/livestream
srs_init_rtmp("#txt_url", null);
// url set to: http://localhost:8080/live/livestream.flv
srs_init_flv("#txt_url", null);
if (query.agent == "true") {
document.write(navigator.userAgent);

View file

@ -11,7 +11,7 @@
</style>
</head>
<body>
<img src='https://ossrs.net:8443/gif/v1/sls.gif?site=ossrs.net&path=/player/vlc'/>
<img src='https://ossrs.net/gif/v1/sls.gif?site=ossrs.net&path=/player/vlc'/>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
@ -19,12 +19,13 @@
<div class="nav-collapse collapse">
<ul class="nav">
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
<li><a id="nav_rtc_player" href="rtc_player.html">RTC播放器</a></li>
<li><a id="nav_rtc_publisher" href="rtc_publisher.html">RTC推流</a></li>
<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
<li><a id="nav_srs_chat" href="srs_chat.html">SRS会议</a></li>
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
<!--<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>-->
<!--<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>-->
<li class="active"><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
<li><a id="nav_gb28181" href="srs_gb28181.html">SRS-GB28181</a></li>
</ul>
</div>
</div>

View file

@ -1,85 +1,4 @@
#!/bin/bash
cat <<END >>/dev/null
touch git-ensure-commit &&
echo "cd `pwd` &&" >git-ensure-commit &&
echo "bash `pwd`/git.commit.sh" >>git-ensure-commit &&
chmod +x git-ensure-commit &&
sudo rm -f /bin/git-ensure-commit &&
sudo mv git-ensure-commit /usr/local/bin/git-ensure-commit
END
for file in $(git remote); do echo ""; git push $file; done
echo "submit code to github.com"
echo "argv[0]=$0"
if [[ ! -f $0 ]]; then
echo "directly execute the scripts on shell.";
work_dir=`pwd`
else
echo "execute scripts in file: $0";
work_dir=`dirname $0`; work_dir=`(cd ${work_dir} && pwd)`
fi
work_dir=`(cd ${work_dir}/.. && pwd)`
product_dir=$work_dir
# allow start script from any dir
cd $work_dir && work_branch=`git branch|grep "*"|awk '{print $2}'` && commit_branch=2.0release && git checkout $commit_branch
ret=$ret; if [[ $ret -ne 0 ]]; then echo "switch branch failed. ret=$ret"; exit $ret; fi
echo "work branch is $work_branch"
echo "commit branch is $commit_branch"
. ${product_dir}/scripts/_log.sh
ret=$?; if [[ $ret -ne 0 ]]; then exit $ret; fi
ok_msg "导入脚本成功"
function remote_check()
{
remote=$1
url=$2
git remote -v| grep "$url" >/dev/null 2>&1
ret=$?; if [[ 0 -ne $ret ]]; then
echo "remote $remote not found, add by:"
echo " git remote add $remote $url"
exit -1
fi
ok_msg "remote $remote ok, url is $url"
}
remote_check origin git@github.com:ossrs/srs.git
remote_check srs.winlin git@github.com:winlinvip/simple-rtmp-server.git
remote_check srs.csdn git@code.csdn.net:winlinvip/srs-csdn.git
remote_check srs.oschina git@git.oschina.net:winlinvip/srs.oschina.git
remote_check srs.gitlab git@gitlab.com:winlinvip/srs-gitlab.git
ok_msg "remote check ok"
function sync_push()
{
for ((;;)); do
git push $*
ret=$?; if [[ 0 -ne $ret ]]; then
failed_msg "Retry for failed: git push $*"
sleep 3
continue
else
ok_msg "Success: git push $*"
fi
break
done
}
sync_push origin
sync_push srs.winlin
sync_push srs.csdn
sync_push srs.oschina
sync_push srs.gitlab
ok_msg "push branches ok"
sync_push --tags srs.winlin
sync_push --tags srs.csdn
sync_push --tags srs.oschina
sync_push --tags srs.gitlab
ok_msg "push tags ok"
git checkout $work_branch
ok_msg "switch to work branch $work_branch"
exit 0

View file

@ -26,8 +26,8 @@ for option
do
case "$option" in
-*=*)
value=`echo "$option" | sed -e 's|[-_a-zA-Z0-9/]*=||'`
option=`echo "$option" | sed -e 's|=[-_a-zA-Z0-9/]*||'`
value=`echo "$option" | sed -e 's|[-_a-zA-Z0-9/]*=||'`
option=`echo "$option" | sed -e 's|=[-_a-zA-Z0-9/~]*||'`
;;
*) value="" ;;
esac
@ -59,6 +59,8 @@ if [ $help = yes ]; then
--pi for pi platform, configure/make/package.
--x86-64 alias for --x86-x64.
--jobs Set the configure and make jobs.
--console The path for https://github.com/ossrs/srs-console
END
exit 0
fi
@ -151,6 +153,18 @@ ok_msg "start install srs"
ret=$?; if [[ 0 -ne ${ret} ]]; then failed_msg "install srs failed"; exit $ret; fi
ok_msg "install srs success"
# Copy srs-console
HTTP_HOME="${package_dir}/${INSTALL}/objs/nginx/html/"
(
cp $work_dir/research/api-server/static-dir/index.html ${HTTP_HOME} &&
cp $work_dir/research/api-server/static-dir/favicon.ico ${HTTP_HOME} &&
cp $work_dir/research/api-server/static-dir/crossdomain.xml ${HTTP_HOME} &&
cp -R $work_dir/research/players ${HTTP_HOME} &&
cp -R $work_dir/research/console ${HTTP_HOME}
) >> $log 2>&1
ret=$?; if [[ 0 -ne ${ret} ]]; then failed_msg "copy utilities failed"; exit $ret; fi
ok_msg "copy utilities success"
# copy extra files to package.
ok_msg "start copy extra files to package"
(

View file

@ -3796,7 +3796,7 @@ srs_error_t SrsConfig::check_normal_config()
&& e != "acodec" && e != "abitrate" && e != "asample_rate" && e != "achannels"
&& e != "aparams" && e != "output" && e != "perfile"
&& e != "iformat" && e != "oformat") {
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal vhost.transcode.engine.%s of %s", m.c_str(), vhost->arg0().c_str());
return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal vhost.transcode.engine.%s of %s", e.c_str(), vhost->arg0().c_str());
}
}
}
@ -4056,6 +4056,18 @@ bool SrsConfig::get_asprocess()
return SRS_CONF_PERFER_FALSE(conf->arg0());
}
bool SrsConfig::whether_query_latest_version()
{
static bool DEFAULT = true;
SrsConfDirective* conf = root->get("query_latest_version");
if (!conf) {
return DEFAULT;
}
return SRS_CONF_PERFER_TRUE(conf->arg0());
}
bool SrsConfig::empty_ip_ok()
{
static bool DEFAULT = true;

View file

@ -468,6 +468,8 @@ public:
virtual std::string get_work_dir();
// Whether use asprocess mode.
virtual bool get_asprocess();
// Whether query the latest available version of SRS.
virtual bool whether_query_latest_version();
// Whether empty client IP is ok.
virtual bool empty_ip_ok();
// Get the start wait in ms for gracefully quit.

View file

@ -75,7 +75,7 @@ SrsBufferCache::~SrsBufferCache()
srs_freep(req);
}
srs_error_t SrsBufferCache::update(SrsSource* s, SrsRequest* r)
srs_error_t SrsBufferCache::update_auth(SrsSource* s, SrsRequest* r)
{
srs_freep(req);
req = r->copy();
@ -523,7 +523,7 @@ SrsLiveStream::~SrsLiveStream()
srs_freep(req);
}
srs_error_t SrsLiveStream::update(SrsSource* s, SrsRequest* r)
srs_error_t SrsLiveStream::update_auth(SrsSource* s, SrsRequest* r)
{
source = s;
@ -938,9 +938,10 @@ srs_error_t SrsHttpStreamServer::http_mount(SrsSource* s, SrsRequest* r)
}
srs_trace("http: mount flv stream for sid=%s, mount=%s", sid.c_str(), mount.c_str());
} else {
// The entry exists, we reuse it and update the request of stream and cache.
entry = sflvs[sid];
entry->stream->update(s, r);
entry->cache->update(s, r);
entry->stream->update_auth(s, r);
entry->cache->update_auth(s, r);
}
if (entry->stream) {

View file

@ -46,7 +46,7 @@ private:
public:
SrsBufferCache(SrsSource* s, SrsRequest* r);
virtual ~SrsBufferCache();
virtual srs_error_t update(SrsSource* s, SrsRequest* r);
virtual srs_error_t update_auth(SrsSource* s, SrsRequest* r);
public:
virtual srs_error_t start();
virtual srs_error_t dump_cache(SrsConsumer* consumer, SrsRtmpJitterAlgorithm jitter);
@ -188,7 +188,7 @@ private:
public:
SrsLiveStream(SrsSource* s, SrsRequest* r, SrsBufferCache* c);
virtual ~SrsLiveStream();
virtual srs_error_t update(SrsSource* s, SrsRequest* r);
virtual srs_error_t update_auth(SrsSource* s, SrsRequest* r);
public:
virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
private:

View file

@ -484,7 +484,7 @@ void SrsIngester::show_ingest_log_message()
}
// random choose one ingester to report.
int index = rand() % (int)ingesters.size();
int index = srs_random() % (int)ingesters.size();
SrsIngesterFFMPEG* ingester = ingesters.at(index);
// reportable

View file

@ -0,0 +1,187 @@
/*
The MIT License (MIT)
Copyright (c) 2013-2015 SRS(ossrs)
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.
*/
#include <srs_app_latest_version.hpp>
#include <srs_core_autofree.hpp>
#include <srs_kernel_error.hpp>
#include <srs_kernel_log.hpp>
#include <srs_kernel_utility.hpp>
#include <srs_protocol_json.hpp>
#include <srs_protocol_utility.hpp>
#include <srs_app_config.hpp>
#include <srs_app_http_conn.hpp>
#include <srs_app_http_client.hpp>
#include <srs_app_utility.hpp>
#include <srs_app_uuid.hpp>
#include <unistd.h>
#include <sstream>
using namespace std;
// Whether we are in docker, defined in main module.
extern bool _srs_in_docker;
void srs_build_features(stringstream& ss)
{
ss << "&os=linux";
ss << "&docker=" << _srs_in_docker
<< "&packager=" << SRS_AUTO_PACKAGER;
}
SrsLatestVersion::SrsLatestVersion()
{
trd_ = new SrsSTCoroutine("signal", this);
}
SrsLatestVersion::~SrsLatestVersion()
{
srs_freep(trd_);
}
srs_error_t SrsLatestVersion::start()
{
if (!_srs_config->whether_query_latest_version()) {
return srs_success;
}
if (true) {
uuid_t uuid;
uuid_generate_time(uuid);
char buf[32];
for (int i = 0; i < 16; i++) {
snprintf(buf + i * 2, sizeof(buf), "%02x", uuid[i]);
}
server_id_ = string(buf, sizeof(buf));
}
return trd_->start();
}
srs_error_t SrsLatestVersion::cycle()
{
srs_error_t err = srs_success;
srs_utime_t first_random_wait = 0;
srs_random_generate((char*)&first_random_wait, 8);
first_random_wait = srs_utime_t(uint64_t((first_random_wait + srs_update_system_time() + getpid())) % (5 * 60)) * SRS_UTIME_SECONDS; // in s.
// Only report after 5+ minutes.
first_random_wait += 5 * 60 * SRS_UTIME_SECONDS;
srs_trace("Startup query id=%s, eip=%s, wait=%ds", server_id_.c_str(), srs_get_public_internet_address().c_str(), srsu2msi(first_random_wait)/1000);
srs_usleep(first_random_wait);
while (true) {
srs_utime_t starttime = srs_update_system_time();
if ((err = query_latest_version()) != srs_success) {
srs_warn("query err %s", srs_error_desc(err).c_str());
srs_freep(err); // Ignore any error.
}
srs_trace("Finish query id=%s, eip=%s, match=%s, stable=%s, cost=%dms", server_id_.c_str(), srs_get_public_internet_address().c_str(), match_version_.c_str(), stable_version_.c_str(), srsu2msi(srs_update_system_time() - starttime));
srs_usleep(3600 * SRS_UTIME_SECONDS); // Every an hour.
}
return err;
}
srs_error_t SrsLatestVersion::query_latest_version()
{
srs_error_t err = srs_success;
// Generate uri and parse to object.
stringstream ss;
ss << "http://api.ossrs.net/service/v1/releases?"
<< "version=v" << VERSION_MAJOR << "." << VERSION_MINOR << "." << VERSION_REVISION
<< "&id=" << server_id_ << "&role=srs"
<< "&eip=" << srs_get_public_internet_address()
<< "&ts=" << srs_get_system_time()
<< "&alive=" << srsu2ms(srs_get_system_time() - srs_get_system_startup_time()) / 1000;
srs_build_features(ss);
string url = ss.str();
SrsHttpUri uri;
if ((err = uri.initialize(url)) != srs_success) {
return srs_error_wrap(err, "http: post failed. url=%s", url.c_str());
}
// Start HTTP request and read response.
SrsHttpClient http;
if ((err = http.initialize(uri.get_host(), uri.get_port())) != srs_success) {
return err;
}
// Path with query.
string path = uri.get_path();
path += "?";
path += uri.get_query();
ISrsHttpMessage* msg = NULL;
if ((err = http.get(path, "", &msg)) != srs_success) {
return err;
}
SrsAutoFree(ISrsHttpMessage, msg);
string res;
int code = msg->status_code();
if ((err = msg->body_read_all(res)) != srs_success) {
return err;
}
// Check the response code and content.
if (code != SRS_CONSTS_HTTP_OK) {
return srs_error_new(ERROR_HTTP_STATUS_INVALID, "invalid response status=%d", code);
}
if (res.empty()) {
return srs_error_new(ERROR_HTTP_DATA_INVALID, "invalid empty response");
}
// Response in json object.
SrsJsonAny* jres = SrsJsonAny::loads((char*)res.c_str());
if (!jres || !jres->is_object()) {
return srs_error_new(ERROR_HTTP_DATA_INVALID, "invalid response %s", res.c_str());
}
SrsAutoFree(SrsJsonAny, jres);
SrsJsonObject* obj = jres->to_object();
SrsJsonAny* prop = NULL;
// Parse fields of response.
if ((prop = obj->ensure_property_string("match_version")) == NULL) {
return srs_error_new(ERROR_RESPONSE_CODE, "no match_version");
}
match_version_ = prop->to_str();
if ((prop = obj->ensure_property_string("stable_version")) == NULL) {
return srs_error_new(ERROR_RESPONSE_CODE, "no stable_version");
}
stable_version_ = prop->to_str();
return err;
}

View file

@ -0,0 +1,58 @@
/*
The MIT License (MIT)
Copyright (c) 2013-2015 SRS(ossrs)
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.
*/
#ifndef SRS_APP_LATEST_VERSION_HPP
#define SRS_APP_LATEST_VERSION_HPP
/*
#include <srs_app_latest_version.hpp>
*/
#include <srs_core.hpp>
#include <srs_app_thread.hpp>
#include <string>
class SrsLatestVersion : public ISrsCoroutineHandler
{
private:
SrsCoroutine* trd_;
std::string server_id_;
private:
std::string match_version_;
std::string stable_version_;
public:
SrsLatestVersion();
virtual ~SrsLatestVersion();
public:
virtual srs_error_t start();
// interface ISrsEndlessThreadHandler.
public:
virtual srs_error_t cycle();
private:
srs_error_t query_latest_version();
};
#endif

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