Initial import: sources, staging packages, build scripts and CI workflow
Build and Publish Debian Packages / Build Packages (push) Failing after 18s
Build and Publish Debian Packages / Publish to eendoe repository (push) Skipped

This commit is contained in:
bart
2026-10-20 21:20:13 +02:00
commit 9cc903b2a4
269 changed files with 73441 additions and 0 deletions
@@ -0,0 +1,769 @@
.PHONY: srs_mp4_parser srs_hls_ingester
GCC = gcc
CXX = g++
AR = ar
LINK = g++
CXXFLAGS = -std=c++11 -Wall -g -O0 -fsanitize=address -fno-omit-frame-pointer
.PHONY: default srs srs_ingest_hls
default:
#####################################################################################
# The module CORE.
#####################################################################################
# INCS for CORE, headers of module and its depends to compile
CORE_MODULE_INCS = -I./src/core
CORE_INCS = -I./src/core
CORE_LIBS_INCS = -I./objs
# DEPS for CORE, the depends of make schema
CORE_DEPS = ./src/core/srs_core.hpp ./src/core/srs_core_version.hpp ./src/core/srs_core_version5.hpp ./src/core/srs_core_autofree.hpp ./src/core/srs_core_performance.hpp ./src/core/srs_core_time.hpp ./src/core/srs_core_platform.hpp
# OBJ for CORE, each object file
./objs/src/core/srs_core.o: $(CORE_DEPS) ./src/core/srs_core.cpp
$(CXX) -c $(CXXFLAGS) \
$(CORE_INCS)\
$(CORE_LIBS_INCS)\
-o ./objs/src/core/srs_core.o \
./src/core/srs_core.cpp
./objs/src/core/srs_core_version.o: $(CORE_DEPS) ./src/core/srs_core_version.cpp
$(CXX) -c $(CXXFLAGS) \
$(CORE_INCS)\
$(CORE_LIBS_INCS)\
-o ./objs/src/core/srs_core_version.o \
./src/core/srs_core_version.cpp
./objs/src/core/srs_core_version5.o: $(CORE_DEPS) ./src/core/srs_core_version5.cpp
$(CXX) -c $(CXXFLAGS) \
$(CORE_INCS)\
$(CORE_LIBS_INCS)\
-o ./objs/src/core/srs_core_version5.o \
./src/core/srs_core_version5.cpp
./objs/src/core/srs_core_autofree.o: $(CORE_DEPS) ./src/core/srs_core_autofree.cpp
$(CXX) -c $(CXXFLAGS) \
$(CORE_INCS)\
$(CORE_LIBS_INCS)\
-o ./objs/src/core/srs_core_autofree.o \
./src/core/srs_core_autofree.cpp
./objs/src/core/srs_core_performance.o: $(CORE_DEPS) ./src/core/srs_core_performance.cpp
$(CXX) -c $(CXXFLAGS) \
$(CORE_INCS)\
$(CORE_LIBS_INCS)\
-o ./objs/src/core/srs_core_performance.o \
./src/core/srs_core_performance.cpp
./objs/src/core/srs_core_time.o: $(CORE_DEPS) ./src/core/srs_core_time.cpp
$(CXX) -c $(CXXFLAGS) \
$(CORE_INCS)\
$(CORE_LIBS_INCS)\
-o ./objs/src/core/srs_core_time.o \
./src/core/srs_core_time.cpp
./objs/src/core/srs_core_platform.o: $(CORE_DEPS) ./src/core/srs_core_platform.cpp
$(CXX) -c $(CXXFLAGS) \
$(CORE_INCS)\
$(CORE_LIBS_INCS)\
-o ./objs/src/core/srs_core_platform.o \
./src/core/srs_core_platform.cpp
#####################################################################################
# The module KERNEL.
#####################################################################################
# INCS for KERNEL, headers of module and its depends to compile
KERNEL_MODULE_INCS = -I./src/kernel
KERNEL_INCS = -I./src/kernel $(CORE_MODULE_INCS)
KERNEL_LIBS_INCS = -I./objs -I./objs/openssl/include
# DEPS for KERNEL, the depends of make schema
KERNEL_DEPS = ./src/kernel/srs_kernel_error.hpp ./src/kernel/srs_kernel_log.hpp ./src/kernel/srs_kernel_buffer.hpp ./src/kernel/srs_kernel_utility.hpp ./src/kernel/srs_kernel_flv.hpp ./src/kernel/srs_kernel_codec.hpp ./src/kernel/srs_kernel_io.hpp ./src/kernel/srs_kernel_consts.hpp ./src/kernel/srs_kernel_aac.hpp ./src/kernel/srs_kernel_mp3.hpp ./src/kernel/srs_kernel_ts.hpp ./src/kernel/srs_kernel_ps.hpp ./src/kernel/srs_kernel_stream.hpp ./src/kernel/srs_kernel_balance.hpp ./src/kernel/srs_kernel_mp4.hpp ./src/kernel/srs_kernel_file.hpp ./src/kernel/srs_kernel_kbps.hpp ./src/kernel/srs_kernel_rtc_rtp.hpp ./src/kernel/srs_kernel_rtc_rtcp.hpp $(CORE_DEPS)
# OBJ for KERNEL, each object file
./objs/src/kernel/srs_kernel_error.o: $(KERNEL_DEPS) ./src/kernel/srs_kernel_error.cpp
$(CXX) -c $(CXXFLAGS) \
$(KERNEL_INCS)\
$(KERNEL_LIBS_INCS)\
-o ./objs/src/kernel/srs_kernel_error.o \
./src/kernel/srs_kernel_error.cpp
./objs/src/kernel/srs_kernel_log.o: $(KERNEL_DEPS) ./src/kernel/srs_kernel_log.cpp
$(CXX) -c $(CXXFLAGS) \
$(KERNEL_INCS)\
$(KERNEL_LIBS_INCS)\
-o ./objs/src/kernel/srs_kernel_log.o \
./src/kernel/srs_kernel_log.cpp
./objs/src/kernel/srs_kernel_buffer.o: $(KERNEL_DEPS) ./src/kernel/srs_kernel_buffer.cpp
$(CXX) -c $(CXXFLAGS) \
$(KERNEL_INCS)\
$(KERNEL_LIBS_INCS)\
-o ./objs/src/kernel/srs_kernel_buffer.o \
./src/kernel/srs_kernel_buffer.cpp
./objs/src/kernel/srs_kernel_utility.o: $(KERNEL_DEPS) ./src/kernel/srs_kernel_utility.cpp
$(CXX) -c $(CXXFLAGS) \
$(KERNEL_INCS)\
$(KERNEL_LIBS_INCS)\
-o ./objs/src/kernel/srs_kernel_utility.o \
./src/kernel/srs_kernel_utility.cpp
./objs/src/kernel/srs_kernel_flv.o: $(KERNEL_DEPS) ./src/kernel/srs_kernel_flv.cpp
$(CXX) -c $(CXXFLAGS) \
$(KERNEL_INCS)\
$(KERNEL_LIBS_INCS)\
-o ./objs/src/kernel/srs_kernel_flv.o \
./src/kernel/srs_kernel_flv.cpp
./objs/src/kernel/srs_kernel_codec.o: $(KERNEL_DEPS) ./src/kernel/srs_kernel_codec.cpp
$(CXX) -c $(CXXFLAGS) \
$(KERNEL_INCS)\
$(KERNEL_LIBS_INCS)\
-o ./objs/src/kernel/srs_kernel_codec.o \
./src/kernel/srs_kernel_codec.cpp
./objs/src/kernel/srs_kernel_io.o: $(KERNEL_DEPS) ./src/kernel/srs_kernel_io.cpp
$(CXX) -c $(CXXFLAGS) \
$(KERNEL_INCS)\
$(KERNEL_LIBS_INCS)\
-o ./objs/src/kernel/srs_kernel_io.o \
./src/kernel/srs_kernel_io.cpp
./objs/src/kernel/srs_kernel_consts.o: $(KERNEL_DEPS) ./src/kernel/srs_kernel_consts.cpp
$(CXX) -c $(CXXFLAGS) \
$(KERNEL_INCS)\
$(KERNEL_LIBS_INCS)\
-o ./objs/src/kernel/srs_kernel_consts.o \
./src/kernel/srs_kernel_consts.cpp
./objs/src/kernel/srs_kernel_aac.o: $(KERNEL_DEPS) ./src/kernel/srs_kernel_aac.cpp
$(CXX) -c $(CXXFLAGS) \
$(KERNEL_INCS)\
$(KERNEL_LIBS_INCS)\
-o ./objs/src/kernel/srs_kernel_aac.o \
./src/kernel/srs_kernel_aac.cpp
./objs/src/kernel/srs_kernel_mp3.o: $(KERNEL_DEPS) ./src/kernel/srs_kernel_mp3.cpp
$(CXX) -c $(CXXFLAGS) \
$(KERNEL_INCS)\
$(KERNEL_LIBS_INCS)\
-o ./objs/src/kernel/srs_kernel_mp3.o \
./src/kernel/srs_kernel_mp3.cpp
./objs/src/kernel/srs_kernel_ts.o: $(KERNEL_DEPS) ./src/kernel/srs_kernel_ts.cpp
$(CXX) -c $(CXXFLAGS) \
$(KERNEL_INCS)\
$(KERNEL_LIBS_INCS)\
-o ./objs/src/kernel/srs_kernel_ts.o \
./src/kernel/srs_kernel_ts.cpp
./objs/src/kernel/srs_kernel_ps.o: $(KERNEL_DEPS) ./src/kernel/srs_kernel_ps.cpp
$(CXX) -c $(CXXFLAGS) \
$(KERNEL_INCS)\
$(KERNEL_LIBS_INCS)\
-o ./objs/src/kernel/srs_kernel_ps.o \
./src/kernel/srs_kernel_ps.cpp
./objs/src/kernel/srs_kernel_stream.o: $(KERNEL_DEPS) ./src/kernel/srs_kernel_stream.cpp
$(CXX) -c $(CXXFLAGS) \
$(KERNEL_INCS)\
$(KERNEL_LIBS_INCS)\
-o ./objs/src/kernel/srs_kernel_stream.o \
./src/kernel/srs_kernel_stream.cpp
./objs/src/kernel/srs_kernel_balance.o: $(KERNEL_DEPS) ./src/kernel/srs_kernel_balance.cpp
$(CXX) -c $(CXXFLAGS) \
$(KERNEL_INCS)\
$(KERNEL_LIBS_INCS)\
-o ./objs/src/kernel/srs_kernel_balance.o \
./src/kernel/srs_kernel_balance.cpp
./objs/src/kernel/srs_kernel_mp4.o: $(KERNEL_DEPS) ./src/kernel/srs_kernel_mp4.cpp
$(CXX) -c $(CXXFLAGS) \
$(KERNEL_INCS)\
$(KERNEL_LIBS_INCS)\
-o ./objs/src/kernel/srs_kernel_mp4.o \
./src/kernel/srs_kernel_mp4.cpp
./objs/src/kernel/srs_kernel_file.o: $(KERNEL_DEPS) ./src/kernel/srs_kernel_file.cpp
$(CXX) -c $(CXXFLAGS) \
$(KERNEL_INCS)\
$(KERNEL_LIBS_INCS)\
-o ./objs/src/kernel/srs_kernel_file.o \
./src/kernel/srs_kernel_file.cpp
./objs/src/kernel/srs_kernel_kbps.o: $(KERNEL_DEPS) ./src/kernel/srs_kernel_kbps.cpp
$(CXX) -c $(CXXFLAGS) \
$(KERNEL_INCS)\
$(KERNEL_LIBS_INCS)\
-o ./objs/src/kernel/srs_kernel_kbps.o \
./src/kernel/srs_kernel_kbps.cpp
./objs/src/kernel/srs_kernel_rtc_rtp.o: $(KERNEL_DEPS) ./src/kernel/srs_kernel_rtc_rtp.cpp
$(CXX) -c $(CXXFLAGS) \
$(KERNEL_INCS)\
$(KERNEL_LIBS_INCS)\
-o ./objs/src/kernel/srs_kernel_rtc_rtp.o \
./src/kernel/srs_kernel_rtc_rtp.cpp
./objs/src/kernel/srs_kernel_rtc_rtcp.o: $(KERNEL_DEPS) ./src/kernel/srs_kernel_rtc_rtcp.cpp
$(CXX) -c $(CXXFLAGS) \
$(KERNEL_INCS)\
$(KERNEL_LIBS_INCS)\
-o ./objs/src/kernel/srs_kernel_rtc_rtcp.o \
./src/kernel/srs_kernel_rtc_rtcp.cpp
#####################################################################################
# The module PROTOCOL.
#####################################################################################
# INCS for PROTOCOL, headers of module and its depends to compile
PROTOCOL_MODULE_INCS = -I./src/protocol
PROTOCOL_INCS = -I./src/protocol $(CORE_MODULE_INCS)$(KERNEL_MODULE_INCS)
PROTOCOL_LIBS_INCS = -I./objs -I./objs/st -I./objs/openssl/include -I./objs/srt/include
# DEPS for PROTOCOL, the depends of make schema
PROTOCOL_DEPS = ./src/protocol/srs_protocol_amf0.hpp ./src/protocol/srs_protocol_io.hpp ./src/protocol/srs_protocol_conn.hpp ./src/protocol/srs_protocol_rtmp_handshake.hpp ./src/protocol/srs_protocol_rtmp_stack.hpp ./src/protocol/srs_protocol_utility.hpp ./src/protocol/srs_protocol_rtmp_msg_array.hpp ./src/protocol/srs_protocol_stream.hpp ./src/protocol/srs_protocol_raw_avc.hpp ./src/protocol/srs_protocol_http_stack.hpp ./src/protocol/srs_protocol_kbps.hpp ./src/protocol/srs_protocol_json.hpp ./src/protocol/srs_protocol_format.hpp ./src/protocol/srs_protocol_log.hpp ./src/protocol/srs_protocol_st.hpp ./src/protocol/srs_protocol_http_client.hpp ./src/protocol/srs_protocol_http_conn.hpp ./src/protocol/srs_protocol_rtmp_conn.hpp ./src/protocol/srs_protocol_protobuf.hpp ./src/protocol/srs_protocol_srt.hpp ./src/protocol/srs_protocol_rtc_stun.hpp $(CORE_DEPS) $(KERNEL_DEPS)
# OBJ for PROTOCOL, each object file
./objs/src/protocol/srs_protocol_amf0.o: $(PROTOCOL_DEPS) ./src/protocol/srs_protocol_amf0.cpp
$(CXX) -c $(CXXFLAGS) \
$(PROTOCOL_INCS)\
$(PROTOCOL_LIBS_INCS)\
-o ./objs/src/protocol/srs_protocol_amf0.o \
./src/protocol/srs_protocol_amf0.cpp
./objs/src/protocol/srs_protocol_io.o: $(PROTOCOL_DEPS) ./src/protocol/srs_protocol_io.cpp
$(CXX) -c $(CXXFLAGS) \
$(PROTOCOL_INCS)\
$(PROTOCOL_LIBS_INCS)\
-o ./objs/src/protocol/srs_protocol_io.o \
./src/protocol/srs_protocol_io.cpp
./objs/src/protocol/srs_protocol_conn.o: $(PROTOCOL_DEPS) ./src/protocol/srs_protocol_conn.cpp
$(CXX) -c $(CXXFLAGS) \
$(PROTOCOL_INCS)\
$(PROTOCOL_LIBS_INCS)\
-o ./objs/src/protocol/srs_protocol_conn.o \
./src/protocol/srs_protocol_conn.cpp
./objs/src/protocol/srs_protocol_rtmp_handshake.o: $(PROTOCOL_DEPS) ./src/protocol/srs_protocol_rtmp_handshake.cpp
$(CXX) -c $(CXXFLAGS) \
$(PROTOCOL_INCS)\
$(PROTOCOL_LIBS_INCS)\
-o ./objs/src/protocol/srs_protocol_rtmp_handshake.o \
./src/protocol/srs_protocol_rtmp_handshake.cpp
./objs/src/protocol/srs_protocol_rtmp_stack.o: $(PROTOCOL_DEPS) ./src/protocol/srs_protocol_rtmp_stack.cpp
$(CXX) -c $(CXXFLAGS) \
$(PROTOCOL_INCS)\
$(PROTOCOL_LIBS_INCS)\
-o ./objs/src/protocol/srs_protocol_rtmp_stack.o \
./src/protocol/srs_protocol_rtmp_stack.cpp
./objs/src/protocol/srs_protocol_utility.o: $(PROTOCOL_DEPS) ./src/protocol/srs_protocol_utility.cpp
$(CXX) -c $(CXXFLAGS) \
$(PROTOCOL_INCS)\
$(PROTOCOL_LIBS_INCS)\
-o ./objs/src/protocol/srs_protocol_utility.o \
./src/protocol/srs_protocol_utility.cpp
./objs/src/protocol/srs_protocol_rtmp_msg_array.o: $(PROTOCOL_DEPS) ./src/protocol/srs_protocol_rtmp_msg_array.cpp
$(CXX) -c $(CXXFLAGS) \
$(PROTOCOL_INCS)\
$(PROTOCOL_LIBS_INCS)\
-o ./objs/src/protocol/srs_protocol_rtmp_msg_array.o \
./src/protocol/srs_protocol_rtmp_msg_array.cpp
./objs/src/protocol/srs_protocol_stream.o: $(PROTOCOL_DEPS) ./src/protocol/srs_protocol_stream.cpp
$(CXX) -c $(CXXFLAGS) \
$(PROTOCOL_INCS)\
$(PROTOCOL_LIBS_INCS)\
-o ./objs/src/protocol/srs_protocol_stream.o \
./src/protocol/srs_protocol_stream.cpp
./objs/src/protocol/srs_protocol_raw_avc.o: $(PROTOCOL_DEPS) ./src/protocol/srs_protocol_raw_avc.cpp
$(CXX) -c $(CXXFLAGS) \
$(PROTOCOL_INCS)\
$(PROTOCOL_LIBS_INCS)\
-o ./objs/src/protocol/srs_protocol_raw_avc.o \
./src/protocol/srs_protocol_raw_avc.cpp
./objs/src/protocol/srs_protocol_http_stack.o: $(PROTOCOL_DEPS) ./src/protocol/srs_protocol_http_stack.cpp
$(CXX) -c $(CXXFLAGS) \
$(PROTOCOL_INCS)\
$(PROTOCOL_LIBS_INCS)\
-o ./objs/src/protocol/srs_protocol_http_stack.o \
./src/protocol/srs_protocol_http_stack.cpp
./objs/src/protocol/srs_protocol_kbps.o: $(PROTOCOL_DEPS) ./src/protocol/srs_protocol_kbps.cpp
$(CXX) -c $(CXXFLAGS) \
$(PROTOCOL_INCS)\
$(PROTOCOL_LIBS_INCS)\
-o ./objs/src/protocol/srs_protocol_kbps.o \
./src/protocol/srs_protocol_kbps.cpp
./objs/src/protocol/srs_protocol_json.o: $(PROTOCOL_DEPS) ./src/protocol/srs_protocol_json.cpp
$(CXX) -c $(CXXFLAGS) \
$(PROTOCOL_INCS)\
$(PROTOCOL_LIBS_INCS)\
-o ./objs/src/protocol/srs_protocol_json.o \
./src/protocol/srs_protocol_json.cpp
./objs/src/protocol/srs_protocol_format.o: $(PROTOCOL_DEPS) ./src/protocol/srs_protocol_format.cpp
$(CXX) -c $(CXXFLAGS) \
$(PROTOCOL_INCS)\
$(PROTOCOL_LIBS_INCS)\
-o ./objs/src/protocol/srs_protocol_format.o \
./src/protocol/srs_protocol_format.cpp
./objs/src/protocol/srs_protocol_log.o: $(PROTOCOL_DEPS) ./src/protocol/srs_protocol_log.cpp
$(CXX) -c $(CXXFLAGS) \
$(PROTOCOL_INCS)\
$(PROTOCOL_LIBS_INCS)\
-o ./objs/src/protocol/srs_protocol_log.o \
./src/protocol/srs_protocol_log.cpp
./objs/src/protocol/srs_protocol_st.o: $(PROTOCOL_DEPS) ./src/protocol/srs_protocol_st.cpp
$(CXX) -c $(CXXFLAGS) \
$(PROTOCOL_INCS)\
$(PROTOCOL_LIBS_INCS)\
-o ./objs/src/protocol/srs_protocol_st.o \
./src/protocol/srs_protocol_st.cpp
./objs/src/protocol/srs_protocol_http_client.o: $(PROTOCOL_DEPS) ./src/protocol/srs_protocol_http_client.cpp
$(CXX) -c $(CXXFLAGS) \
$(PROTOCOL_INCS)\
$(PROTOCOL_LIBS_INCS)\
-o ./objs/src/protocol/srs_protocol_http_client.o \
./src/protocol/srs_protocol_http_client.cpp
./objs/src/protocol/srs_protocol_http_conn.o: $(PROTOCOL_DEPS) ./src/protocol/srs_protocol_http_conn.cpp
$(CXX) -c $(CXXFLAGS) \
$(PROTOCOL_INCS)\
$(PROTOCOL_LIBS_INCS)\
-o ./objs/src/protocol/srs_protocol_http_conn.o \
./src/protocol/srs_protocol_http_conn.cpp
./objs/src/protocol/srs_protocol_rtmp_conn.o: $(PROTOCOL_DEPS) ./src/protocol/srs_protocol_rtmp_conn.cpp
$(CXX) -c $(CXXFLAGS) \
$(PROTOCOL_INCS)\
$(PROTOCOL_LIBS_INCS)\
-o ./objs/src/protocol/srs_protocol_rtmp_conn.o \
./src/protocol/srs_protocol_rtmp_conn.cpp
./objs/src/protocol/srs_protocol_protobuf.o: $(PROTOCOL_DEPS) ./src/protocol/srs_protocol_protobuf.cpp
$(CXX) -c $(CXXFLAGS) \
$(PROTOCOL_INCS)\
$(PROTOCOL_LIBS_INCS)\
-o ./objs/src/protocol/srs_protocol_protobuf.o \
./src/protocol/srs_protocol_protobuf.cpp
./objs/src/protocol/srs_protocol_srt.o: $(PROTOCOL_DEPS) ./src/protocol/srs_protocol_srt.cpp
$(CXX) -c $(CXXFLAGS) \
$(PROTOCOL_INCS)\
$(PROTOCOL_LIBS_INCS)\
-o ./objs/src/protocol/srs_protocol_srt.o \
./src/protocol/srs_protocol_srt.cpp
./objs/src/protocol/srs_protocol_rtc_stun.o: $(PROTOCOL_DEPS) ./src/protocol/srs_protocol_rtc_stun.cpp
$(CXX) -c $(CXXFLAGS) \
$(PROTOCOL_INCS)\
$(PROTOCOL_LIBS_INCS)\
-o ./objs/src/protocol/srs_protocol_rtc_stun.o \
./src/protocol/srs_protocol_rtc_stun.cpp
#####################################################################################
# The module APP.
#####################################################################################
# INCS for APP, headers of module and its depends to compile
APP_MODULE_INCS = -I./src/app
APP_INCS = -I./src/app $(CORE_MODULE_INCS)$(KERNEL_MODULE_INCS)$(PROTOCOL_MODULE_INCS)
APP_LIBS_INCS = -I./objs -I./objs/openssl/include -I./objs/srtp2/include -I./objs/ffmpeg/include
# DEPS for APP, the depends of make schema
APP_DEPS = ./src/app/srs_app_server.hpp ./src/app/srs_app_conn.hpp ./src/app/srs_app_rtmp_conn.hpp ./src/app/srs_app_source.hpp ./src/app/srs_app_refer.hpp ./src/app/srs_app_hls.hpp ./src/app/srs_app_forward.hpp ./src/app/srs_app_encoder.hpp ./src/app/srs_app_http_stream.hpp ./src/app/srs_app_st.hpp ./src/app/srs_app_log.hpp ./src/app/srs_app_config.hpp ./src/app/srs_app_stream_bridge.hpp ./src/app/srs_app_pithy_print.hpp ./src/app/srs_app_reload.hpp ./src/app/srs_app_http_api.hpp ./src/app/srs_app_http_conn.hpp ./src/app/srs_app_http_hooks.hpp ./src/app/srs_app_ingest.hpp ./src/app/srs_app_ffmpeg.hpp ./src/app/srs_app_utility.hpp ./src/app/srs_app_edge.hpp ./src/app/srs_app_heartbeat.hpp ./src/app/srs_app_empty.hpp ./src/app/srs_app_http_client.hpp ./src/app/srs_app_http_static.hpp ./src/app/srs_app_recv_thread.hpp ./src/app/srs_app_security.hpp ./src/app/srs_app_statistic.hpp ./src/app/srs_app_hds.hpp ./src/app/srs_app_mpegts_udp.hpp ./src/app/srs_app_listener.hpp ./src/app/srs_app_async_call.hpp ./src/app/srs_app_caster_flv.hpp ./src/app/srs_app_latest_version.hpp ./src/app/srs_app_uuid.hpp ./src/app/srs_app_process.hpp ./src/app/srs_app_ng_exec.hpp ./src/app/srs_app_hourglass.hpp ./src/app/srs_app_dash.hpp ./src/app/srs_app_fragment.hpp ./src/app/srs_app_dvr.hpp ./src/app/srs_app_coworkers.hpp ./src/app/srs_app_hybrid.hpp ./src/app/srs_app_threads.hpp ./src/app/srs_app_srt_server.hpp ./src/app/srs_app_srt_listener.hpp ./src/app/srs_app_srt_conn.hpp ./src/app/srs_app_srt_utility.hpp ./src/app/srs_app_srt_source.hpp ./src/app/srs_app_rtc_conn.hpp ./src/app/srs_app_rtc_dtls.hpp ./src/app/srs_app_rtc_sdp.hpp ./src/app/srs_app_rtc_network.hpp ./src/app/srs_app_rtc_queue.hpp ./src/app/srs_app_rtc_server.hpp ./src/app/srs_app_rtc_source.hpp ./src/app/srs_app_rtc_api.hpp ./src/app/srs_app_rtc_codec.hpp $(CORE_DEPS) $(KERNEL_DEPS) $(PROTOCOL_DEPS)
# OBJ for APP, each object file
./objs/src/app/srs_app_server.o: $(APP_DEPS) ./src/app/srs_app_server.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_server.o \
./src/app/srs_app_server.cpp
./objs/src/app/srs_app_conn.o: $(APP_DEPS) ./src/app/srs_app_conn.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_conn.o \
./src/app/srs_app_conn.cpp
./objs/src/app/srs_app_rtmp_conn.o: $(APP_DEPS) ./src/app/srs_app_rtmp_conn.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_rtmp_conn.o \
./src/app/srs_app_rtmp_conn.cpp
./objs/src/app/srs_app_source.o: $(APP_DEPS) ./src/app/srs_app_source.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_source.o \
./src/app/srs_app_source.cpp
./objs/src/app/srs_app_refer.o: $(APP_DEPS) ./src/app/srs_app_refer.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_refer.o \
./src/app/srs_app_refer.cpp
./objs/src/app/srs_app_hls.o: $(APP_DEPS) ./src/app/srs_app_hls.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_hls.o \
./src/app/srs_app_hls.cpp
./objs/src/app/srs_app_forward.o: $(APP_DEPS) ./src/app/srs_app_forward.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_forward.o \
./src/app/srs_app_forward.cpp
./objs/src/app/srs_app_encoder.o: $(APP_DEPS) ./src/app/srs_app_encoder.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_encoder.o \
./src/app/srs_app_encoder.cpp
./objs/src/app/srs_app_http_stream.o: $(APP_DEPS) ./src/app/srs_app_http_stream.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_http_stream.o \
./src/app/srs_app_http_stream.cpp
./objs/src/app/srs_app_st.o: $(APP_DEPS) ./src/app/srs_app_st.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_st.o \
./src/app/srs_app_st.cpp
./objs/src/app/srs_app_log.o: $(APP_DEPS) ./src/app/srs_app_log.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_log.o \
./src/app/srs_app_log.cpp
./objs/src/app/srs_app_config.o: $(APP_DEPS) ./src/app/srs_app_config.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_config.o \
./src/app/srs_app_config.cpp
./objs/src/app/srs_app_stream_bridge.o: $(APP_DEPS) ./src/app/srs_app_stream_bridge.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_stream_bridge.o \
./src/app/srs_app_stream_bridge.cpp
./objs/src/app/srs_app_pithy_print.o: $(APP_DEPS) ./src/app/srs_app_pithy_print.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_pithy_print.o \
./src/app/srs_app_pithy_print.cpp
./objs/src/app/srs_app_reload.o: $(APP_DEPS) ./src/app/srs_app_reload.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_reload.o \
./src/app/srs_app_reload.cpp
./objs/src/app/srs_app_http_api.o: $(APP_DEPS) ./src/app/srs_app_http_api.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_http_api.o \
./src/app/srs_app_http_api.cpp
./objs/src/app/srs_app_http_conn.o: $(APP_DEPS) ./src/app/srs_app_http_conn.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_http_conn.o \
./src/app/srs_app_http_conn.cpp
./objs/src/app/srs_app_http_hooks.o: $(APP_DEPS) ./src/app/srs_app_http_hooks.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_http_hooks.o \
./src/app/srs_app_http_hooks.cpp
./objs/src/app/srs_app_ingest.o: $(APP_DEPS) ./src/app/srs_app_ingest.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_ingest.o \
./src/app/srs_app_ingest.cpp
./objs/src/app/srs_app_ffmpeg.o: $(APP_DEPS) ./src/app/srs_app_ffmpeg.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_ffmpeg.o \
./src/app/srs_app_ffmpeg.cpp
./objs/src/app/srs_app_utility.o: $(APP_DEPS) ./src/app/srs_app_utility.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_utility.o \
./src/app/srs_app_utility.cpp
./objs/src/app/srs_app_edge.o: $(APP_DEPS) ./src/app/srs_app_edge.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_edge.o \
./src/app/srs_app_edge.cpp
./objs/src/app/srs_app_heartbeat.o: $(APP_DEPS) ./src/app/srs_app_heartbeat.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_heartbeat.o \
./src/app/srs_app_heartbeat.cpp
./objs/src/app/srs_app_empty.o: $(APP_DEPS) ./src/app/srs_app_empty.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_empty.o \
./src/app/srs_app_empty.cpp
./objs/src/app/srs_app_http_client.o: $(APP_DEPS) ./src/app/srs_app_http_client.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_http_client.o \
./src/app/srs_app_http_client.cpp
./objs/src/app/srs_app_http_static.o: $(APP_DEPS) ./src/app/srs_app_http_static.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_http_static.o \
./src/app/srs_app_http_static.cpp
./objs/src/app/srs_app_recv_thread.o: $(APP_DEPS) ./src/app/srs_app_recv_thread.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_recv_thread.o \
./src/app/srs_app_recv_thread.cpp
./objs/src/app/srs_app_security.o: $(APP_DEPS) ./src/app/srs_app_security.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_security.o \
./src/app/srs_app_security.cpp
./objs/src/app/srs_app_statistic.o: $(APP_DEPS) ./src/app/srs_app_statistic.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_statistic.o \
./src/app/srs_app_statistic.cpp
./objs/src/app/srs_app_hds.o: $(APP_DEPS) ./src/app/srs_app_hds.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_hds.o \
./src/app/srs_app_hds.cpp
./objs/src/app/srs_app_mpegts_udp.o: $(APP_DEPS) ./src/app/srs_app_mpegts_udp.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_mpegts_udp.o \
./src/app/srs_app_mpegts_udp.cpp
./objs/src/app/srs_app_listener.o: $(APP_DEPS) ./src/app/srs_app_listener.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_listener.o \
./src/app/srs_app_listener.cpp
./objs/src/app/srs_app_async_call.o: $(APP_DEPS) ./src/app/srs_app_async_call.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_async_call.o \
./src/app/srs_app_async_call.cpp
./objs/src/app/srs_app_caster_flv.o: $(APP_DEPS) ./src/app/srs_app_caster_flv.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_caster_flv.o \
./src/app/srs_app_caster_flv.cpp
./objs/src/app/srs_app_latest_version.o: $(APP_DEPS) ./src/app/srs_app_latest_version.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_latest_version.o \
./src/app/srs_app_latest_version.cpp
./objs/src/app/srs_app_uuid.o: $(APP_DEPS) ./src/app/srs_app_uuid.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_uuid.o \
./src/app/srs_app_uuid.cpp
./objs/src/app/srs_app_process.o: $(APP_DEPS) ./src/app/srs_app_process.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_process.o \
./src/app/srs_app_process.cpp
./objs/src/app/srs_app_ng_exec.o: $(APP_DEPS) ./src/app/srs_app_ng_exec.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_ng_exec.o \
./src/app/srs_app_ng_exec.cpp
./objs/src/app/srs_app_hourglass.o: $(APP_DEPS) ./src/app/srs_app_hourglass.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_hourglass.o \
./src/app/srs_app_hourglass.cpp
./objs/src/app/srs_app_dash.o: $(APP_DEPS) ./src/app/srs_app_dash.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_dash.o \
./src/app/srs_app_dash.cpp
./objs/src/app/srs_app_fragment.o: $(APP_DEPS) ./src/app/srs_app_fragment.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_fragment.o \
./src/app/srs_app_fragment.cpp
./objs/src/app/srs_app_dvr.o: $(APP_DEPS) ./src/app/srs_app_dvr.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_dvr.o \
./src/app/srs_app_dvr.cpp
./objs/src/app/srs_app_coworkers.o: $(APP_DEPS) ./src/app/srs_app_coworkers.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_coworkers.o \
./src/app/srs_app_coworkers.cpp
./objs/src/app/srs_app_hybrid.o: $(APP_DEPS) ./src/app/srs_app_hybrid.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_hybrid.o \
./src/app/srs_app_hybrid.cpp
./objs/src/app/srs_app_threads.o: $(APP_DEPS) ./src/app/srs_app_threads.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_threads.o \
./src/app/srs_app_threads.cpp
./objs/src/app/srs_app_srt_server.o: $(APP_DEPS) ./src/app/srs_app_srt_server.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_srt_server.o \
./src/app/srs_app_srt_server.cpp
./objs/src/app/srs_app_srt_listener.o: $(APP_DEPS) ./src/app/srs_app_srt_listener.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_srt_listener.o \
./src/app/srs_app_srt_listener.cpp
./objs/src/app/srs_app_srt_conn.o: $(APP_DEPS) ./src/app/srs_app_srt_conn.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_srt_conn.o \
./src/app/srs_app_srt_conn.cpp
./objs/src/app/srs_app_srt_utility.o: $(APP_DEPS) ./src/app/srs_app_srt_utility.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_srt_utility.o \
./src/app/srs_app_srt_utility.cpp
./objs/src/app/srs_app_srt_source.o: $(APP_DEPS) ./src/app/srs_app_srt_source.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_srt_source.o \
./src/app/srs_app_srt_source.cpp
./objs/src/app/srs_app_rtc_conn.o: $(APP_DEPS) ./src/app/srs_app_rtc_conn.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_rtc_conn.o \
./src/app/srs_app_rtc_conn.cpp
./objs/src/app/srs_app_rtc_dtls.o: $(APP_DEPS) ./src/app/srs_app_rtc_dtls.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_rtc_dtls.o \
./src/app/srs_app_rtc_dtls.cpp
./objs/src/app/srs_app_rtc_sdp.o: $(APP_DEPS) ./src/app/srs_app_rtc_sdp.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_rtc_sdp.o \
./src/app/srs_app_rtc_sdp.cpp
./objs/src/app/srs_app_rtc_network.o: $(APP_DEPS) ./src/app/srs_app_rtc_network.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_rtc_network.o \
./src/app/srs_app_rtc_network.cpp
./objs/src/app/srs_app_rtc_queue.o: $(APP_DEPS) ./src/app/srs_app_rtc_queue.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_rtc_queue.o \
./src/app/srs_app_rtc_queue.cpp
./objs/src/app/srs_app_rtc_server.o: $(APP_DEPS) ./src/app/srs_app_rtc_server.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_rtc_server.o \
./src/app/srs_app_rtc_server.cpp
./objs/src/app/srs_app_rtc_source.o: $(APP_DEPS) ./src/app/srs_app_rtc_source.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_rtc_source.o \
./src/app/srs_app_rtc_source.cpp
./objs/src/app/srs_app_rtc_api.o: $(APP_DEPS) ./src/app/srs_app_rtc_api.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_rtc_api.o \
./src/app/srs_app_rtc_api.cpp
./objs/src/app/srs_app_rtc_codec.o: $(APP_DEPS) ./src/app/srs_app_rtc_codec.cpp
$(CXX) -c $(CXXFLAGS) \
$(APP_INCS)\
$(APP_LIBS_INCS)\
-o ./objs/src/app/srs_app_rtc_codec.o \
./src/app/srs_app_rtc_codec.cpp
#####################################################################################
# The module SERVER.
#####################################################################################
# INCS for SERVER, headers of module and its depends to compile
SERVER_MODULE_INCS = -I./src/main
SERVER_INCS = -I./src/main $(CORE_MODULE_INCS)$(KERNEL_MODULE_INCS)$(PROTOCOL_MODULE_INCS)$(APP_MODULE_INCS)
SERVER_LIBS_INCS = -I./objs -I./objs/openssl/include -I./objs/srtp2/include -I./objs/ffmpeg/include
# DEPS for SERVER, the depends of make schema
SERVER_DEPS = $(CORE_DEPS) $(KERNEL_DEPS) $(PROTOCOL_DEPS) $(APP_DEPS)
# OBJ for SERVER, each object file
./objs/src/main/srs_main_server.o: $(SERVER_DEPS) ./src/main/srs_main_server.cpp
$(CXX) -c $(CXXFLAGS) \
$(SERVER_INCS)\
$(SERVER_LIBS_INCS)\
-o ./objs/src/main/srs_main_server.o \
./src/main/srs_main_server.cpp
#####################################################################################
# The module MAIN.
#####################################################################################
# INCS for MAIN, headers of module and its depends to compile
MAIN_MODULE_INCS = -I./src/main
MAIN_INCS = -I./src/main $(CORE_MODULE_INCS)$(KERNEL_MODULE_INCS)$(PROTOCOL_MODULE_INCS)$(APP_MODULE_INCS)
MAIN_LIBS_INCS = -I./objs -I./objs/openssl/include -I./objs/srtp2/include -I./objs/ffmpeg/include
# DEPS for MAIN, the depends of make schema
MAIN_DEPS = $(CORE_DEPS) $(KERNEL_DEPS) $(PROTOCOL_DEPS) $(APP_DEPS)
# OBJ for MAIN, each object file
./objs/src/main/srs_main_mp4_parser.o: $(MAIN_DEPS) ./src/main/srs_main_mp4_parser.cpp
$(CXX) -c $(CXXFLAGS) \
$(MAIN_INCS)\
$(MAIN_LIBS_INCS)\
-o ./objs/src/main/srs_main_mp4_parser.o \
./src/main/srs_main_mp4_parser.cpp
./objs/src/main/srs_main_ingest_hls.o: $(MAIN_DEPS) ./src/main/srs_main_ingest_hls.cpp
$(CXX) -c $(CXXFLAGS) \
$(MAIN_INCS)\
$(MAIN_LIBS_INCS)\
-o ./objs/src/main/srs_main_ingest_hls.o \
./src/main/srs_main_ingest_hls.cpp
# build ./objs/srs
srs: ./objs/srs
./objs/srs: ./objs/src/core/srs_core.o ./objs/src/core/srs_core_version.o ./objs/src/core/srs_core_version5.o ./objs/src/core/srs_core_autofree.o ./objs/src/core/srs_core_performance.o ./objs/src/core/srs_core_time.o ./objs/src/core/srs_core_platform.o ./objs/src/kernel/srs_kernel_error.o ./objs/src/kernel/srs_kernel_log.o ./objs/src/kernel/srs_kernel_buffer.o ./objs/src/kernel/srs_kernel_utility.o ./objs/src/kernel/srs_kernel_flv.o ./objs/src/kernel/srs_kernel_codec.o ./objs/src/kernel/srs_kernel_io.o ./objs/src/kernel/srs_kernel_consts.o ./objs/src/kernel/srs_kernel_aac.o ./objs/src/kernel/srs_kernel_mp3.o ./objs/src/kernel/srs_kernel_ts.o ./objs/src/kernel/srs_kernel_ps.o ./objs/src/kernel/srs_kernel_stream.o ./objs/src/kernel/srs_kernel_balance.o ./objs/src/kernel/srs_kernel_mp4.o ./objs/src/kernel/srs_kernel_file.o ./objs/src/kernel/srs_kernel_kbps.o ./objs/src/kernel/srs_kernel_rtc_rtp.o ./objs/src/kernel/srs_kernel_rtc_rtcp.o ./objs/src/protocol/srs_protocol_amf0.o ./objs/src/protocol/srs_protocol_io.o ./objs/src/protocol/srs_protocol_conn.o ./objs/src/protocol/srs_protocol_rtmp_handshake.o ./objs/src/protocol/srs_protocol_rtmp_stack.o ./objs/src/protocol/srs_protocol_utility.o ./objs/src/protocol/srs_protocol_rtmp_msg_array.o ./objs/src/protocol/srs_protocol_stream.o ./objs/src/protocol/srs_protocol_raw_avc.o ./objs/src/protocol/srs_protocol_http_stack.o ./objs/src/protocol/srs_protocol_kbps.o ./objs/src/protocol/srs_protocol_json.o ./objs/src/protocol/srs_protocol_format.o ./objs/src/protocol/srs_protocol_log.o ./objs/src/protocol/srs_protocol_st.o ./objs/src/protocol/srs_protocol_http_client.o ./objs/src/protocol/srs_protocol_http_conn.o ./objs/src/protocol/srs_protocol_rtmp_conn.o ./objs/src/protocol/srs_protocol_protobuf.o ./objs/src/protocol/srs_protocol_srt.o ./objs/src/protocol/srs_protocol_rtc_stun.o ./objs/src/app/srs_app_server.o ./objs/src/app/srs_app_conn.o ./objs/src/app/srs_app_rtmp_conn.o ./objs/src/app/srs_app_source.o ./objs/src/app/srs_app_refer.o ./objs/src/app/srs_app_hls.o ./objs/src/app/srs_app_forward.o ./objs/src/app/srs_app_encoder.o ./objs/src/app/srs_app_http_stream.o ./objs/src/app/srs_app_st.o ./objs/src/app/srs_app_log.o ./objs/src/app/srs_app_config.o ./objs/src/app/srs_app_stream_bridge.o ./objs/src/app/srs_app_pithy_print.o ./objs/src/app/srs_app_reload.o ./objs/src/app/srs_app_http_api.o ./objs/src/app/srs_app_http_conn.o ./objs/src/app/srs_app_http_hooks.o ./objs/src/app/srs_app_ingest.o ./objs/src/app/srs_app_ffmpeg.o ./objs/src/app/srs_app_utility.o ./objs/src/app/srs_app_edge.o ./objs/src/app/srs_app_heartbeat.o ./objs/src/app/srs_app_empty.o ./objs/src/app/srs_app_http_client.o ./objs/src/app/srs_app_http_static.o ./objs/src/app/srs_app_recv_thread.o ./objs/src/app/srs_app_security.o ./objs/src/app/srs_app_statistic.o ./objs/src/app/srs_app_hds.o ./objs/src/app/srs_app_mpegts_udp.o ./objs/src/app/srs_app_listener.o ./objs/src/app/srs_app_async_call.o ./objs/src/app/srs_app_caster_flv.o ./objs/src/app/srs_app_latest_version.o ./objs/src/app/srs_app_uuid.o ./objs/src/app/srs_app_process.o ./objs/src/app/srs_app_ng_exec.o ./objs/src/app/srs_app_hourglass.o ./objs/src/app/srs_app_dash.o ./objs/src/app/srs_app_fragment.o ./objs/src/app/srs_app_dvr.o ./objs/src/app/srs_app_coworkers.o ./objs/src/app/srs_app_hybrid.o ./objs/src/app/srs_app_threads.o ./objs/src/app/srs_app_srt_server.o ./objs/src/app/srs_app_srt_listener.o ./objs/src/app/srs_app_srt_conn.o ./objs/src/app/srs_app_srt_utility.o ./objs/src/app/srs_app_srt_source.o ./objs/src/app/srs_app_rtc_conn.o ./objs/src/app/srs_app_rtc_dtls.o ./objs/src/app/srs_app_rtc_sdp.o ./objs/src/app/srs_app_rtc_network.o ./objs/src/app/srs_app_rtc_queue.o ./objs/src/app/srs_app_rtc_server.o ./objs/src/app/srs_app_rtc_source.o ./objs/src/app/srs_app_rtc_api.o ./objs/src/app/srs_app_rtc_codec.o ./objs/src/main/srs_main_server.o
$(LINK) -o ./objs/srs ./objs/src/core/srs_core.o ./objs/src/core/srs_core_version.o ./objs/src/core/srs_core_version5.o ./objs/src/core/srs_core_autofree.o ./objs/src/core/srs_core_performance.o ./objs/src/core/srs_core_time.o ./objs/src/core/srs_core_platform.o ./objs/src/kernel/srs_kernel_error.o ./objs/src/kernel/srs_kernel_log.o ./objs/src/kernel/srs_kernel_buffer.o ./objs/src/kernel/srs_kernel_utility.o ./objs/src/kernel/srs_kernel_flv.o ./objs/src/kernel/srs_kernel_codec.o ./objs/src/kernel/srs_kernel_io.o ./objs/src/kernel/srs_kernel_consts.o ./objs/src/kernel/srs_kernel_aac.o ./objs/src/kernel/srs_kernel_mp3.o ./objs/src/kernel/srs_kernel_ts.o ./objs/src/kernel/srs_kernel_ps.o ./objs/src/kernel/srs_kernel_stream.o ./objs/src/kernel/srs_kernel_balance.o ./objs/src/kernel/srs_kernel_mp4.o ./objs/src/kernel/srs_kernel_file.o ./objs/src/kernel/srs_kernel_kbps.o ./objs/src/kernel/srs_kernel_rtc_rtp.o ./objs/src/kernel/srs_kernel_rtc_rtcp.o ./objs/src/protocol/srs_protocol_amf0.o ./objs/src/protocol/srs_protocol_io.o ./objs/src/protocol/srs_protocol_conn.o ./objs/src/protocol/srs_protocol_rtmp_handshake.o ./objs/src/protocol/srs_protocol_rtmp_stack.o ./objs/src/protocol/srs_protocol_utility.o ./objs/src/protocol/srs_protocol_rtmp_msg_array.o ./objs/src/protocol/srs_protocol_stream.o ./objs/src/protocol/srs_protocol_raw_avc.o ./objs/src/protocol/srs_protocol_http_stack.o ./objs/src/protocol/srs_protocol_kbps.o ./objs/src/protocol/srs_protocol_json.o ./objs/src/protocol/srs_protocol_format.o ./objs/src/protocol/srs_protocol_log.o ./objs/src/protocol/srs_protocol_st.o ./objs/src/protocol/srs_protocol_http_client.o ./objs/src/protocol/srs_protocol_http_conn.o ./objs/src/protocol/srs_protocol_rtmp_conn.o ./objs/src/protocol/srs_protocol_protobuf.o ./objs/src/protocol/srs_protocol_srt.o ./objs/src/protocol/srs_protocol_rtc_stun.o ./objs/src/app/srs_app_server.o ./objs/src/app/srs_app_conn.o ./objs/src/app/srs_app_rtmp_conn.o ./objs/src/app/srs_app_source.o ./objs/src/app/srs_app_refer.o ./objs/src/app/srs_app_hls.o ./objs/src/app/srs_app_forward.o ./objs/src/app/srs_app_encoder.o ./objs/src/app/srs_app_http_stream.o ./objs/src/app/srs_app_st.o ./objs/src/app/srs_app_log.o ./objs/src/app/srs_app_config.o ./objs/src/app/srs_app_stream_bridge.o ./objs/src/app/srs_app_pithy_print.o ./objs/src/app/srs_app_reload.o ./objs/src/app/srs_app_http_api.o ./objs/src/app/srs_app_http_conn.o ./objs/src/app/srs_app_http_hooks.o ./objs/src/app/srs_app_ingest.o ./objs/src/app/srs_app_ffmpeg.o ./objs/src/app/srs_app_utility.o ./objs/src/app/srs_app_edge.o ./objs/src/app/srs_app_heartbeat.o ./objs/src/app/srs_app_empty.o ./objs/src/app/srs_app_http_client.o ./objs/src/app/srs_app_http_static.o ./objs/src/app/srs_app_recv_thread.o ./objs/src/app/srs_app_security.o ./objs/src/app/srs_app_statistic.o ./objs/src/app/srs_app_hds.o ./objs/src/app/srs_app_mpegts_udp.o ./objs/src/app/srs_app_listener.o ./objs/src/app/srs_app_async_call.o ./objs/src/app/srs_app_caster_flv.o ./objs/src/app/srs_app_latest_version.o ./objs/src/app/srs_app_uuid.o ./objs/src/app/srs_app_process.o ./objs/src/app/srs_app_ng_exec.o ./objs/src/app/srs_app_hourglass.o ./objs/src/app/srs_app_dash.o ./objs/src/app/srs_app_fragment.o ./objs/src/app/srs_app_dvr.o ./objs/src/app/srs_app_coworkers.o ./objs/src/app/srs_app_hybrid.o ./objs/src/app/srs_app_threads.o ./objs/src/app/srs_app_srt_server.o ./objs/src/app/srs_app_srt_listener.o ./objs/src/app/srs_app_srt_conn.o ./objs/src/app/srs_app_srt_utility.o ./objs/src/app/srs_app_srt_source.o ./objs/src/app/srs_app_rtc_conn.o ./objs/src/app/srs_app_rtc_dtls.o ./objs/src/app/srs_app_rtc_sdp.o ./objs/src/app/srs_app_rtc_network.o ./objs/src/app/srs_app_rtc_queue.o ./objs/src/app/srs_app_rtc_server.o ./objs/src/app/srs_app_rtc_source.o ./objs/src/app/srs_app_rtc_api.o ./objs/src/app/srs_app_rtc_codec.o ./objs/src/main/srs_main_server.o ./objs/st/libst.a ./objs/openssl/lib/libssl.a ./objs/openssl/lib/libcrypto.a ./objs/srtp2/lib/libsrtp2.a ./objs/ffmpeg/lib/libavcodec.a ./objs/ffmpeg/lib/libswresample.a ./objs/ffmpeg/lib/libavutil.a ./objs/opus/lib/libopus.a ./objs/srt/lib/libsrt.a -ldl -lpthread -lrt -rdynamic -fsanitize=address -fno-omit-frame-pointer -static-libasan
# build ./objs/srs_mp4_parser
srs_mp4_parser: ./objs/srs_mp4_parser
./objs/srs_mp4_parser: ./objs/src/core/srs_core.o ./objs/src/core/srs_core_version.o ./objs/src/core/srs_core_version5.o ./objs/src/core/srs_core_autofree.o ./objs/src/core/srs_core_performance.o ./objs/src/core/srs_core_time.o ./objs/src/core/srs_core_platform.o ./objs/src/kernel/srs_kernel_error.o ./objs/src/kernel/srs_kernel_log.o ./objs/src/kernel/srs_kernel_buffer.o ./objs/src/kernel/srs_kernel_utility.o ./objs/src/kernel/srs_kernel_flv.o ./objs/src/kernel/srs_kernel_codec.o ./objs/src/kernel/srs_kernel_io.o ./objs/src/kernel/srs_kernel_consts.o ./objs/src/kernel/srs_kernel_aac.o ./objs/src/kernel/srs_kernel_mp3.o ./objs/src/kernel/srs_kernel_ts.o ./objs/src/kernel/srs_kernel_ps.o ./objs/src/kernel/srs_kernel_stream.o ./objs/src/kernel/srs_kernel_balance.o ./objs/src/kernel/srs_kernel_mp4.o ./objs/src/kernel/srs_kernel_file.o ./objs/src/kernel/srs_kernel_kbps.o ./objs/src/kernel/srs_kernel_rtc_rtp.o ./objs/src/kernel/srs_kernel_rtc_rtcp.o ./objs/src/protocol/srs_protocol_amf0.o ./objs/src/protocol/srs_protocol_io.o ./objs/src/protocol/srs_protocol_conn.o ./objs/src/protocol/srs_protocol_rtmp_handshake.o ./objs/src/protocol/srs_protocol_rtmp_stack.o ./objs/src/protocol/srs_protocol_utility.o ./objs/src/protocol/srs_protocol_rtmp_msg_array.o ./objs/src/protocol/srs_protocol_stream.o ./objs/src/protocol/srs_protocol_raw_avc.o ./objs/src/protocol/srs_protocol_http_stack.o ./objs/src/protocol/srs_protocol_kbps.o ./objs/src/protocol/srs_protocol_json.o ./objs/src/protocol/srs_protocol_format.o ./objs/src/protocol/srs_protocol_log.o ./objs/src/protocol/srs_protocol_st.o ./objs/src/protocol/srs_protocol_http_client.o ./objs/src/protocol/srs_protocol_http_conn.o ./objs/src/protocol/srs_protocol_rtmp_conn.o ./objs/src/protocol/srs_protocol_protobuf.o ./objs/src/protocol/srs_protocol_srt.o ./objs/src/protocol/srs_protocol_rtc_stun.o ./objs/src/app/srs_app_server.o ./objs/src/app/srs_app_conn.o ./objs/src/app/srs_app_rtmp_conn.o ./objs/src/app/srs_app_source.o ./objs/src/app/srs_app_refer.o ./objs/src/app/srs_app_hls.o ./objs/src/app/srs_app_forward.o ./objs/src/app/srs_app_encoder.o ./objs/src/app/srs_app_http_stream.o ./objs/src/app/srs_app_st.o ./objs/src/app/srs_app_log.o ./objs/src/app/srs_app_config.o ./objs/src/app/srs_app_stream_bridge.o ./objs/src/app/srs_app_pithy_print.o ./objs/src/app/srs_app_reload.o ./objs/src/app/srs_app_http_api.o ./objs/src/app/srs_app_http_conn.o ./objs/src/app/srs_app_http_hooks.o ./objs/src/app/srs_app_ingest.o ./objs/src/app/srs_app_ffmpeg.o ./objs/src/app/srs_app_utility.o ./objs/src/app/srs_app_edge.o ./objs/src/app/srs_app_heartbeat.o ./objs/src/app/srs_app_empty.o ./objs/src/app/srs_app_http_client.o ./objs/src/app/srs_app_http_static.o ./objs/src/app/srs_app_recv_thread.o ./objs/src/app/srs_app_security.o ./objs/src/app/srs_app_statistic.o ./objs/src/app/srs_app_hds.o ./objs/src/app/srs_app_mpegts_udp.o ./objs/src/app/srs_app_listener.o ./objs/src/app/srs_app_async_call.o ./objs/src/app/srs_app_caster_flv.o ./objs/src/app/srs_app_latest_version.o ./objs/src/app/srs_app_uuid.o ./objs/src/app/srs_app_process.o ./objs/src/app/srs_app_ng_exec.o ./objs/src/app/srs_app_hourglass.o ./objs/src/app/srs_app_dash.o ./objs/src/app/srs_app_fragment.o ./objs/src/app/srs_app_dvr.o ./objs/src/app/srs_app_coworkers.o ./objs/src/app/srs_app_hybrid.o ./objs/src/app/srs_app_threads.o ./objs/src/app/srs_app_srt_server.o ./objs/src/app/srs_app_srt_listener.o ./objs/src/app/srs_app_srt_conn.o ./objs/src/app/srs_app_srt_utility.o ./objs/src/app/srs_app_srt_source.o ./objs/src/app/srs_app_rtc_conn.o ./objs/src/app/srs_app_rtc_dtls.o ./objs/src/app/srs_app_rtc_sdp.o ./objs/src/app/srs_app_rtc_network.o ./objs/src/app/srs_app_rtc_queue.o ./objs/src/app/srs_app_rtc_server.o ./objs/src/app/srs_app_rtc_source.o ./objs/src/app/srs_app_rtc_api.o ./objs/src/app/srs_app_rtc_codec.o ./objs/src/main/srs_main_mp4_parser.o
$(LINK) -o ./objs/srs_mp4_parser ./objs/src/core/srs_core.o ./objs/src/core/srs_core_version.o ./objs/src/core/srs_core_version5.o ./objs/src/core/srs_core_autofree.o ./objs/src/core/srs_core_performance.o ./objs/src/core/srs_core_time.o ./objs/src/core/srs_core_platform.o ./objs/src/kernel/srs_kernel_error.o ./objs/src/kernel/srs_kernel_log.o ./objs/src/kernel/srs_kernel_buffer.o ./objs/src/kernel/srs_kernel_utility.o ./objs/src/kernel/srs_kernel_flv.o ./objs/src/kernel/srs_kernel_codec.o ./objs/src/kernel/srs_kernel_io.o ./objs/src/kernel/srs_kernel_consts.o ./objs/src/kernel/srs_kernel_aac.o ./objs/src/kernel/srs_kernel_mp3.o ./objs/src/kernel/srs_kernel_ts.o ./objs/src/kernel/srs_kernel_ps.o ./objs/src/kernel/srs_kernel_stream.o ./objs/src/kernel/srs_kernel_balance.o ./objs/src/kernel/srs_kernel_mp4.o ./objs/src/kernel/srs_kernel_file.o ./objs/src/kernel/srs_kernel_kbps.o ./objs/src/kernel/srs_kernel_rtc_rtp.o ./objs/src/kernel/srs_kernel_rtc_rtcp.o ./objs/src/protocol/srs_protocol_amf0.o ./objs/src/protocol/srs_protocol_io.o ./objs/src/protocol/srs_protocol_conn.o ./objs/src/protocol/srs_protocol_rtmp_handshake.o ./objs/src/protocol/srs_protocol_rtmp_stack.o ./objs/src/protocol/srs_protocol_utility.o ./objs/src/protocol/srs_protocol_rtmp_msg_array.o ./objs/src/protocol/srs_protocol_stream.o ./objs/src/protocol/srs_protocol_raw_avc.o ./objs/src/protocol/srs_protocol_http_stack.o ./objs/src/protocol/srs_protocol_kbps.o ./objs/src/protocol/srs_protocol_json.o ./objs/src/protocol/srs_protocol_format.o ./objs/src/protocol/srs_protocol_log.o ./objs/src/protocol/srs_protocol_st.o ./objs/src/protocol/srs_protocol_http_client.o ./objs/src/protocol/srs_protocol_http_conn.o ./objs/src/protocol/srs_protocol_rtmp_conn.o ./objs/src/protocol/srs_protocol_protobuf.o ./objs/src/protocol/srs_protocol_srt.o ./objs/src/protocol/srs_protocol_rtc_stun.o ./objs/src/app/srs_app_server.o ./objs/src/app/srs_app_conn.o ./objs/src/app/srs_app_rtmp_conn.o ./objs/src/app/srs_app_source.o ./objs/src/app/srs_app_refer.o ./objs/src/app/srs_app_hls.o ./objs/src/app/srs_app_forward.o ./objs/src/app/srs_app_encoder.o ./objs/src/app/srs_app_http_stream.o ./objs/src/app/srs_app_st.o ./objs/src/app/srs_app_log.o ./objs/src/app/srs_app_config.o ./objs/src/app/srs_app_stream_bridge.o ./objs/src/app/srs_app_pithy_print.o ./objs/src/app/srs_app_reload.o ./objs/src/app/srs_app_http_api.o ./objs/src/app/srs_app_http_conn.o ./objs/src/app/srs_app_http_hooks.o ./objs/src/app/srs_app_ingest.o ./objs/src/app/srs_app_ffmpeg.o ./objs/src/app/srs_app_utility.o ./objs/src/app/srs_app_edge.o ./objs/src/app/srs_app_heartbeat.o ./objs/src/app/srs_app_empty.o ./objs/src/app/srs_app_http_client.o ./objs/src/app/srs_app_http_static.o ./objs/src/app/srs_app_recv_thread.o ./objs/src/app/srs_app_security.o ./objs/src/app/srs_app_statistic.o ./objs/src/app/srs_app_hds.o ./objs/src/app/srs_app_mpegts_udp.o ./objs/src/app/srs_app_listener.o ./objs/src/app/srs_app_async_call.o ./objs/src/app/srs_app_caster_flv.o ./objs/src/app/srs_app_latest_version.o ./objs/src/app/srs_app_uuid.o ./objs/src/app/srs_app_process.o ./objs/src/app/srs_app_ng_exec.o ./objs/src/app/srs_app_hourglass.o ./objs/src/app/srs_app_dash.o ./objs/src/app/srs_app_fragment.o ./objs/src/app/srs_app_dvr.o ./objs/src/app/srs_app_coworkers.o ./objs/src/app/srs_app_hybrid.o ./objs/src/app/srs_app_threads.o ./objs/src/app/srs_app_srt_server.o ./objs/src/app/srs_app_srt_listener.o ./objs/src/app/srs_app_srt_conn.o ./objs/src/app/srs_app_srt_utility.o ./objs/src/app/srs_app_srt_source.o ./objs/src/app/srs_app_rtc_conn.o ./objs/src/app/srs_app_rtc_dtls.o ./objs/src/app/srs_app_rtc_sdp.o ./objs/src/app/srs_app_rtc_network.o ./objs/src/app/srs_app_rtc_queue.o ./objs/src/app/srs_app_rtc_server.o ./objs/src/app/srs_app_rtc_source.o ./objs/src/app/srs_app_rtc_api.o ./objs/src/app/srs_app_rtc_codec.o ./objs/src/main/srs_main_mp4_parser.o ./objs/st/libst.a ./objs/openssl/lib/libssl.a ./objs/openssl/lib/libcrypto.a ./objs/srtp2/lib/libsrtp2.a ./objs/ffmpeg/lib/libavcodec.a ./objs/ffmpeg/lib/libswresample.a ./objs/ffmpeg/lib/libavutil.a ./objs/opus/lib/libopus.a ./objs/srt/lib/libsrt.a -ldl -lpthread -lrt -rdynamic -fsanitize=address -fno-omit-frame-pointer -static-libasan
# build ./objs/srs_hls_ingester
srs_hls_ingester: ./objs/srs_hls_ingester
./objs/srs_hls_ingester: ./objs/src/core/srs_core.o ./objs/src/core/srs_core_version.o ./objs/src/core/srs_core_version5.o ./objs/src/core/srs_core_autofree.o ./objs/src/core/srs_core_performance.o ./objs/src/core/srs_core_time.o ./objs/src/core/srs_core_platform.o ./objs/src/kernel/srs_kernel_error.o ./objs/src/kernel/srs_kernel_log.o ./objs/src/kernel/srs_kernel_buffer.o ./objs/src/kernel/srs_kernel_utility.o ./objs/src/kernel/srs_kernel_flv.o ./objs/src/kernel/srs_kernel_codec.o ./objs/src/kernel/srs_kernel_io.o ./objs/src/kernel/srs_kernel_consts.o ./objs/src/kernel/srs_kernel_aac.o ./objs/src/kernel/srs_kernel_mp3.o ./objs/src/kernel/srs_kernel_ts.o ./objs/src/kernel/srs_kernel_ps.o ./objs/src/kernel/srs_kernel_stream.o ./objs/src/kernel/srs_kernel_balance.o ./objs/src/kernel/srs_kernel_mp4.o ./objs/src/kernel/srs_kernel_file.o ./objs/src/kernel/srs_kernel_kbps.o ./objs/src/kernel/srs_kernel_rtc_rtp.o ./objs/src/kernel/srs_kernel_rtc_rtcp.o ./objs/src/protocol/srs_protocol_amf0.o ./objs/src/protocol/srs_protocol_io.o ./objs/src/protocol/srs_protocol_conn.o ./objs/src/protocol/srs_protocol_rtmp_handshake.o ./objs/src/protocol/srs_protocol_rtmp_stack.o ./objs/src/protocol/srs_protocol_utility.o ./objs/src/protocol/srs_protocol_rtmp_msg_array.o ./objs/src/protocol/srs_protocol_stream.o ./objs/src/protocol/srs_protocol_raw_avc.o ./objs/src/protocol/srs_protocol_http_stack.o ./objs/src/protocol/srs_protocol_kbps.o ./objs/src/protocol/srs_protocol_json.o ./objs/src/protocol/srs_protocol_format.o ./objs/src/protocol/srs_protocol_log.o ./objs/src/protocol/srs_protocol_st.o ./objs/src/protocol/srs_protocol_http_client.o ./objs/src/protocol/srs_protocol_http_conn.o ./objs/src/protocol/srs_protocol_rtmp_conn.o ./objs/src/protocol/srs_protocol_protobuf.o ./objs/src/protocol/srs_protocol_srt.o ./objs/src/protocol/srs_protocol_rtc_stun.o ./objs/src/app/srs_app_server.o ./objs/src/app/srs_app_conn.o ./objs/src/app/srs_app_rtmp_conn.o ./objs/src/app/srs_app_source.o ./objs/src/app/srs_app_refer.o ./objs/src/app/srs_app_hls.o ./objs/src/app/srs_app_forward.o ./objs/src/app/srs_app_encoder.o ./objs/src/app/srs_app_http_stream.o ./objs/src/app/srs_app_st.o ./objs/src/app/srs_app_log.o ./objs/src/app/srs_app_config.o ./objs/src/app/srs_app_stream_bridge.o ./objs/src/app/srs_app_pithy_print.o ./objs/src/app/srs_app_reload.o ./objs/src/app/srs_app_http_api.o ./objs/src/app/srs_app_http_conn.o ./objs/src/app/srs_app_http_hooks.o ./objs/src/app/srs_app_ingest.o ./objs/src/app/srs_app_ffmpeg.o ./objs/src/app/srs_app_utility.o ./objs/src/app/srs_app_edge.o ./objs/src/app/srs_app_heartbeat.o ./objs/src/app/srs_app_empty.o ./objs/src/app/srs_app_http_client.o ./objs/src/app/srs_app_http_static.o ./objs/src/app/srs_app_recv_thread.o ./objs/src/app/srs_app_security.o ./objs/src/app/srs_app_statistic.o ./objs/src/app/srs_app_hds.o ./objs/src/app/srs_app_mpegts_udp.o ./objs/src/app/srs_app_listener.o ./objs/src/app/srs_app_async_call.o ./objs/src/app/srs_app_caster_flv.o ./objs/src/app/srs_app_latest_version.o ./objs/src/app/srs_app_uuid.o ./objs/src/app/srs_app_process.o ./objs/src/app/srs_app_ng_exec.o ./objs/src/app/srs_app_hourglass.o ./objs/src/app/srs_app_dash.o ./objs/src/app/srs_app_fragment.o ./objs/src/app/srs_app_dvr.o ./objs/src/app/srs_app_coworkers.o ./objs/src/app/srs_app_hybrid.o ./objs/src/app/srs_app_threads.o ./objs/src/app/srs_app_srt_server.o ./objs/src/app/srs_app_srt_listener.o ./objs/src/app/srs_app_srt_conn.o ./objs/src/app/srs_app_srt_utility.o ./objs/src/app/srs_app_srt_source.o ./objs/src/app/srs_app_rtc_conn.o ./objs/src/app/srs_app_rtc_dtls.o ./objs/src/app/srs_app_rtc_sdp.o ./objs/src/app/srs_app_rtc_network.o ./objs/src/app/srs_app_rtc_queue.o ./objs/src/app/srs_app_rtc_server.o ./objs/src/app/srs_app_rtc_source.o ./objs/src/app/srs_app_rtc_api.o ./objs/src/app/srs_app_rtc_codec.o ./objs/src/main/srs_main_ingest_hls.o
$(LINK) -o ./objs/srs_hls_ingester ./objs/src/core/srs_core.o ./objs/src/core/srs_core_version.o ./objs/src/core/srs_core_version5.o ./objs/src/core/srs_core_autofree.o ./objs/src/core/srs_core_performance.o ./objs/src/core/srs_core_time.o ./objs/src/core/srs_core_platform.o ./objs/src/kernel/srs_kernel_error.o ./objs/src/kernel/srs_kernel_log.o ./objs/src/kernel/srs_kernel_buffer.o ./objs/src/kernel/srs_kernel_utility.o ./objs/src/kernel/srs_kernel_flv.o ./objs/src/kernel/srs_kernel_codec.o ./objs/src/kernel/srs_kernel_io.o ./objs/src/kernel/srs_kernel_consts.o ./objs/src/kernel/srs_kernel_aac.o ./objs/src/kernel/srs_kernel_mp3.o ./objs/src/kernel/srs_kernel_ts.o ./objs/src/kernel/srs_kernel_ps.o ./objs/src/kernel/srs_kernel_stream.o ./objs/src/kernel/srs_kernel_balance.o ./objs/src/kernel/srs_kernel_mp4.o ./objs/src/kernel/srs_kernel_file.o ./objs/src/kernel/srs_kernel_kbps.o ./objs/src/kernel/srs_kernel_rtc_rtp.o ./objs/src/kernel/srs_kernel_rtc_rtcp.o ./objs/src/protocol/srs_protocol_amf0.o ./objs/src/protocol/srs_protocol_io.o ./objs/src/protocol/srs_protocol_conn.o ./objs/src/protocol/srs_protocol_rtmp_handshake.o ./objs/src/protocol/srs_protocol_rtmp_stack.o ./objs/src/protocol/srs_protocol_utility.o ./objs/src/protocol/srs_protocol_rtmp_msg_array.o ./objs/src/protocol/srs_protocol_stream.o ./objs/src/protocol/srs_protocol_raw_avc.o ./objs/src/protocol/srs_protocol_http_stack.o ./objs/src/protocol/srs_protocol_kbps.o ./objs/src/protocol/srs_protocol_json.o ./objs/src/protocol/srs_protocol_format.o ./objs/src/protocol/srs_protocol_log.o ./objs/src/protocol/srs_protocol_st.o ./objs/src/protocol/srs_protocol_http_client.o ./objs/src/protocol/srs_protocol_http_conn.o ./objs/src/protocol/srs_protocol_rtmp_conn.o ./objs/src/protocol/srs_protocol_protobuf.o ./objs/src/protocol/srs_protocol_srt.o ./objs/src/protocol/srs_protocol_rtc_stun.o ./objs/src/app/srs_app_server.o ./objs/src/app/srs_app_conn.o ./objs/src/app/srs_app_rtmp_conn.o ./objs/src/app/srs_app_source.o ./objs/src/app/srs_app_refer.o ./objs/src/app/srs_app_hls.o ./objs/src/app/srs_app_forward.o ./objs/src/app/srs_app_encoder.o ./objs/src/app/srs_app_http_stream.o ./objs/src/app/srs_app_st.o ./objs/src/app/srs_app_log.o ./objs/src/app/srs_app_config.o ./objs/src/app/srs_app_stream_bridge.o ./objs/src/app/srs_app_pithy_print.o ./objs/src/app/srs_app_reload.o ./objs/src/app/srs_app_http_api.o ./objs/src/app/srs_app_http_conn.o ./objs/src/app/srs_app_http_hooks.o ./objs/src/app/srs_app_ingest.o ./objs/src/app/srs_app_ffmpeg.o ./objs/src/app/srs_app_utility.o ./objs/src/app/srs_app_edge.o ./objs/src/app/srs_app_heartbeat.o ./objs/src/app/srs_app_empty.o ./objs/src/app/srs_app_http_client.o ./objs/src/app/srs_app_http_static.o ./objs/src/app/srs_app_recv_thread.o ./objs/src/app/srs_app_security.o ./objs/src/app/srs_app_statistic.o ./objs/src/app/srs_app_hds.o ./objs/src/app/srs_app_mpegts_udp.o ./objs/src/app/srs_app_listener.o ./objs/src/app/srs_app_async_call.o ./objs/src/app/srs_app_caster_flv.o ./objs/src/app/srs_app_latest_version.o ./objs/src/app/srs_app_uuid.o ./objs/src/app/srs_app_process.o ./objs/src/app/srs_app_ng_exec.o ./objs/src/app/srs_app_hourglass.o ./objs/src/app/srs_app_dash.o ./objs/src/app/srs_app_fragment.o ./objs/src/app/srs_app_dvr.o ./objs/src/app/srs_app_coworkers.o ./objs/src/app/srs_app_hybrid.o ./objs/src/app/srs_app_threads.o ./objs/src/app/srs_app_srt_server.o ./objs/src/app/srs_app_srt_listener.o ./objs/src/app/srs_app_srt_conn.o ./objs/src/app/srs_app_srt_utility.o ./objs/src/app/srs_app_srt_source.o ./objs/src/app/srs_app_rtc_conn.o ./objs/src/app/srs_app_rtc_dtls.o ./objs/src/app/srs_app_rtc_sdp.o ./objs/src/app/srs_app_rtc_network.o ./objs/src/app/srs_app_rtc_queue.o ./objs/src/app/srs_app_rtc_server.o ./objs/src/app/srs_app_rtc_source.o ./objs/src/app/srs_app_rtc_api.o ./objs/src/app/srs_app_rtc_codec.o ./objs/src/main/srs_main_ingest_hls.o ./objs/st/libst.a ./objs/openssl/lib/libssl.a ./objs/openssl/lib/libcrypto.a ./objs/srtp2/lib/libsrtp2.a ./objs/ffmpeg/lib/libavcodec.a ./objs/ffmpeg/lib/libswresample.a ./objs/ffmpeg/lib/libavutil.a ./objs/opus/lib/libopus.a ./objs/srt/lib/libsrt.a -ldl -lpthread -lrt -rdynamic -fsanitize=address -fno-omit-frame-pointer -static-libasan
@@ -0,0 +1,31 @@
#!/bin/bash
#####################################################################################
# linux shell color support.
RED="\\033[31m"
GREEN="\\033[32m"
YELLOW="\\033[33m"
BLACK="\\033[0m"
echo -e "${GREEN}The build summary:${BLACK}"
echo -e " ${BLACK}+------------------------------------------------------------------------------------${BLACK}"
echo -e " ${GREEN}For SRS benchmark, gperf, gprof and valgrind, please read:${BLACK}"
echo -e " ${GREEN} https://www.jianshu.com/p/6d4a89359352${BLACK}"
echo -e " ${BLACK}+------------------------------------------------------------------------------------${BLACK}"
echo -e " |${GREEN}The main server usage: ./objs/srs -c conf/srs.conf, start the srs server${BLACK}"
echo -e " | ${GREEN}About HLS, please read https://ossrs.net/lts/zh-cn/docs/v4/doc/delivery-hls${BLACK}"
echo -e " | ${GREEN}About DVR, please read https://ossrs.net/lts/zh-cn/docs/v4/doc/dvr${BLACK}"
echo -e " | ${GREEN}About SSL, please read https://ossrs.net/lts/zh-cn/docs/v4/doc/rtmp-handshake${BLACK}"
echo -e " | ${GREEN}About transcoding, please read https://ossrs.net/lts/zh-cn/docs/v4/doc/ffmpeg${BLACK}"
echo -e " | ${GREEN}About ingester, please read https://ossrs.net/lts/zh-cn/docs/v4/doc/ingest${BLACK}"
echo -e " | ${GREEN}About http-callback, please read https://ossrs.net/lts/zh-cn/docs/v4/doc/http-callback${BLACK}"
echo -e " | ${GREEN}Aoubt http-server, please read https://ossrs.net/lts/zh-cn/docs/v4/doc/http-server${BLACK}"
echo -e " | ${GREEN}About http-api, please read https://ossrs.net/lts/zh-cn/docs/v4/doc/http-api${BLACK}"
echo -e " | ${GREEN}About stream-caster, please read https://ossrs.net/lts/zh-cn/docs/v4/doc/streamer${BLACK}"
echo -e " | ${YELLOW}(Disabled) About VALGRIND, please read https://github.com/ossrs/state-threads/issues/2${BLACK}"
echo -e " ${BLACK}+------------------------------------------------------------------------------------${BLACK}"
echo -e "${GREEN}binaries, please read https://ossrs.net/lts/zh-cn/docs/v4/doc/install${BLACK}"
echo "You can:"
echo " ./objs/srs -c conf/srs.conf"
echo " to start the srs server, with config conf/srs.conf."
@@ -0,0 +1,67 @@
<!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: 30px;
}
</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='//ossrs.net/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><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>
@@ -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>
@@ -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);
@@ -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
@@ -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);
@@ -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
@@ -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 one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -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');
}
}
});
}
}
}
});
@@ -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: 12 KiB

@@ -0,0 +1,103 @@
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("/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>';
@@ -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;
}
@@ -0,0 +1,104 @@
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("/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>';
@@ -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.slice(flag.length);
}
while (system_string_endswith(str, flag)) {
str = str.slice(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.slice(0, obj.pathname.lastIndexOf("/"));
obj.filename = obj.pathname.slice(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.substring(1, a.pathname.lastIndexOf("/"));
var stream = a.pathname.slice(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.slice(app.indexOf("?"));
app = app.slice(0, app.indexOf("?"));
if (params.indexOf("vhost=") > 0) {
vhost = params.slice(params.indexOf("vhost=") + "vhost=".length);
if (vhost.indexOf("&") > 0) {
vhost = vhost.slice(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.slice(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);
}
@@ -0,0 +1,67 @@
<!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: 30px;
}
</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='//ossrs.net/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><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>
@@ -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>
@@ -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>
@@ -0,0 +1,41 @@
<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>URL</th>
<th>分类</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 |sc_filter_streamURL}}</td>
<td>{{client.type}}</td>
<td>
<a ng-click="kickoff(client)" href="javascript:void(0)">踢Ta</a>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
@@ -0,0 +1,41 @@
<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>URL</th>
<th>Category</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 |sc_filter_streamURL}}</td>
<td>{{client.type}}</td>
<td>
<a ng-click="kickoff(client)" href="javascript:void(0)">Kickoff It</a>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
@@ -0,0 +1,47 @@
<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">
SRS 4.0不再支持HTTP RAW API。参考<a href='https://github.com/ossrs/srs/issues/2653'>#2653</a>。
</div>
<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-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>
</div>
@@ -0,0 +1,47 @@
<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">
HTTP RAW API is removed, please read <a href='https://github.com/ossrs/srs/issues/2653'>#2653</a>。
</div>
<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-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>
</div>
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -0,0 +1,49 @@
<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>URL</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>{{stream |sc_filter_streamURL}}</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>
@@ -0,0 +1,49 @@
<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>URL</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>{{stream |sc_filter_streamURL}}</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>
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -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>
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="82" height="20"><style>a:hover #llink{fill:url(#b);stroke:#ccc}a:hover #rlink{fill:#4183c4}</style><linearGradient id="a" x2="0" y2="100%"><stop offset="0" stop-color="#fcfcfc" stop-opacity="0"/><stop offset="1" stop-opacity=".1"/></linearGradient><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#ccc" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><g stroke="#d5d5d5"><rect stroke="none" fill="#fcfcfc" x="0.5" y="0.5" width="54" height="19" rx="2"/><rect x="60.5" y="0.5" width="21" height="19" rx="2" fill="#fafafa"/><rect x="60" y="7.5" width="0.5" height="5" stroke="#fafafa"/><path d="M60.5 6.5 l-3 3v1 l3 3" stroke="d5d5d5" fill="#fafafa"/></g><image x="5" y="3" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjMTgxNzE3IiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+R2l0SHViPC90aXRsZT48cGF0aCBkPSJNMTIgLjI5N2MtNi42MyAwLTEyIDUuMzczLTEyIDEyIDAgNS4zMDMgMy40MzggOS44IDguMjA1IDExLjM4NS42LjExMy44Mi0uMjU4LjgyLS41NzcgMC0uMjg1LS4wMS0xLjA0LS4wMTUtMi4wNC0zLjMzOC43MjQtNC4wNDItMS42MS00LjA0Mi0xLjYxQzQuNDIyIDE4LjA3IDMuNjMzIDE3LjcgMy42MzMgMTcuN2MtMS4wODctLjc0NC4wODQtLjcyOS4wODQtLjcyOSAxLjIwNS4wODQgMS44MzggMS4yMzYgMS44MzggMS4yMzYgMS4wNyAxLjgzNSAyLjgwOSAxLjMwNSAzLjQ5NS45OTguMTA4LS43NzYuNDE3LTEuMzA1Ljc2LTEuNjA1LTIuNjY1LS4zLTUuNDY2LTEuMzMyLTUuNDY2LTUuOTMgMC0xLjMxLjQ2NS0yLjM4IDEuMjM1LTMuMjItLjEzNS0uMzAzLS41NC0xLjUyMy4xMDUtMy4xNzYgMCAwIDEuMDA1LS4zMjIgMy4zIDEuMjMuOTYtLjI2NyAxLjk4LS4zOTkgMy0uNDA1IDEuMDIuMDA2IDIuMDQuMTM4IDMgLjQwNSAyLjI4LTEuNTUyIDMuMjg1LTEuMjMgMy4yODUtMS4yMy42NDUgMS42NTMuMjQgMi44NzMuMTIgMy4xNzYuNzY1Ljg0IDEuMjMgMS45MSAxLjIzIDMuMjIgMCA0LjYxLTIuODA1IDUuNjI1LTUuNDc1IDUuOTIuNDIuMzYuODEgMS4wOTYuODEgMi4yMiAwIDEuNjA2LS4wMTUgMi44OTYtLjAxNSAzLjI4NiAwIC4zMTUuMjEuNjkuODI1LjU3QzIwLjU2NSAyMi4wOTIgMjQgMTcuNTkyIDI0IDEyLjI5N2MwLTYuNjI3LTUuMzczLTEyLTEyLTEyIi8+PC9zdmc+"/><g aria-hidden="false" fill="#333" text-anchor="middle" font-family="Helvetica Neue,Helvetica,Arial,sans-serif" text-rendering="geometricPrecision" font-weight="700" font-size="110px" line-height="14px"><a target="_blank" xlink:href="https://github.com/ossrs/signaling"><text aria-hidden="true" x="355" y="150" fill="#fff" transform="scale(.1)" textLength="270">Stars</text><text x="355" y="140" transform="scale(.1)" textLength="270">Stars</text><rect id="llink" stroke="#d5d5d5" fill="url(#a)" x=".5" y=".5" width="54" height="19" rx="2"/></a><a target="_blank" xlink:href="https://github.com/ossrs/signaling/stargazers"><rect width="22" x="60" height="20" fill="rgba(0,0,0,0)"/><text aria-hidden="true" x="705" y="150" fill="#fff" transform="scale(.1)" textLength="130">27</text><text id="rlink" x="705" y="140" transform="scale(.1)" textLength="130">27</text></a></g></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 783 B

@@ -0,0 +1,34 @@
<html>
<head>
<title>SRS</title>
<meta charset="utf-8">
</head>
<body>
<h3><a href="https://github.com/ossrs/signaling">Signaling</a> works!</h3>
<p>
Run demo for <a class="srs_demo" href="one2one.html?autostart=true">WebRTC: One to One over SFU(SRS)</a><br/>
点击进入<a class="srs_demo" href="one2one.html?autostart=true">SRS一对一通话演示</a>
</p>
<p>
Run demo for <a class="srs_demo" href="room.html?autostart=true">WebRTC: Video Room over SFU(SRS)</a><br/>
点击进入<a class="srs_demo" href="room.html?autostart=true">SRS多人通话演示</a>
</p>
<script>
let roomName = Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).slice(0, 7);
let elems = document.getElementsByClassName('srs_demo');
for (var i = 0; i < elems.length; i++) {
let elem = elems.item(i);
// Use random room.
let href = elem.getAttribute('href') + '&room=' + roomName;
// For run demos on SRS http server.
if (window.location.port === '8080') {
href += '&wsp=1989';
}
elem.setAttribute('href', href);
}
</script>
</body>
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
@@ -0,0 +1,535 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2013-2021 Winlin
*
* 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.
*/
'use strict';
// Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
// Async-awat-prmise based SRS RTC Publisher.
function SrsRtcPublisherAsync() {
var self = {};
// https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
self.constraints = {
audio: true,
video: {
width: {ideal: 320, max: 576}
}
};
// @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(self.constraints);
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
stream.getTracks().forEach(function (track) {
self.pc.addTrack(track);
// Notify about local track when stream is ok.
self.ontrack && self.ontrack({track: 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, tid: conf.tid, 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/';
return session;
};
// Close the publisher.
self.close = function () {
self.pc && self.pc.close();
self.pc = null;
};
// The callback when got local stream.
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
self.ontrack = function (event) {
// Add track to stream of SDK.
self.stream.addTrack(event.track);
};
// 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,
tid: Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).slice(0, 7)
};
},
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.substring(1, a.pathname.lastIndexOf("/"));
var stream = a.pathname.slice(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.slice(app.indexOf("?"));
app = app.slice(0, app.indexOf("?"));
if (params.indexOf("vhost=") > 0) {
vhost = params.slice(params.indexOf("vhost=") + "vhost=".length);
if (vhost.indexOf("&") > 0) {
vhost = vhost.slice(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.slice(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);
// To keep api consistent between player and publisher.
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
// @see https://webrtc.org/getting-started/media-devices
self.stream = new MediaStream();
return self;
}
// Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
// 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, tid: conf.tid, 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/';
return session;
};
// Close the player.
self.close = function() {
self.pc && self.pc.close();
self.pc = null;
};
// The callback when got remote track.
// Note that the onaddstream is deprecated, @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/onaddstream
self.ontrack = function (event) {
// https://webrtc.org/getting-started/remote-streams
self.stream.addTrack(event.track);
};
// 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,
tid: Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).slice(0, 7)
};
},
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.substring(1, a.pathname.lastIndexOf("/"));
var stream = a.pathname.slice(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.slice(app.indexOf("?"));
app = app.slice(0, app.indexOf("?"));
if (params.indexOf("vhost=") > 0) {
vhost = params.slice(params.indexOf("vhost=") + "vhost=".length);
if (vhost.indexOf("&") > 0) {
vhost = vhost.slice(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.slice(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);
// Create a stream to add track to the stream, @see https://webrtc.org/getting-started/remote-streams
self.stream = new MediaStream();
// https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/ontrack
self.pc.ontrack = function(event) {
if (self.ontrack) {
self.ontrack(event);
}
};
return self;
}
// Format the codec of RTCRtpSender, kind(audio/video) is optional filter.
// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#getting_the_supported_codecs
function SrsRtcFormatSenders(senders, kind) {
var codecs = [];
senders.forEach(function (sender) {
var params = sender.getParameters();
params && params.codecs && params.codecs.forEach(function(c) {
if (kind && sender.track.kind !== kind) {
return;
}
if (c.mimeType.indexOf('/red') > 0 || c.mimeType.indexOf('/rtx') > 0 || c.mimeType.indexOf('/fec') > 0) {
return;
}
var s = '';
s += c.mimeType.replace('audio/', '').replace('video/', '');
s += ', ' + c.clockRate + 'HZ';
if (sender.track.kind === "audio") {
s += ', channels: ' + c.channels;
}
s += ', pt: ' + c.payloadType;
codecs.push(s);
});
});
return codecs.join(", ");
}
@@ -0,0 +1,148 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2013-2021 Winlin
*
* 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.
*/
'use strict';
// Async-await-promise based SRS RTC Signaling.
function SrsRtcSignalingAsync() {
var self = {};
// The schema is ws or wss, host is ip or ip:port, display is nickname
// of user to join the room.
self.connect = async function (schema, host, room, display) {
var url = schema + '://' + host + '/sig/v1/rtc';
self.ws = new WebSocket(url + '?room=' + room + '&display=' + display);
self.ws.onmessage = function(event) {
var r = JSON.parse(event.data);
var promise = self._internals.msgs[r.tid];
if (promise) {
promise.resolve(r.msg);
delete self._internals.msgs[r.tid];
} else {
self.onmessage(r.msg);
}
};
return new Promise(function (resolve, reject) {
self.ws.onopen = function (event) {
resolve(event);
};
self.ws.onerror = function (event) {
reject(event);
};
});
};
// The message is a json object.
self.send = async function (message) {
return new Promise(function (resolve, reject) {
var r = {tid: Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).slice(0, 7), msg: message};
self._internals.msgs[r.tid] = {resolve: resolve, reject: reject};
self.ws.send(JSON.stringify(r));
});
};
self.close = function () {
self.ws && self.ws.close();
self.ws = null;
for (const tid in self._internals.msgs) {
var promise = self._internals.msgs[tid];
promise.reject('close');
}
};
// The callback when got messages from signaling server.
self.onmessage = function (msg) {
};
self._internals = {
// Key is tid, value is object {resolve, reject, response}.
msgs: {}
};
return self;
}
// Parse params in query string.
function SrsRtcSignalingParse(location) {
let query = location.href.split('?')[1];
query = query? '?' + query : null;
let wsSchema = location.href.split('wss=')[1];
wsSchema = wsSchema? wsSchema.split('&')[0] : (location.protocol === 'http:'? 'ws' : 'wss');
let wsHost = location.href.split('wsh=')[1];
wsHost = wsHost? wsHost.split('&')[0] : location.hostname;
let wsPort = location.href.split('wsp=')[1];
wsPort = wsPort? wsPort.split('&')[0] : location.host.split(':')[1];
let host = location.href.split('host=')[1];
host = host? host.split('&')[0] : location.hostname;
let room = location.href.split('room=')[1];
room = room? room.split('&')[0] : null;
let display = location.href.split('display=')[1];
display = display? display.split('&')[0] : Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).toString(16).slice(0, 7);
let autostart = location.href.split('autostart=')[1];
autostart = autostart && autostart.split('&')[0] === 'true';
// Remove data in query.
let rawQuery = query;
if (query) {
query = query.replace('wss=' + wsSchema, '');
query = query.replace('wsh=' + wsHost, '');
query = query.replace('wsp=' + wsPort, '');
query = query.replace('host=' + host, '');
if (room) {
query = query.replace('room=' + room, '');
}
query = query.replace('display=' + display, '');
query = query.replace('autostart=' + autostart, '');
while (query.indexOf('&&') >= 0) {
query = query.replace('&&', '&');
}
query = query.replace('?&', '?');
if (query.lastIndexOf('?') === query.length - 1) {
query = query.slice(0, query.length - 1);
}
if (query.lastIndexOf('&') === query.length - 1) {
query = query.slice(0, query.length - 1);
}
}
// Regenerate the host of websocket.
wsHost = wsPort? wsHost.split(':')[0] + ':' + wsPort : wsHost;
return {
query: query, rawQuery: rawQuery, wsSchema: wsSchema, wsHost: wsHost, host: host,
room: room, display: display, autostart: autostart,
};
}
@@ -0,0 +1,306 @@
<!DOCTYPE html>
<html>
<head>
<title>SRS</title>
<meta charset="utf-8">
<style>
body{
padding-top: 30px;
}
</style>
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
<script type="text/javascript" src="js/jquery-1.12.2.min.js"></script>
<script type="text/javascript" src="js/adapter-7.4.0.min.js"></script>
<script type="text/javascript" src="js/srs.sdk.js"></script>
<script type="text/javascript" src="js/srs.sig.js"></script>
</head>
<body>
<img src='https://ossrs.net/gif/v1/sls.gif?site=ossrs.net&path=/player/one2one'/>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="https://github.com/ossrs/srs">SRS</a>
<div class="nav-collapse collapse">
<ul class="nav srs_nav">
<li class="active"><a href="one2one.html">一对一通话</a></li>
<li><a href="room.html">多人通话</a></li>
<li class="srs_ignore">
<a href="https://github.com/ossrs/signaling">
<img alt="GitHub Repo stars" src="img/shields-io-signaling.svg">
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="container">
<div class="form-inline">
SRS:
<input type="text" id="txt_host" class="input-medium" value="">
Room:
<input type="text" id="txt_room" class="input-small" value="live">
Display:
<input type="text" id="txt_display" class="input-small" value="">
<button class="btn btn-primary" id="btn_start">开始通话</button>
</div>
<div class="row">
<div class="span4 hide" id="publisher">
<label></label>
<video id="rtc_media_publisher" width="310" autoplay muted controls></video>
<label></label>
<span id='self'></span>
</div>
<div class="span6 hide" id="player">
<label></label>
<video id="rtc_media_player" width="310" autoplay muted controls></video>
<label></label>
<span id='peer'></span>
<a href="javascript:control_refresh_peer()">Refresh</a>
<input type="text" id="txt_alert" class="input-medium" value="">
<a href="javascript:control_alert_peer()">Alert</a>
</div>
</div>
<label></label>
<div class="accordion hide srs_merge">
<div class="accordion-group">
<div class="accordion-heading">
<a href="javascript:void(0)" class="accordion-toggle">FFmpeg合流转直播</a>
</div>
<div class="accordion-body collapse in">
<div class="accordion-inner" style="overflow:auto">
ffmpeg -f flv -i rtmp://<span class="ff_host"></span>/<span class="ff_app"></span>/<span class="ff_first"></span> -f flv -i rtmp://<span class="ff_host"></span>/<span class="ff_app"></span>/<span class="ff_second"></span> \ <br/>
&nbsp;&nbsp;&nbsp;&nbsp; -filter_complex "[1:v]scale=w=96:h=72[ckout];[0:v][ckout]overlay=x=W-w-10:y=H-h-10[out]" -map "[out]" \ <br/>
&nbsp;&nbsp;&nbsp;&nbsp; -c:v libx264 -profile:v high -preset medium \ <br/>
&nbsp;&nbsp;&nbsp;&nbsp; -filter_complex amix -c:a aac \ <br/>
&nbsp;&nbsp;&nbsp;&nbsp; -f flv -y
<span id="ff_output">
rtmp://<span class="ff_host"></span>/<span class="ff_app"></span>/merge
</span>
<span id="ff_wxvideo"></span>
&& <br/>
echo "ok"
</div>
<div class="accordion-inner">
<a href="#" id="ff_preview" target="_blank" class="accordion-toggle">
预览:rtmp://<span class="ff_host"></span>/<span class="ff_app"></span>/merge
</a>
</div>
</div>
</div>
</div>
<label></label>
<div class="accordion hide srs_merge">
<div class="accordion-group">
<div class="accordion-heading">
<a href="javascript:void(0)" class="accordion-toggle">
视频号推流信息
</a>
</div>
<div class="accordion-body collapse in">
<div class="accordion-inner">
推流地址 <input type="text" id="txt_wx_video_tcurl" class="input-xxlarge">
</div>
<div class="accordion-inner">
推流密钥 <input type="text" id="txt_wx_video_stream" class="input-xxlarge">
</div>
<div class="accordion-inner">
<button class="btn btn-primary" id="btn_apply">应用</button>
</div>
</div>
</div>
</div>
</div>
<footer class="footer">
<div class="container">
<p>&copy; SRS 2020</p>
</div>
</footer>
<script type="text/javascript">
var sig = null;
var publisher = null;
var player = null;
var control_refresh_peer = null;
var control_alert_peer = null;
$(function(){
console.log('?wss=x to specify the websocket schema, ws or wss');
console.log('?wsh=x to specify the websocket server ip');
console.log('?wsp=x to specify the websocket server port');
console.log('?host=x to specify the SRS server');
console.log('?room=x to specify the room to join');
console.log('?display=x to specify your nick name');
var startDemo = async function () {
var host = $('#txt_host').val();
var room = $('#txt_room').val();
var display = $('#txt_display').val();
// Connect to signaling first.
if (sig) {
sig.close();
}
sig = new SrsRtcSignalingAsync();
sig.onmessage = function (msg) {
console.log('Notify: ', msg);
if (msg.event === 'leave') {
$('#player').hide();
}
if (msg.event === 'publish') {
if (msg.peer && msg.peer.publishing && msg.peer.display !== display) {
startPlay(host, room, msg.peer.display);
}
}
if (msg.event === 'control') {
if (msg.param === 'refresh') {
setTimeout(function () {
window.location.reload();
}, 500);
} else if (msg.param === 'alert') {
alert('From ' + msg.peer.display + ': ' + msg.data);
}
}
if (msg.participants.length >= 2) {
$('.srs_merge').show();
} else {
$('.srs_merge').hide();
}
};
await sig.connect(conf.wsSchema, conf.wsHost, room, display);
control_refresh_peer = async function () {
let r1 = await sig.send({action:'control', room:room, display:display, call:'refresh'});
console.log('Signaling: control peer to refresh ok', r1);
};
control_alert_peer = async function () {
let r1 = await sig.send({action:'control', room:room, display:display, call:'alert', data:$('#txt_alert').val()});
console.log('Signaling: control peer to alert ok', r1);
};
let r0 = await sig.send({action:'join', room:room, display:display});
console.log('Signaling: join ok', r0);
// For one to one demo, alert and ignore when room is full.
if (r0.participants.length > 2) {
alert('Room is full, already ' + (r0.participants.length - 1) + ' participants');
sig.close();
return;
}
// Start publish media if signaling is ok.
await startPublish(host, room, display);
let r1 = await sig.send({action:'publish', room:room, display:display});
console.log('Signaling: publish ok', r1);
// Play the stream already in room.
r0.participants.forEach(function(participant) {
if (participant.display === display || !participant.publishing) return;
startPlay(host, room, participant.display);
});
if (r0.participants.length >= 2) {
$('.srs_merge').show();
}
};
var startPublish = function (host, room, display) {
$(".ff_first").each(function(i,e) {
$(e).text(display);
});
var url = 'webrtc://' + host + '/' + room + '/' + display + conf.query;
$('#rtc_media_publisher').show();
$('#publisher').show();
if (publisher) {
publisher.close();
}
publisher = new SrsRtcPublisherAsync();
$('#rtc_media_publisher').prop('srcObject', publisher.stream);
return publisher.publish(url).then(function(session){
$('#self').text('Self: ' + url);
}).catch(function (reason) {
publisher.close();
$('#rtc_media_publisher').hide();
console.error(reason);
});
};
var startPlay = function (host, room, display) {
$(".ff_second").each(function(i,e) {
$(e).text(display);
});
var url = 'webrtc://' + host + '/' + room + '/' + display + conf.query;
$('#rtc_media_player').show();
$('#player').show();
if (player) {
player.close();
}
player = new SrsRtcPlayerAsync();
$('#rtc_media_player').prop('srcObject', player.stream);
player.play(url).then(function(session){
$('#peer').text('Peer: ' + display);
$('#rtc_media_player').prop('muted', false);
}).catch(function (reason) {
player.close();
$('#rtc_media_player').hide();
console.error(reason);
});
};
// Pass-by to SRS url.
let conf = SrsRtcSignalingParse(window.location);
$('#txt_host').val(conf.host);
conf.room && $('#txt_room').val(conf.room);
$('#txt_display').val(conf.display);
$(".ff_host").each(function(i,e) {
$(e).text(conf.host);
});
$(".ff_app").each(function(i,e) {
$(e).text($('#txt_room').val());
});
$('#ff_preview').attr('href', 'http://ossrs.net/players/srs_player.html?app=' + $('#txt_room').val() + '&stream=merge.flv&server=' + conf.host + '&vhost=' + conf.host + '&autostart=true');
// Update href for all navs.
$('ul.srs_nav').children('li').not('.srs_ignore').children('a').each(function (i, e) {
$(e).attr('href', $(e).attr('href') + conf.rawQuery);
});
$('#btn_apply').click(function () {
if ($('#txt_wx_video_tcurl').val() !== '' && $('#txt_wx_video_stream').val() !== '') {
$('#ff_wxvideo').text('"' + $('#txt_wx_video_tcurl').val() + $('#txt_wx_video_stream').val() + '"').show();
$('#ff_output').hide();
$('#ff_preview').parent().hide();
} else {
$('#ff_wxvideo').hide();
$('#ff_output').show();
$('#ff_preview').parent().show();
}
});
$("#btn_start").click(startDemo);
// Never play util windows loaded @see https://github.com/ossrs/srs/issues/2732
if (conf.autostart) {
window.addEventListener("load", function(){ startDemo(); });
}
});
</script>
</body>
</html>
@@ -0,0 +1,207 @@
<!DOCTYPE html>
<html>
<head>
<title>SRS</title>
<meta charset="utf-8">
<style>
body{
padding-top: 30px;
}
</style>
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
<script type="text/javascript" src="js/jquery-1.12.2.min.js"></script>
<script type="text/javascript" src="js/adapter-7.4.0.min.js"></script>
<script type="text/javascript" src="js/srs.sdk.js"></script>
<script type="text/javascript" src="js/srs.sig.js"></script>
</head>
<body>
<img src='https://ossrs.net/gif/v1/sls.gif?site=ossrs.net&path=/player/room'/>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="https://github.com/ossrs/srs">SRS</a>
<div class="nav-collapse collapse">
<ul class="nav srs_nav">
<li><a href="one2one.html">一对一通话</a></li>
<li class="active"><a href="room.html">多人通话</a></li>
<li class="srs_ignore">
<a href="https://github.com/ossrs/signaling">
<img alt="GitHub Repo stars" src="img/shields-io-signaling.svg">
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="container">
<div class="form-inline">
SRS:
<input type="text" id="txt_host" class="input-medium" value="">
Room:
<input type="text" id="txt_room" class="input-small" value="live">
Display:
<input type="text" id="txt_display" class="input-small" value="">
<button class="btn btn-primary" id="btn_start">加入房间</button>
</div>
<div class="row srs_players">
<div class="span4 hide" id="publisher">
<label></label>
<video id="rtc_media_publisher" width="310" autoplay muted controls></video>
<label></label>
<span id='self'></span>
</div>
<div class="span4 hide" id="player">
<label></label>
<video id="rtc_media_player" width="310" autoplay muted controls></video>
<label></label>
<span id='peer'></span>
</div>
</div>
</div>
<script type="text/javascript">
var sig = null;
var publisher = null;
var players = {}; // Key is display, value is a player object.
$(function(){
console.log('?wss=x to specify the websocket schema, ws or wss');
console.log('?wsh=x to specify the websocket server ip');
console.log('?wsp=x to specify the websocket server port');
console.log('?host=x to specify the SRS server');
console.log('?room=x to specify the room to join');
console.log('?display=x to specify your nick name');
var startDemo = async function () {
var host = $('#txt_host').val();
var room = $('#txt_room').val();
var display = $('#txt_display').val();
// Connect to signaling first.
if (sig) {
sig.close();
}
sig = new SrsRtcSignalingAsync();
sig.onmessage = function (msg) {
console.log('Notify: ', msg);
// Subscribe if new user start to publish.
if (msg.event === 'publish') {
if (msg.peer && msg.peer.publishing && msg.peer.display !== display) {
startPlay(host, room, msg.peer.display);
}
}
// Remove dead players.
if (msg.event === 'join' || msg.event === 'leave') {
$.each(players, function(k, obj) {
let stillAlive = false;
msg.participants.forEach(function (participant) {
if (participant.display === k) stillAlive = true;
});
if (!stillAlive) {
obj.player.close();
obj.ui.remove();
}
});
}
};
await sig.connect(conf.wsSchema, conf.wsHost, room, display);
let r0 = await sig.send({action:'join', room:room, display:display});
console.log('Signaling: join ok', r0);
// Start publish media if signaling is ok.
await startPublish(host, room, display);
let r1 = await sig.send({action:'publish', room:room, display:display});
console.log('Signaling: publish ok', r1);
// Play the stream already in room.
r0.participants.forEach(function(participant) {
if (participant.display === display || !participant.publishing) return;
startPlay(host, room, participant.display);
});
};
var startPublish = function (host, room, display) {
$(".ff_first").each(function(i,e) {
$(e).text(display);
});
var url = 'webrtc://' + host + '/' + room + '/' + display + conf.query;
$('#rtc_media_publisher').show();
$('#publisher').show();
if (publisher) {
publisher.close();
}
publisher = new SrsRtcPublisherAsync();
$('#rtc_media_publisher').prop('srcObject', publisher.stream);
return publisher.publish(url).then(function(session){
$('#self').text('Self: ' + url);
}).catch(function (reason) {
publisher.close();
$('#rtc_media_publisher').hide();
console.error(reason);
});
};
var startPlay = function (host, room, display) {
$(".ff_second").each(function(i,e) {
$(e).text(display);
});
// Remove exists.
if (players[display]) {
players[display].ui.remove();
players[display].player.close();
}
// Clone a player from template.
let ui = $('#player').clone().attr('id', 'player-' + display);
let video = ui.children('#rtc_media_player');
console.log(video.length);
let player = new SrsRtcPlayerAsync();
players[display] = {ui:ui, video:video, player:player};
$('.srs_players').append(ui);
// Start play for this user.
var url = 'webrtc://' + host + '/' + room + '/' + display + conf.query;
video.show();
ui.show();
video.prop('srcObject', player.stream);
player.play(url).then(function(session){
ui.children('#peer').text('Peer: ' + url);
video.prop('muted', false);
}).catch(function (reason) {
player.close();
video.hide();
console.error(reason);
});
};
// Pass-by to SRS url.
let conf = SrsRtcSignalingParse(window.location);
$('#txt_host').val(conf.host);
conf.room && $('#txt_room').val(conf.room);
$('#txt_display').val(conf.display);
// Update href for all navs.
$('ul.srs_nav').children('li').not('.srs_ignore').children('a').each(function (i, e) {
$(e).attr('href', $(e).attr('href') + conf.rawQuery);
});
$("#btn_start").click(startDemo);
// Never play util windows loaded @see https://github.com/ossrs/srs/issues/2732
if (conf.autostart) {
window.addEventListener("load", function(){ startDemo(); });
}
});
</script>
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

@@ -0,0 +1,72 @@
<html>
<head>
<title>SRS</title>
<meta charset="utf-8">
<style>
.span6 {
width: 480px;
}
.code {
background-color: rgb(246 248 250);
padding: 8px;
overflow: auto;
font-size: 85%;
line-height: 1.45;
border-radius: 6px;
word-break: normal;
word-wrap: normal;
box-sizing: border-box;
display: block;
white-space: pre;
}
</style>
</head>
<body>
<div>
<h3><a href="https://github.com/ossrs/srs">SRS</a> works!</h3>
<p>
Click <a id="enConsole" href="#">here</a> to enter SRS console.<br/>
点击进入<a id="cnConsole" href="#">SRS控制台</a>
</p>
<p>
Publish stream by <a href="https://ffmpeg.org/download.html">FFmpeg</a> or <a href="https://obsproject.com/download">OBS</a>:<br/>
请使用工具<a href="https://ffmpeg.org/download.html">FFmpeg</a>或者<a href="https://obsproject.com/download">OBS</a>推流到下面地址:
<pre id="url" class="code span6"></pre>
</p>
<p>
Click <a id="enPlayer" href="#">here</a> to start SRS player.<br/>
点击进入<a id="cnPlayer" href="#">SRS播放器</a>
</p>
<p><a href="https://github.com/ossrs/srs">SRS Team &copy; 2022</a></p>
</div>
<script type="text/javascript">
// Build RTMP url.
if (true) {
const rtmpUrl = `rtmp://${window.location.hostname}/live/livestream`;
document.getElementById('url').innerText = rtmpUrl;
}
// Build console url.
if (true) {
// The prefix for default website.
const prefix = `${window.location.protocol}//${window.location.host}`;
// If not 8080, user should proxy to the default port.
const query = parseInt(window.location.port) === 8080 ? `?port=1985` : '';
const enUrl = `${prefix}/console/en_index.html#/summaries${query}`;
const cnUrl = `${prefix}/console/ng_index.html#/summaries${query}`;
document.getElementById("enConsole").setAttribute('href', enUrl);
document.getElementById("cnConsole").setAttribute('href', cnUrl);
}
// The player url.
if (true) {
const prefix = `players/?schema=${window.location.protocol.replace(':', '')}`;
const httpPort = window.location.port || (window.location.protocol === 'http:' ? 80 : 443);
// If not 8080, user should proxy both stream and API to the default port.
const query = parseInt(window.location.port) === 8080 ? '' : `&port=${httpPort}&api=${httpPort}`;
document.getElementById("enPlayer").setAttribute('href', `${prefix}${query}`);
document.getElementById("cnPlayer").setAttribute('href', `${prefix}${query}`);
}
</script>
</body>
@@ -0,0 +1,3 @@
<video width="100%" autoplay controls autobuffer type="application/vnd.apple.mpegurl"
src="livestream.m3u8">
</video>
@@ -0,0 +1,16 @@
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:7
#EXT-X-TARGETDURATION:15
#EXTINF:11.966, no desc
livestream-7.ts
#EXTINF:11.966, no desc
livestream-8.ts
#EXTINF:11.966, no desc
livestream-9.ts
#EXTINF:11.966, no desc
livestream-10.ts
#EXTINF:11.966, no desc
livestream-11.ts
#EXTINF:11.966, no desc
livestream-12.ts
@@ -0,0 +1 @@
Nginx is ok.
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<title>SRS</title>
<meta charset="utf-8">
<script type="text/javascript" src="players/js/jquery-1.12.2.min.js"></script>
<script type="text/javascript" src="players/js/srs.page.js"></script>
<script type="text/javascript" src="players/js/srs.utility.js"></script>
<script type="text/javascript" src="players/js/winlin.utility.js"></script>
</head>
<body>
<script type="text/javascript">
setTimeout(function(){
window.location.href = "players/index.html" + window.location.search;
}, 500);
</script>
</body>
@@ -0,0 +1,3 @@
<cross-domain-policy>
<allow-access-from domain="*"/>
</cross-domain-policy>
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 783 B

@@ -0,0 +1,41 @@
<!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: 30px;
}
</style>
<script type="text/javascript" src="js/jquery-1.12.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.utility.js"></script>
<script type="text/javascript" src="js/winlin.utility.js"></script>
<script type="text/javascript">
$(function(){
update_nav();
var query = parse_query_string();
var params = [];
for (var key in query.user_query) {
params.push(key + "=" + query[key]);
}
var url = "srs_player.html";
if (params.length) {
url += '?' + params.join('&');
}
setTimeout(function () {
window.location.href = url;
}, 0);
});
</script>
</head>
<body>
</body>
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,486 @@
/*
json2.js
2013-05-26
Public Domain.
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
See http://www.JSON.org/js.html
This code should be minified before deployment.
See http://javascript.crockford.com/jsmin.html
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
NOT CONTROL.
This file creates a global JSON object containing two methods: stringify
and parse.
JSON.stringify(value, replacer, space)
value any JavaScript value, usually an object or array.
replacer an optional parameter that determines how object
values are stringified for objects. It can be a
function or an array of strings.
space an optional parameter that specifies the indentation
of nested structures. If it is omitted, the text will
be packed without extra whitespace. If it is a number,
it will specify the number of spaces to indent at each
level. If it is a string (such as '\t' or '&nbsp;'),
it contains the characters used to indent at each level.
This method produces a JSON text from a JavaScript value.
When an object value is found, if the object contains a toJSON
method, its toJSON method will be called and the result will be
stringified. A toJSON method does not serialize: it returns the
value represented by the name/value pair that should be serialized,
or undefined if nothing should be serialized. The toJSON method
will be passed the key associated with the value, and this will be
bound to the value
For example, this would serialize Dates as ISO strings.
Date.prototype.toJSON = function (key) {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
return this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z';
};
You can provide an optional replacer method. It will be passed the
key and value of each member, with this bound to the containing
object. The value that is returned from your method will be
serialized. If your method returns undefined, then the member will
be excluded from the serialization.
If the replacer parameter is an array of strings, then it will be
used to select the members to be serialized. It filters the results
such that only members with keys listed in the replacer array are
stringified.
Values that do not have JSON representations, such as undefined or
functions, will not be serialized. Such values in objects will be
dropped; in arrays they will be replaced with null. You can use
a replacer function to replace those with JSON values.
JSON.stringify(undefined) returns undefined.
The optional space parameter produces a stringification of the
value that is filled with line breaks and indentation to make it
easier to read.
If the space parameter is a non-empty string, then that string will
be used for indentation. If the space parameter is a number, then
the indentation will be that many spaces.
Example:
text = JSON.stringify(['e', {pluribus: 'unum'}]);
// text is '["e",{"pluribus":"unum"}]'
text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
// text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
text = JSON.stringify([new Date()], function (key, value) {
return this[key] instanceof Date ?
'Date(' + this[key] + ')' : value;
});
// text is '["Date(---current time---)"]'
JSON.parse(text, reviver)
This method parses a JSON text to produce an object or array.
It can throw a SyntaxError exception.
The optional reviver parameter is a function that can filter and
transform the results. It receives each of the keys and values,
and its return value is used instead of the original value.
If it returns what it received, then the structure is not modified.
If it returns undefined then the member is deleted.
Example:
// Parse the text. Values that look like ISO date strings will
// be converted to Date objects.
myData = JSON.parse(text, function (key, value) {
var a;
if (typeof value === 'string') {
a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
if (a) {
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+a[5], +a[6]));
}
}
return value;
});
myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
var d;
if (typeof value === 'string' &&
value.slice(0, 5) === 'Date(' &&
value.slice(-1) === ')') {
d = new Date(value.slice(5, -1));
if (d) {
return d;
}
}
return value;
});
This is a reference implementation. You are free to copy, modify, or
redistribute.
*/
/*jslint evil: true, regexp: true */
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
lastIndex, length, parse, prototype, push, replace, slice, stringify,
test, toJSON, toString, valueOf
*/
// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.
if (typeof JSON !== 'object') {
JSON = {};
}
(function () {
'use strict';
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
if (typeof Date.prototype.toJSON !== 'function') {
Date.prototype.toJSON = function () {
return isFinite(this.valueOf())
? this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z'
: null;
};
String.prototype.toJSON =
Number.prototype.toJSON =
Boolean.prototype.toJSON = function () {
return this.valueOf();
};
}
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
gap,
indent,
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
},
rep;
function quote(string) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.
escapable.lastIndex = 0;
return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
var c = meta[a];
return typeof c === 'string'
? c
: '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"' : '"' + string + '"';
}
function str(key, holder) {
// Produce a string from holder[key].
var i, // The loop counter.
k, // The member key.
v, // The member value.
length,
mind = gap,
partial,
value = holder[key];
// If the value has a toJSON method, call it to obtain a replacement value.
if (value && typeof value === 'object' &&
typeof value.toJSON === 'function') {
value = value.toJSON(key);
}
// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.
if (typeof rep === 'function') {
value = rep.call(holder, key, value);
}
// What happens next depends on the value's type.
switch (typeof value) {
case 'string':
return quote(value);
case 'number':
// JSON numbers must be finite. Encode non-finite numbers as null.
return isFinite(value) ? String(value) : 'null';
case 'boolean':
case 'null':
// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.
return String(value);
// If the type is 'object', we might be dealing with an object or an array or
// null.
case 'object':
// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.
if (!value) {
return 'null';
}
// Make an array to hold the partial results of stringifying this object value.
gap += indent;
partial = [];
// Is the value an array?
if (Object.prototype.toString.apply(value) === '[object Array]') {
// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.
length = value.length;
for (i = 0; i < length; i += 1) {
partial[i] = str(i, value) || 'null';
}
// Join all of the elements together, separated with commas, and wrap them in
// brackets.
v = partial.length === 0
? '[]'
: gap
? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
: '[' + partial.join(',') + ']';
gap = mind;
return v;
}
// If the replacer is an array, use it to select the members to be stringified.
if (rep && typeof rep === 'object') {
length = rep.length;
for (i = 0; i < length; i += 1) {
if (typeof rep[i] === 'string') {
k = rep[i];
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
} else {
// Otherwise, iterate through all of the keys in the object.
for (k in value) {
if (Object.prototype.hasOwnProperty.call(value, k)) {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
}
// Join all of the member texts together, separated with commas,
// and wrap them in braces.
v = partial.length === 0
? '{}'
: gap
? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
: '{' + partial.join(',') + '}';
gap = mind;
return v;
}
}
// If the JSON object does not yet have a stringify method, give it one.
if (typeof JSON.stringify !== 'function') {
JSON.stringify = function (value, replacer, space) {
// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.
var i;
gap = '';
indent = '';
// If the space parameter is a number, make an indent string containing that
// many spaces.
if (typeof space === 'number') {
for (i = 0; i < space; i += 1) {
indent += ' ';
}
// If the space parameter is a string, it will be used as the indent string.
} else if (typeof space === 'string') {
indent = space;
}
// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.
rep = replacer;
if (replacer && typeof replacer !== 'function' &&
(typeof replacer !== 'object' ||
typeof replacer.length !== 'number')) {
throw new Error('JSON.stringify');
}
// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.
return str('', {'': value});
};
}
// If the JSON object does not yet have a parse method, give it one.
if (typeof JSON.parse !== 'function') {
JSON.parse = function (text, reviver) {
// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.
var j;
function walk(holder, key) {
// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.
var k, v, value = holder[key];
if (value && typeof value === 'object') {
for (k in value) {
if (Object.prototype.hasOwnProperty.call(value, k)) {
v = walk(value, k);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
}
return reviver.call(holder, key, value);
}
// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.
text = String(text);
cx.lastIndex = 0;
if (cx.test(text)) {
text = text.replace(cx, function (a) {
return '\\u' +
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
});
}
// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.
// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
if (/^[\],:{}\s]*$/
.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
.replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.
j = eval('(' + text + ')');
// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.
return typeof reviver === 'function'
? walk({'': j}, '')
: j;
}
// If the text is not JSON parseable, then a SyntaxError is thrown.
throw new SyntaxError('JSON.parse');
};
}
}());
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,37 @@
/**
* log specified, there must be a log element as:
<!-- for the log -->
<div class="alert alert-info fade in" id="txt_log">
<button type="button" class="close" data-dismiss="alert">×</button>
<strong><span id="txt_log_title">标题:</span></strong>
<span id="txt_log_msg">日志内容</span>
</div>
*/
var srs_log_disabled = false;
function info(desc) {
if (srs_log_disabled) {
return;
}
$("#txt_log").addClass("alert-info").removeClass("alert-error").removeClass("alert-warn");
$("#txt_log_title").text("Info:");
$("#txt_log_msg").html(desc);
}
function warn(code, desc) {
if (srs_log_disabled) {
return;
}
$("#txt_log").removeClass("alert-info").removeClass("alert-error").addClass("alert-warn");
$("#txt_log_title").text("Warn:");
$("#txt_log_msg").html("code: " + code + ", " + desc);
}
function error(code, desc) {
if (srs_log_disabled) {
return;
}
$("#txt_log").removeClass("alert-info").addClass("alert-error").removeClass("alert-warn");
$("#txt_log_title").text("Error:");
$("#txt_log_msg").html("code: " + code + ", " + desc);
}
@@ -0,0 +1,183 @@
// to query the swf anti cache.
function srs_get_version_code() { return "1.33"; }
/**
* player specified size.
*/
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; }
/**
* update the navigator, add same query string.
*/
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_whip").attr("href", "whip.html" + window.location.search);
$("#nav_whep").attr("href", "whep.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_vlc").attr("href", "vlc.html" + window.location.search);
}
// Special extra params, such as auth_key.
function user_extra_params(query, params, rtc) {
var queries = params || [];
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' || key === 'api'
|| key === 'path'
) {
continue;
}
if (query[key]) {
queries.push(key + '=' + query[key]);
}
}
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 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_flv_url() {
var query = parse_query_string();
var schema = (!query.schema)? "http":query.schema;
var server = (!query.server)? window.location.hostname:query.server;
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.flv":query.stream;
var queries = [];
if (server !== vhost && vhost !== "__defaultVhost__") {
queries.push("vhost=" + vhost);
}
queries = user_extra_params(query, queries);
var uri = schema + "://" + server;
if (!is_default_port(schema, port)) {
uri += ":" + port;
}
uri += "/" + app + "/" + stream + "?" + queries.join('&');
while (uri.indexOf("?") === uri.length - 1) {
uri = uri.slice(0, uri.length - 1);
}
return uri;
}
function build_default_rtc_url(query) {
// The format for query string to overwrite configs of server.
console.log('?eip=x.x.x.x to overwrite candidate. 覆盖服务器candidate(外网IP)配置');
console.log('?api=x to overwrite WebRTC API(1985).');
console.log('?schema=http|https to overwrite WebRTC API protocol.');
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)? "livestream":query.stream;
var api = query.api? ':'+query.api : '';
var queries = [];
if (server !== vhost && vhost !== "__defaultVhost__") {
queries.push("vhost=" + vhost);
}
if (query.schema && window.location.protocol !== query.schema + ':') {
queries.push('schema=' + query.schema);
}
queries = user_extra_params(query, queries, true);
var uri = "webrtc://" + server + api + "/" + app + "/" + stream + "?" + queries.join('&');
while (uri.lastIndexOf("?") === uri.length - 1) {
uri = uri.slice(0, uri.length - 1);
}
return uri;
};
function build_default_whip_whep_url(query, apiPath) {
// The format for query string to overwrite configs of server.
console.log('?eip=x.x.x.x to overwrite candidate. 覆盖服务器candidate(外网IP)配置');
console.log('?api=x to overwrite WebRTC API(1985).');
console.log('?schema=http|https to overwrite WebRTC API protocol.');
console.log(`?path=xxx to overwrite default ${apiPath}`);
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)? "livestream":query.stream;
var api = ':' + (query.api || (window.location.protocol === 'http:' ? '1985' : '1990'));
const realApiPath = query.path || apiPath;
var queries = [];
if (server !== vhost && vhost !== "__defaultVhost__") {
queries.push("vhost=" + vhost);
}
if (query.schema && window.location.protocol !== query.schema + ':') {
queries.push('schema=' + query.schema);
}
queries = user_extra_params(query, queries, true);
var uri = window.location.protocol + "//" + server + api + realApiPath + "?app=" + app + "&stream=" + stream + "&" + queries.join('&');
while (uri.lastIndexOf("?") === uri.length - 1) {
uri = uri.slice(0, uri.length - 1);
}
while (uri.lastIndexOf("&") === uri.length - 1) {
uri = uri.slice(0, uri.length - 1);
}
return uri;
}
/**
* initialize the page.
* @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_flv(flv_url, modal_player) {
update_nav();
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");
}
}
function srs_init_rtc(id, query) {
update_nav();
$(id).val(build_default_rtc_url(query));
}
function srs_init_whip(id, query) {
update_nav();
$(id).val(build_default_whip_whep_url(query, '/rtc/v1/whip/'));
}
function srs_init_whep(id, query) {
update_nav();
$(id).val(build_default_whip_whep_url(query, '/rtc/v1/whip-play/'));
}
@@ -0,0 +1,382 @@
/**
* the SrsPlayer object.
* @param container the html container id.
* @param width a float value specifies the width of player.
* @param height a float value specifies the height of player.
* @param private_object [optional] an object that used as private object,
* for example, the logic chat object which owner this player.
* Usage:
<script type="text/javascript" src="js/swfobject.js"></script>
<script type="text/javascript" src="js/srs.player.js"></script>
<div id="player"></div>
var p = new SrsPlayer("player", 640, 480);
p.set_srs_player_url("srs_player.swf?v=1.0.0");
p.on_player_ready = function() {
p.set_bt(0.8);
p.set_mbt(1.2);
p.play("rtmp://ossrs.net/live/livestream");
};
p.on_player_metadata = function(metadata) {
console.log(metadata);
console.log(p.dump_log());
};
p.start();
*/
function SrsPlayer(container, width, height, private_object) {
if (!SrsPlayer.__id) {
SrsPlayer.__id = 100;
}
if (!SrsPlayer.__players) {
SrsPlayer.__players = [];
}
SrsPlayer.__players.push(this);
this.private_object = private_object;
this.container = container;
this.width = width;
this.height = height;
this.id = SrsPlayer.__id++;
this.stream_url = null;
this.buffer_time = 0.3; // default to 0.3
this.max_buffer_time = this.buffer_time * 3; // default to 3 x bufferTime.
this.volume = 1.0; // default to 100%
this.callbackObj = null;
this.srs_player_url = "srs_player/release/srs_player.swf?_version="+srs_get_version_code();
// callback set the following values.
this.meatadata = {}; // for on_player_metadata
this.time = 0; // current stream time.
this.buffer_length = 0; // current stream buffer length.
this.kbps = 0; // current stream bitrate(video+audio) in kbps.
this.fps = 0; // current stream video fps.
this.rtime = 0; // flash relative time in ms.
this.__fluency = {
total_empty_count: 0,
total_empty_time: 0,
current_empty_time: 0
};
this.__fluency.on_stream_empty = function(time) {
this.total_empty_count++;
this.current_empty_time = time;
};
this.__fluency.on_stream_full = function(time) {
if (this.current_empty_time > 0) {
this.total_empty_time += time - this.current_empty_time;
this.current_empty_time = 0;
}
};
this.__fluency.calc = function(time) {
var den = this.total_empty_count * 4 + this.total_empty_time * 2 + time;
if (den > 0) {
return time * 100 / den;
}
return 0;
};
}
/**
* user can set some callback, then start the player.
* @param url the default url.
* callbacks:
* on_player_ready():int, when srs player ready, user can play().
* on_player_metadata(metadata:Object):int, when srs player get metadata.
* methods:
* set_bt(t:Number):void, set the buffer time in seconds.
* set_mbt(t:Number):void, set the max buffer time in seconds.
* dump_log():String, get all logs of player.
*/
SrsPlayer.prototype.start = function(url) {
if (url) {
this.stream_url = url;
}
// embed the flash.
var flashvars = {};
flashvars.id = this.id;
flashvars.on_player_ready = "__srs_on_player_ready";
flashvars.on_player_metadata = "__srs_on_player_metadata";
flashvars.on_player_timer = "__srs_on_player_timer";
flashvars.on_player_empty = "__srs_on_player_empty";
flashvars.on_player_full = "__srs_on_player_full";
flashvars.on_player_status = "__srs_on_player_status";
var params = {};
params.wmode = "opaque";
params.allowFullScreen = "true";
params.allowScriptAccess = "always";
var attributes = {};
var self = this;
swfobject.embedSWF(
this.srs_player_url,
this.container,
this.width, this.height,
"11.1.0", "js/AdobeFlashPlayerInstall.swf",
flashvars, params, attributes,
function(callbackObj){
self.callbackObj = callbackObj;
if (!callbackObj.success) {
console.error('Initialize player failed:'); console.error(callbackObj);
}
}
);
return this;
}
/**
* play the stream.
* @param stream_url the url of stream, rtmp or http.
* @param volume the volume, 0 is mute, 1 is 100%, 2 is 200%.
*/
SrsPlayer.prototype.play = function(url, volume) {
this.stop();
SrsPlayer.__players.push(this);
if (url) {
this.stream_url = url;
}
// volume maybe 0, so never use if(volume) to check its value.
if (volume != null && volume != undefined) {
this.volume = volume;
}
this.callbackObj.ref.__play(this.stream_url, this.width, this.height, this.buffer_time, this.max_buffer_time, this.volume);
}
/**
* stop play stream.
*/
SrsPlayer.prototype.stop = function() {
this.callbackObj.ref.__stop();
}
/**
* pause the play.
*/
SrsPlayer.prototype.pause = function() {
this.callbackObj.ref.__pause();
}
/**
* resume the play.
*/
SrsPlayer.prototype.resume = function() {
this.callbackObj.ref.__resume();
}
/**
* get the stream fluency, where 100 is 100%.
*/
SrsPlayer.prototype.fluency = function() {
return this.__fluency.calc(this.rtime);
}
/**
* get the stream empty count.
*/
SrsPlayer.prototype.empty_count = function() {
return this.__fluency.total_empty_count;
}
/**
* get all log data.
*/
SrsPlayer.prototype.dump_log = function() {
return this.callbackObj.ref.__dump_log();
}
/**
* to set the DAR, for example, DAR=16:9 where num=16,den=9.
* @param num, for example, 16.
* use metadata width if 0.
* use user specified width if -1.
* @param den, for example, 9.
* use metadata height if 0.
* use user specified height if -1.
*/
SrsPlayer.prototype.set_dar = function(num, den) {
this.callbackObj.ref.__set_dar(num, den);
}
/**
* set the fullscreen size data.
* @refer the refer fullscreen mode. it can be:
* video: use video orignal size.
* screen: use screen size to rescale video.
* @param percent, the rescale percent, where
* 100 means 100%.
*/
SrsPlayer.prototype.set_fs = function(refer, percent) {
this.callbackObj.ref.__set_fs(refer, percent);
}
/**
* set the stream buffer time in seconds.
* @buffer_time the buffer time in seconds.
*/
SrsPlayer.prototype.set_bt = function(buffer_time) {
if (this.buffer_time == buffer_time) {
return;
}
this.buffer_time = buffer_time;
this.callbackObj.ref.__set_bt(buffer_time);
// reset the max buffer time to 3 x buffer_time.
this.set_mbt(buffer_time * 3);
}
/**
* set the stream max buffer time in seconds.
* @param max_buffer_time the max buffer time in seconds.
* @remark this is the key feature for realtime communication by flash.
*/
SrsPlayer.prototype.set_mbt = function(max_buffer_time) {
// we must atleast set the max buffer time to 0.6s.
max_buffer_time = Math.max(0.6, max_buffer_time);
// max buffer time always greater than buffer time.
max_buffer_time = Math.max(this.buffer_time, max_buffer_time);
if (parseInt(this.max_buffer_time * 10) == parseInt(max_buffer_time * 10)) {
return;
}
this.max_buffer_time = max_buffer_time;
this.callbackObj.ref.__set_mbt(max_buffer_time);
}
/**
* set the srs_player.swf url
* @param url, srs_player.swf's url.
* @param params, object.
*/
SrsPlayer.prototype.set_srs_player_url = function(url, params) {
var query_array = [],
query_string = "",
p;
params = params || {};
params._version = srs_get_version_code();
for (p in params) {
if (params.hasOwnProperty(p)) {
query_array.push(p + "=" + encodeURIComponent(params[p]));
}
}
query_string = query_array.join("&");
this.srs_player_url = url + "?" + query_string;
}
/**
* the callback when player is ready.
*/
SrsPlayer.prototype.on_player_ready = function() {
}
/**
* the callback when player got metadata.
* @param metadata the metadata which player got.
*/
SrsPlayer.prototype.on_player_metadata = function(metadata) {
// ignore.
}
/**
* the callback when player timer event.
* @param time current stream time.
* @param buffer_length current buffer length.
* @param kbps current video plus audio bitrate in kbps.
* @param fps current video fps.
* @param rtime current relative time by flash.util.getTimer().
*/
SrsPlayer.prototype.on_player_timer = function(time, buffer_length, kbps, fps, rtime) {
// ignore.
}
/**
* the callback when player got NetStream.Buffer.Empty
* @param time current relative time by flash.util.getTimer().
*/
SrsPlayer.prototype.on_player_empty = function(time) {
// ignore.
}
/**
* the callback when player got NetStream.Buffer.Full
* @param time current relative time by flash.util.getTimer().
*/
SrsPlayer.prototype.on_player_full = function(time) {
// ignore.
}
/**
* the callback when player status change.
* @param code the status code, "init", "connected", "play", "closed", "rejected", "failed".
* init => connected/rejected/failed
* connected => play/rejected => closed
* @param desc the description for the status.
*/
SrsPlayer.prototype.on_player_status = function(code, desc) {
// ignore.
}
/**
* helpers.
*/
function __srs_find_player(id) {
for (var i = 0; i < SrsPlayer.__players.length; i++) {
var player = SrsPlayer.__players[i];
if (player.id != id) {
continue;
}
return player;
}
throw new Error("player not found. id=" + id);
}
function __srs_on_player_ready(id) {
var player = __srs_find_player(id);
player.on_player_ready();
}
function __srs_on_player_metadata(id, metadata) {
var player = __srs_find_player(id);
// user may override the on_player_metadata,
// so set the data before invoke it.
player.metadata = metadata;
player.on_player_metadata(metadata);
}
function __srs_on_player_timer(id, time, buffer_length, kbps, fps, rtime) {
var player = __srs_find_player(id);
buffer_length = Math.max(0, buffer_length);
buffer_length = Math.min(player.buffer_time, buffer_length);
time = Math.max(0, time);
// user may override the on_player_timer,
// so set the data before invoke it.
player.time = time;
player.buffer_length = buffer_length;
player.kbps = kbps;
player.fps = fps;
player.rtime = rtime;
player.on_player_timer(time, buffer_length, kbps, fps, rtime);
}
function __srs_on_player_empty(id, time) {
var player = __srs_find_player(id);
player.__fluency.on_stream_empty(time);
player.on_player_empty(time);
}
function __srs_on_player_full(id, time) {
var player = __srs_find_player(id);
player.__fluency.on_stream_full(time);
player.on_player_full(time);
}
function __srs_on_player_status(id, code, desc) {
var player = __srs_find_player(id);
player.on_player_status(code, desc);
if (code != "closed") {
return;
}
for (var i = 0; i < SrsPlayer.__players.length; i++) {
var player = SrsPlayer.__players[i];
if (player.id != this.id) {
continue;
}
SrsPlayer.__players.splice(i, 1);
break;
}
}
@@ -0,0 +1,173 @@
/**
* the SrsPublisher object.
* @param container the html container id.
* @param width a float value specifies the width of publisher.
* @param height a float value specifies the height of publisher.
* @param private_object [optional] an object that used as private object,
* for example, the logic chat object which owner this publisher.
*/
function SrsPublisher(container, width, height, private_object) {
if (!SrsPublisher.__id) {
SrsPublisher.__id = 100;
}
if (!SrsPublisher.__publishers) {
SrsPublisher.__publishers = [];
}
SrsPublisher.__publishers.push(this);
this.private_object = private_object;
this.container = container;
this.width = width;
this.height = height;
this.id = SrsPublisher.__id++;
this.callbackObj = null;
// set the values when publish.
this.url = null;
this.vcodec = {};
this.acodec = {};
// callback set the following values.
this.cameras = [];
this.microphones = [];
this.code = 0;
// error code defines.
this.errors = {
"100": "无法获取指定的摄像头。", //error_camera_get
"101": "无法获取指定的麦克风。", //error_microphone_get
"102": "摄像头为禁用状态,推流时请允许flash访问摄像头。", //error_camera_muted
"103": "服务器关闭了连接。", //error_connection_closed
"104": "服务器连接失败。", //error_connection_failed
"199": "未知错误。"
};
}
/**
* user can set some callback, then start the publisher.
* callbacks:
* on_publisher_ready(cameras, microphones):int, when srs publisher ready, user can publish.
* on_publisher_error(code, desc):int, when srs publisher error, callback this method.
* on_publisher_warn(code, desc):int, when srs publisher warn, callback this method.
*/
SrsPublisher.prototype.start = function() {
// embed the flash.
var flashvars = {};
flashvars.id = this.id;
flashvars.width = this.width;
flashvars.height = this.height;
flashvars.on_publisher_ready = "__srs_on_publisher_ready";
flashvars.on_publisher_error = "__srs_on_publisher_error";
flashvars.on_publisher_warn = "__srs_on_publisher_warn";
var params = {};
params.wmode = "opaque";
params.allowFullScreen = "true";
params.allowScriptAccess = "always";
var attributes = {};
var self = this;
swfobject.embedSWF(
"srs_publisher/release/srs_publisher.swf?_version="+srs_get_version_code(),
this.container,
this.width, this.height,
"11.1.0", "js/AdobeFlashPlayerInstall.swf",
flashvars, params, attributes,
function(callbackObj){
self.callbackObj = callbackObj;
}
);
return this;
}
/**
* publish stream to server.
* @param url a string indicates the rtmp url to publish.
* @param vcodec an object contains the video codec info.
* @param acodec an object contains the audio codec info.
*/
SrsPublisher.prototype.publish = function(url, vcodec, acodec) {
this.stop();
SrsPublisher.__publishers.push(this);
if (url) {
this.url = url;
}
if (vcodec) {
this.vcodec = vcodec;
}
if (acodec) {
this.acodec = acodec;
}
this.callbackObj.ref.__publish(this.url, this.width, this.height, this.vcodec, this.acodec);
}
SrsPublisher.prototype.stop = function() {
for (var i = 0; i < SrsPublisher.__publishers.length; i++) {
var player = SrsPublisher.__publishers[i];
if (player.id != this.id) {
continue;
}
SrsPublisher.__publishers.splice(i, 1);
break;
}
this.callbackObj.ref.__stop();
}
/**
* when publisher ready.
* @param cameras a string array contains the names of cameras.
* @param microphones a string array contains the names of microphones.
*/
SrsPublisher.prototype.on_publisher_ready = function(cameras, microphones) {
}
/**
* when publisher error.
* @code the error code.
* @desc the error desc message.
*/
SrsPublisher.prototype.on_publisher_error = function(code, desc) {
throw new Error("publisher error. code=" + code + ", desc=" + desc);
}
SrsPublisher.prototype.on_publisher_warn = function(code, desc) {
throw new Error("publisher warn. code=" + code + ", desc=" + desc);
}
function __srs_find_publisher(id) {
for (var i = 0; i < SrsPublisher.__publishers.length; i++) {
var publisher = SrsPublisher.__publishers[i];
if (publisher.id != id) {
continue;
}
return publisher;
}
throw new Error("publisher not found. id=" + id);
}
function __srs_on_publisher_ready(id, cameras, microphones) {
var publisher = __srs_find_publisher(id);
publisher.cameras = cameras;
publisher.microphones = microphones;
publisher.on_publisher_ready(cameras, microphones);
}
function __srs_on_publisher_error(id, code) {
var publisher = __srs_find_publisher(id);
publisher.code = code;
publisher.on_publisher_error(code, publisher.errors[""+code]);
}
function __srs_on_publisher_warn(id, code) {
var publisher = __srs_find_publisher(id);
publisher.code = code;
publisher.on_publisher_warn(code, publisher.errors[""+code]);
}
@@ -0,0 +1,681 @@
//
// Copyright (c) 2013-2021 Winlin
//
// SPDX-License-Identifier: MIT
//
'use strict';
function SrsError(name, message) {
this.name = name;
this.message = message;
this.stack = (new Error()).stack;
}
SrsError.prototype = Object.create(Error.prototype);
SrsError.prototype.constructor = SrsError;
// Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
// Async-awat-prmise based SRS RTC Publisher.
function SrsRtcPublisherAsync() {
var self = {};
// https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
self.constraints = {
audio: true,
video: {
width: {ideal: 320, max: 576}
}
};
// @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(eip) of answer:
// webrtc://r.ossrs.net/live/livestream?candidate=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"});
//self.pc.addTransceiver("video", {direction: "sendonly"});
//self.pc.addTransceiver("audio", {direction: "sendonly"});
if (!navigator.mediaDevices && window.location.protocol === 'http:' && window.location.hostname !== 'localhost') {
throw new SrsError('HttpsRequiredError', `Please use HTTPS or localhost to publish, read https://github.com/ossrs/srs/issues/2762#issuecomment-983147576`);
}
var stream = await navigator.mediaDevices.getUserMedia(self.constraints);
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
stream.getTracks().forEach(function (track) {
self.pc.addTrack(track);
// Notify about local track when stream is ok.
self.ontrack && self.ontrack({track: 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, tid: conf.tid, streamurl: conf.streamUrl,
clientip: null, sdp: offer.sdp
};
console.log("Generated offer: ", data);
const xhr = new XMLHttpRequest();
xhr.onload = function() {
if (xhr.readyState !== xhr.DONE) return;
if (xhr.status !== 200 && xhr.status !== 201) return reject(xhr);
const data = JSON.parse(xhr.responseText);
console.log("Got answer: ", data);
return data.code ? reject(xhr) : resolve(data);
}
xhr.open('POST', conf.apiUrl, true);
xhr.setRequestHeader('Content-type', 'application/json');
xhr.send(JSON.stringify(data));
});
await self.pc.setRemoteDescription(
new RTCSessionDescription({type: 'answer', sdp: session.sdp})
);
session.simulator = conf.schema + '//' + conf.urlObject.server + ':' + conf.port + '/rtc/v1/nack/';
return session;
};
// Close the publisher.
self.close = function () {
self.pc && self.pc.close();
self.pc = null;
};
// The callback when got local stream.
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
self.ontrack = function (event) {
// Add track to stream of SDK.
self.stream.addTrack(event.track);
};
// 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 += '/';
}
var 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
apiUrl = apiUrl.replace(api + '&', api + '?');
var streamUrl = urlObject.url;
return {
apiUrl: apiUrl, streamUrl: streamUrl, schema: schema, urlObject: urlObject, port: port,
tid: Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).slice(0, 7)
};
},
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.substring(1, a.pathname.lastIndexOf("/"));
var stream = a.pathname.slice(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.slice(app.indexOf("?"));
app = app.slice(0, app.indexOf("?"));
if (params.indexOf("vhost=") > 0) {
vhost = params.slice(params.indexOf("vhost=") + "vhost=".length);
if (vhost.indexOf("&") > 0) {
vhost = vhost.slice(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.slice(0, url.indexOf("://"));
}
var port = a.port;
if (!port) {
// Finger out by webrtc url, if contains http or https port, to overwrite default 1985.
if (schema === 'webrtc' && url.indexOf(`webrtc://${a.host}:`) === 0) {
port = (url.indexOf(`webrtc://${a.host}:80`) === 0) ? 80 : 443;
}
// Guess by schema.
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);
// To keep api consistent between player and publisher.
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
// @see https://webrtc.org/getting-started/media-devices
self.stream = new MediaStream();
return self;
}
// Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
// 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
// webrtc://r.ossrs.net:80/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(eip) of answer:
// webrtc://r.ossrs.net/live/livestream?candidate=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"});
//self.pc.addTransceiver("video", {direction: "recvonly"});
//self.pc.addTransceiver("audio", {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, tid: conf.tid, streamurl: conf.streamUrl,
clientip: null, sdp: offer.sdp
};
console.log("Generated offer: ", data);
const xhr = new XMLHttpRequest();
xhr.onload = function() {
if (xhr.readyState !== xhr.DONE) return;
if (xhr.status !== 200 && xhr.status !== 201) return reject(xhr);
const data = JSON.parse(xhr.responseText);
console.log("Got answer: ", data);
return data.code ? reject(xhr) : resolve(data);
}
xhr.open('POST', conf.apiUrl, true);
xhr.setRequestHeader('Content-type', 'application/json');
xhr.send(JSON.stringify(data));
});
await self.pc.setRemoteDescription(
new RTCSessionDescription({type: 'answer', sdp: session.sdp})
);
session.simulator = conf.schema + '//' + conf.urlObject.server + ':' + conf.port + '/rtc/v1/nack/';
return session;
};
// Close the player.
self.close = function() {
self.pc && self.pc.close();
self.pc = null;
};
// The callback when got remote track.
// Note that the onaddstream is deprecated, @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/onaddstream
self.ontrack = function (event) {
// https://webrtc.org/getting-started/remote-streams
self.stream.addTrack(event.track);
};
// 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 += '/';
}
var 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
apiUrl = apiUrl.replace(api + '&', api + '?');
var streamUrl = urlObject.url;
return {
apiUrl: apiUrl, streamUrl: streamUrl, schema: schema, urlObject: urlObject, port: port,
tid: Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).slice(0, 7)
};
},
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.substring(1, a.pathname.lastIndexOf("/"));
var stream = a.pathname.slice(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.slice(app.indexOf("?"));
app = app.slice(0, app.indexOf("?"));
if (params.indexOf("vhost=") > 0) {
vhost = params.slice(params.indexOf("vhost=") + "vhost=".length);
if (vhost.indexOf("&") > 0) {
vhost = vhost.slice(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.slice(0, url.indexOf("://"));
}
var port = a.port;
if (!port) {
// Finger out by webrtc url, if contains http or https port, to overwrite default 1985.
if (schema === 'webrtc' && url.indexOf(`webrtc://${a.host}:`) === 0) {
port = (url.indexOf(`webrtc://${a.host}:80`) === 0) ? 80 : 443;
}
// Guess by schema.
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);
// Create a stream to add track to the stream, @see https://webrtc.org/getting-started/remote-streams
self.stream = new MediaStream();
// https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/ontrack
self.pc.ontrack = function(event) {
if (self.ontrack) {
self.ontrack(event);
}
};
return self;
}
// Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
// Async-awat-prmise based SRS RTC Publisher by WHIP.
function SrsRtcWhipWhepAsync() {
var self = {};
// https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
self.constraints = {
audio: true,
video: {
width: {ideal: 320, max: 576}
}
};
// See https://datatracker.ietf.org/doc/draft-ietf-wish-whip/
// @url The WebRTC url to publish with, for example:
// http://localhost:1985/rtc/v1/whip/?app=live&stream=livestream
self.publish = async function (url) {
if (url.indexOf('/whip/') === -1) throw new Error(`invalid WHIP url ${url}`);
self.pc.addTransceiver("audio", {direction: "sendonly"});
self.pc.addTransceiver("video", {direction: "sendonly"});
if (!navigator.mediaDevices && window.location.protocol === 'http:' && window.location.hostname !== 'localhost') {
throw new SrsError('HttpsRequiredError', `Please use HTTPS or localhost to publish, read https://github.com/ossrs/srs/issues/2762#issuecomment-983147576`);
}
var stream = await navigator.mediaDevices.getUserMedia(self.constraints);
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
stream.getTracks().forEach(function (track) {
self.pc.addTrack(track);
// Notify about local track when stream is ok.
self.ontrack && self.ontrack({track: track});
});
var offer = await self.pc.createOffer();
await self.pc.setLocalDescription(offer);
const answer = await new Promise(function (resolve, reject) {
console.log("Generated offer: ", offer);
const xhr = new XMLHttpRequest();
xhr.onload = function() {
if (xhr.readyState !== xhr.DONE) return;
if (xhr.status !== 200 && xhr.status !== 201) return reject(xhr);
const data = xhr.responseText;
console.log("Got answer: ", data);
return data.code ? reject(xhr) : resolve(data);
}
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-type', 'application/sdp');
xhr.send(offer.sdp);
});
await self.pc.setRemoteDescription(
new RTCSessionDescription({type: 'answer', sdp: answer})
);
return self.__internal.parseId(url, offer.sdp, answer);
};
// See https://datatracker.ietf.org/doc/draft-ietf-wish-whip/
// @url The WebRTC url to play with, for example:
// http://localhost:1985/rtc/v1/whip-play/?app=live&stream=livestream
self.play = async function(url) {
if (url.indexOf('/whip-play/') === -1 && url.indexOf('/whep/') === -1) throw new Error(`invalid WHEP url ${url}`);
self.pc.addTransceiver("audio", {direction: "recvonly"});
self.pc.addTransceiver("video", {direction: "recvonly"});
var offer = await self.pc.createOffer();
await self.pc.setLocalDescription(offer);
const answer = await new Promise(function(resolve, reject) {
console.log("Generated offer: ", offer);
const xhr = new XMLHttpRequest();
xhr.onload = function() {
if (xhr.readyState !== xhr.DONE) return;
if (xhr.status !== 200 && xhr.status !== 201) return reject(xhr);
const data = xhr.responseText;
console.log("Got answer: ", data);
return data.code ? reject(xhr) : resolve(data);
}
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-type', 'application/sdp');
xhr.send(offer.sdp);
});
await self.pc.setRemoteDescription(
new RTCSessionDescription({type: 'answer', sdp: answer})
);
return self.__internal.parseId(url, offer.sdp, answer);
};
// Close the publisher.
self.close = function () {
self.pc && self.pc.close();
self.pc = null;
};
// The callback when got local stream.
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
self.ontrack = function (event) {
// Add track to stream of SDK.
self.stream.addTrack(event.track);
};
self.pc = new RTCPeerConnection(null);
// To keep api consistent between player and publisher.
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
// @see https://webrtc.org/getting-started/media-devices
self.stream = new MediaStream();
// Internal APIs.
self.__internal = {
parseId: (url, offer, answer) => {
let sessionid = offer.substr(offer.indexOf('a=ice-ufrag:') + 'a=ice-ufrag:'.length);
sessionid = sessionid.substr(0, sessionid.indexOf('\n') - 1) + ':';
sessionid += answer.substr(answer.indexOf('a=ice-ufrag:') + 'a=ice-ufrag:'.length);
sessionid = sessionid.substr(0, sessionid.indexOf('\n'));
const a = document.createElement("a");
a.href = url;
return {
sessionid: sessionid, // Should be ice-ufrag of answer:offer.
simulator: a.protocol + '//' + a.host + '/rtc/v1/nack/',
};
},
};
// https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/ontrack
self.pc.ontrack = function(event) {
if (self.ontrack) {
self.ontrack(event);
}
};
return self;
}
// Format the codec of RTCRtpSender, kind(audio/video) is optional filter.
// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#getting_the_supported_codecs
function SrsRtcFormatSenders(senders, kind) {
var codecs = [];
senders.forEach(function (sender) {
var params = sender.getParameters();
params && params.codecs && params.codecs.forEach(function(c) {
if (kind && sender.track.kind !== kind) {
return;
}
if (c.mimeType.indexOf('/red') > 0 || c.mimeType.indexOf('/rtx') > 0 || c.mimeType.indexOf('/fec') > 0) {
return;
}
var s = '';
s += c.mimeType.replace('audio/', '').replace('video/', '');
s += ', ' + c.clockRate + 'HZ';
if (sender.track.kind === "audio") {
s += ', channels: ' + c.channels;
}
s += ', pt: ' + c.payloadType;
codecs.push(s);
});
});
return codecs.join(", ");
}
@@ -0,0 +1,8 @@
/**
* parse the rtmp url,
* for example: rtmp://demo.srs.com:1935/live...vhost...players/livestream
* @return object {server, port, vhost, app, stream}
*/
function srs_parse_rtmp_url(rtmp_url) {
return parse_rtmp_url(rtmp_url);
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,686 @@
// 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.23
*/
/**
* 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.slice(flag.length);
}
while (system_string_endswith(str, flag)) {
str = str.slice(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.slice(0, obj.pathname.lastIndexOf("/"));
obj.filename = obj.pathname.slice(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.substring(1, a.pathname.lastIndexOf("/"));
var stream = a.pathname.slice(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.slice(app.indexOf("?"));
app = app.slice(0, app.indexOf("?"));
if (params.indexOf("vhost=") > 0) {
vhost = params.slice(params.indexOf("vhost=") + "vhost=".length);
if (vhost.indexOf("&") > 0) {
vhost = vhost.slice(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.slice(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 {
// For WebRTC, SRS use 1985 as default API port.
ret.port = 1985;
}
}
}
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);
}
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<title>SRS</title>
<meta charset="utf-8">
</head>
<body>
</body>
<script type="text/javascript">
setTimeout(function(){
window.location.href = "players/index.html" + window.location.search;
}, 500);
</script>
</html>
@@ -0,0 +1,115 @@
<!DOCTYPE html>
<html>
<head>
<title>SRS</title>
<meta charset="utf-8">
<style>
body{
padding-top: 30px;
}
</style>
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
<script type="text/javascript" src="js/jquery-1.12.2.min.js"></script>
<script type="text/javascript" src="js/adapter-7.4.0.min.js"></script>
<script type="text/javascript" src="js/srs.sdk.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='//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 id="nav_whip" href="whip.html">WHIP</a></li>
<li><a id="nav_whep" href="whip.html">WHEP</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(){
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();
// https://webrtc.org/getting-started/remote-streams
$('#rtc_media_player').prop('srcObject', sdk.stream);
// Optional callback, SDK will add track to stream.
// sdk.ontrack = function (event) { console.log('Got track', event); sdk.stream.addTrack(event.track); };
// 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');
window.addEventListener("load", function(){ startPlay(); });
}
});
</script>
</body>
</html>
@@ -0,0 +1,142 @@
<!DOCTYPE html>
<html>
<head>
<title>SRS</title>
<meta charset="utf-8">
<style>
body{
padding-top: 30px;
}
</style>
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
<script type="text/javascript" src="js/jquery-1.12.2.min.js"></script>
<script type="text/javascript" src="js/adapter-7.4.0.min.js"></script>
<script type="text/javascript" src="js/srs.sdk.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='//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 id="nav_whip" href="whip.html">WHIP</a></li>
<li><a id="nav_whep" href="whip.html">WHEP</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>
Audio: <span id='acodecs'></span><br/>
Video: <span id='vcodecs'></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(){
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();
// User should set the stream when publish is done, @see https://webrtc.org/getting-started/media-devices
// However SRS SDK provides a consist API like https://webrtc.org/getting-started/remote-streams
$('#rtc_media_player').prop('srcObject', sdk.stream);
// Optional callback, SDK will add track to stream.
// sdk.ontrack = function (event) { console.log('Got track', event); sdk.stream.addTrack(event.track); };
// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#getting_the_supported_codecs
sdk.pc.onicegatheringstatechange = function (event) {
if (sdk.pc.iceGatheringState === "complete") {
$('#acodecs').html(SrsRtcFormatSenders(sdk.pc.getSenders(), "audio"));
$('#vcodecs').html(SrsRtcFormatSenders(sdk.pc.getSenders(), "video"));
}
};
// 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) {
// Throw by sdk.
if (reason instanceof SrsError) {
if (reason.name === 'HttpsRequiredError') {
alert(`WebRTC推流必须是HTTPS或者localhost:${reason.name} ${reason.message}`);
} else {
alert(`${reason.name} ${reason.message}`);
}
}
// See https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia#exceptions
if (reason instanceof DOMException) {
if (reason.name === 'NotFoundError') {
alert(`找不到麦克风和摄像头设备:getUserMedia ${reason.name} ${reason.message}`);
} else if (reason.name === 'NotAllowedError') {
alert(`你禁止了网页访问摄像头和麦克风:getUserMedia ${reason.name} ${reason.message}`);
} else if (['AbortError', 'NotAllowedError', 'NotFoundError', 'NotReadableError', 'OverconstrainedError', 'SecurityError', 'TypeError'].includes(reason.name)) {
alert(`getUserMedia ${reason.name} ${reason.message}`);
}
}
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);
// Never play util windows loaded @see https://github.com/ossrs/srs/issues/2732
if (query.autostart === 'true') {
window.addEventListener("load", function(){ startPublish(); });
}
});
</script>
</body>
</html>
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<title>SRS</title>
<meta charset="utf-8">
</head>
<body>
</body>
<script type="text/javascript" src="players/js/jquery-1.12.2.min.js"></script>
<script type="text/javascript" src="players/js/srs.page.js"></script>
<script type="text/javascript" src="players/js/srs.utility.js"></script>
<script type="text/javascript" src="players/js/winlin.utility.js"></script>
<script type="text/javascript">
setTimeout(function(){
window.location.href = "players/index.html" + window.location.search;
}, 500);
</script>
</html>
@@ -0,0 +1,233 @@
<!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: 30px;
}
#main_modal {
width: 700px;
margin-left: -350px;
}
</style>
</head>
<body>
<img src='//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">
<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_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_vlc" href="vlc.html">VLC播放器</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>点击“开始测速”即可测带宽,最大可测试带宽由服务器限制</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>SRS Bandwidth Check</h3>
</div>
<div class="modal-body">
<div class="row-fluid">
<div class="span1"></div>
<div class="span10">
<div class="progress progress-striped active" id="pb_buffer_bg">
<div class="bar" style="width: 0%;" id="progress_bar"></div>
</div>
</div>
<div class="span1"></div>
</div>
<div id="check_status">status</div>
<div id="check_info">info</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true"> 关闭 </button>
</div>
</div>
</div>
<footer>
<p></p>
<p><a href="https://github.com/ossrs/srs">SRS Team &copy; 2013</a></p>
</footer>
<div class="container">
<div id="player"></div>
</div>
</div>
</body>
<script type="text/javascript" src="js/jquery-1.12.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" src="srs_bwt/src/srs.bandwidth.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 bandwidth = null;
// for bw to init url
// url: scheme://host:port/path?query#fragment
function srs_init_bwt(rtmp_url, hls_url) {
update_nav();
if (rtmp_url) {
$(rtmp_url).val(build_default_bandwidth_rtmp_url());
}
}
// for the bandwidth tool to init page
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;
var uri = schema + "://" + server;
if (!is_default_port(schema, port)) {
uri += ":" + port;
}
uri += "/" + app + "?key=" + key + "&vhost=" + vhost;
return uri;
}
var autoLoadPage = function() {
srs_init_bwt("#txt_url");
$("#btn_play").click(on_click_play);
$("#main_modal").on("show", on_start_bandwidth_test);
$("#main_modal").on("hide", on_stop_bandwidth_test);
};
function on_click_play() {
$("#check_status").text("");
$("#check_info").text("");
$("#progress_bar").width("0%");
$("#main_modal").modal({show:true, keyboard:false});
}
function on_start_bandwidth_test() {
$("#div_container").remove();
$("#progress_bar").removeClass("bar-danger");
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 url = $("#txt_url").val();
bandwidth = new SrsBandwidth("player_id", 100, 1);
bandwidth.on_bandwidth_ready = function() {
this.check_bandwidth(url);
}
bandwidth.on_update_progress = function(percent) {
$("#progress_bar").width(percent + "%");
}
bandwidth.on_update_status = function(status) {
$("#check_status").text(status);
}
bandwidth.on_srs_info = function(srs_server, srs_primary, srs_authors, srs_id, srs_pid, srs_server_ip) {
$("#check_info").text(
"server:" + srs_server + ", srs_id:" + srs_id + ", srs_pid:" + srs_pid + ", ip:" + srs_server_ip
);
}
bandwidth.on_error = function(code) {
$("#progress_bar").addClass("bar-danger");
}
bandwidth.render(url);
}
function on_stop_bandwidth_test() {
bandwidth.stop();
}
</script>
</html>

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