Commit 0d9ccbe2 authored by tianye's avatar tianye

v1.0.3封版

parent e7da080f
#!/bin/bash
source ./install.ini
DEPLOY_FILE=$(dirname $0)
sc_dir=${DEPLOY_DIR}/search-center
#将docker-compose工程部署到本机
if [ ! -d ${sc_dir} ];then
sudo cp -rf search-center ${sc_dir}
cp -rf ${DEPLOY_FILE}/search_center ${sc_dir}
fi
#将配置文件存入环境变量文件,使其全局生效
sudo cp ./install.ini ${sc_dir}/install.ini.example
#sudo echo '' >${sc_dir}/.env
cp ./install.ini ${sc_dir}/install.ini.example
env_context=$(cat ${sc_dir}/install.ini.example |grep -v "^#"|grep -v "^$")
echo "${env_context}">${sc_dir}/.env
source ${sc_dir}/.env
sudo ln -s ${sc_dir}/.env ${sc_dir}/install.ini 2>/dev/null
ln -s ${sc_dir}/.env ${sc_dir}/install.ini 2>/dev/null
cd ${sc_dir}
#docker-compose的基础配置
compose_files="-f docker-compose-base.yaml"
INSTALL_MODE=$1
#修改es连接信息为外部
#修改es连接信息(默认-->外部)
edit_es_info(){
echo -e " 使用外部MQ"
echo -e " 即将修改配置...."
sleep 2
grep -A 10 -w "es:" ${APP_CONF_DIR}/application.yml | \
sed -i -e "s#host: .*#host: ${ES_HOST}#g"\
-e "s#username: .*#username: ${ES_USERNAME}#g"\
-e "s#password: .*#password: ${ES_PASSWORD}#g"\
-e "s#httpPort: .*#httpPort: ${ES_HTTP_PORT}#g"\
-e "s#tcpPort: .*#tcpPort: ${ES_TCP_PORT}#g"
sed -i -e "s#host: es#host: ${ES_HOST}#g"\
-e "s#username: elastic#username: ${ES_USERNAME}#g"\
-e "s#password: changeme#password: ${ES_PASSWORD}#g"\
-e "s#httpPort: 9200#httpPort: ${ES_HTTP_PORT}#g"\
-e "s#tcpPort: 9300#tcpPort: ${ES_TCP_PORT}#g" \
-e "s#clusterName: elasticsearch-cluster#clusterName: ${ES_ClusterName}#g" ${APP_CONF_DIR}/application.yml
}
#修改mq信息为外部
#修改mq连接信息(默认-->外部)
edit_mq_info(){
echo -e " 使用外部MQ"
echo -e " 即将修改配置...."
sleep 2
grep -A 6 -w "rabbitmq:" ${APP_CONF_DIR}/application.yml | \
sed -i -e "s#host: .*#host: ${MQ_HOST}#g"\
-e "s#username: .*#username: ${MQ_USERNAME}#g"\
-e "s#password: .*#password: ${MQ_PASSWORD}#g"\
-e "s#virtual-host: .*#virtual-host: ${MQ_VHOST}#g"
sed -i -e "s#host: rabbitmq01#host: ${MQ_HOST}#g"\
-e "s#username: admin#username: ${MQ_USERNAME}#g"\
-e "s#password: admin#password: ${MQ_PASSWORD}#g"\
-e "s#port: 5672#port: ${MQ_PORT}#g"\
-e "s#virtual-host: /#virtual-host: ${MQ_VHOST}#g" ${APP_CONF_DIR}/application.yml
}
#--------------------部署-----------------------
echo -e "----------安装向导------------"
......@@ -78,14 +77,22 @@ done
#是否使用外部组件
if [ ${INSTALL_MODE} == "all" ];then
echo -e "正在创建数据目录"
mkdir -p ${sc_dir}/data/{es_data,mq_data}
echo -e "正在创建日志目录"
mkdir -p ${sc_dir}/log
sudo docker-compose ${compose_files} up -d
elif [ ${INSTALL_MODE} == "server" ];then
echo -e "正在创建日志目录"
mkdir -p ${sc_dir}/log
edit_es_info
edit_mq_info
sudo docker-compose ${compose_files} up -d
elif [ ${INSTALL_MODE} == "controller" ];then
#判断es
if [ ${ES_ISUSE_STATUS} == 'false' ];then
echo -e "正在创建Elasticsearch数据目录"
mkdir -p ${sc_dir}/data/es_data
compose_files="${compose_files} -f docker-compose-es.yaml"
elif [ ${ES_ISUSE_STATUS} == 'true' ];then
edit_es_info
......@@ -95,6 +102,8 @@ elif [ ${INSTALL_MODE} == "controller" ];then
fi
#判断mq
if [ ${MQ_ISUSE_STATUS} == 'false' ];then
echo -e "正在创建Elasticsearch数据目录"
mkdir -p ${sc_dir}/data/es_data
compose_files="${compose_files} -f docker-compose-mq.yaml"
elif [ ${MQ_ISUSE_STATUS} == 'true' ];then
edit_mq_info
......@@ -102,17 +111,18 @@ elif [ ${INSTALL_MODE} == "controller" ];then
ehco "mq_isuse_status配置错误"
exit 127
fi
echo -e "正在创建日志目录"
mkdir -p ${sc_dir}/log
#将所需要部署的组件ymal输出到文件
echo "${compose_files} -f docker-compose-searchcenter.yaml">${sc_dir}/compose_files
compose_files=$(cat ${sc_dir}/compose_files)
#echo ${compose_files}
sudo docker-compose ${compose_files} up -d
fi
if [ $? -ne 0 ];then
echo "安装过程出现错误,请查看相关配置..."
exit 127
fi
echo "search-center 服务已安装成功..."
......@@ -36,8 +36,8 @@ health-cloud:
httpPort: 9200
auth:
enable: true
username: app_search-center
password: 3HPDm0OdmoMZ6DiYWCUG
username: elastic
password: changeme
env-of-index: qa
management:
health:
......
OZFZEZNVKEIYRGROGLFI
\ No newline at end of file
[{rabbitmq_management,[{tcp_config,[{port,15672}]}]},
{rabbit,[{tcp_listeners,[5672]},
{loopback_users,[]},
{default_user,<<"admin">>},
{default_pass,<<"admin">>}]}].
{application, 'amqp_client', [
{description, "RabbitMQ AMQP Client"},
{vsn, "3.7.28"},
{id, "v3.7.27-1-g1d21a2f"},
{modules, ['amqp_auth_mechanisms','amqp_channel','amqp_channel_sup','amqp_channel_sup_sup','amqp_channels_manager','amqp_client','amqp_connection','amqp_connection_sup','amqp_connection_type_sup','amqp_direct_connection','amqp_direct_consumer','amqp_gen_connection','amqp_gen_consumer','amqp_main_reader','amqp_network_connection','amqp_rpc_client','amqp_rpc_server','amqp_selective_consumer','amqp_ssl','amqp_sup','amqp_uri','amqp_util','rabbit_routing_util','uri_parser']},
{registered, [amqp_client_sup,amqp_sup]},
{applications, [kernel,stdlib,xmerl,rabbit_common]},
{mod, {amqp_client, []}},
{env, [
{prefer_ipv6, false},
{ssl_options, []},
{gc_threshold, 1000000000}
]},
%% Hex.pm package informations.
{licenses, ["MPL 1.1"]},
{links, [
{"Website", "http://www.rabbitmq.com/"},
{"GitHub", "https://github.com/rabbitmq/rabbitmq-erlang-client"},
{"User guide", "http://www.rabbitmq.com/erlang-client-user-guide.html"}
]},
{build_tools, ["make", "rebar3"]},
{files, [
"erlang.mk",
"git-revisions.txt",
"include",
"LICENSE*",
"Makefile",
"rabbitmq-components.mk",
"README",
"README.md",
"src"
]}
]}.
\ No newline at end of file
%% The contents of this file are subject to the Mozilla Public License
%% Version 1.1 (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License at
%% http://www.mozilla.org/MPL/
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
%% License for the specific language governing rights and limitations
%% under the License.
%%
%% The Original Code is RabbitMQ.
%%
%% The Initial Developer of the Original Code is GoPivotal, Inc.
%% Copyright (c) 2007-2020 Pivotal Software, Inc. All rights reserved.
%%
-ifndef(AMQP_CLIENT_HRL).
-define(AMQP_CLIENT_HRL, true).
-include_lib("rabbit_common/include/rabbit.hrl").
-include_lib("rabbit_common/include/rabbit_framing.hrl").
-record(amqp_msg, {props = #'P_basic'{}, payload = <<>>}).
-record(amqp_params_network, {username = <<"guest">>,
password = <<"guest">>,
virtual_host = <<"/">>,
host = "localhost",
port = undefined,
channel_max = 2047,
frame_max = 0,
heartbeat = 10,
connection_timeout = 60000,
ssl_options = none,
auth_mechanisms =
[fun amqp_auth_mechanisms:plain/3,
fun amqp_auth_mechanisms:amqplain/3],
client_properties = [],
socket_options = []}).
-record(amqp_params_direct, {username = none,
password = none,
virtual_host = <<"/">>,
node = node(),
adapter_info = none,
client_properties = []}).
-record(amqp_adapter_info, {host = unknown,
port = unknown,
peer_host = unknown,
peer_port = unknown,
name = unknown,
protocol = unknown,
additional_info = []}).
-endif.
%% The contents of this file are subject to the Mozilla Public License
%% Version 1.1 (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License at
%% http://www.mozilla.org/MPL/
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
%% License for the specific language governing rights and limitations
%% under the License.
%%
%% The Original Code is RabbitMQ.
%%
%% The Initial Developer of the Original Code is GoPivotal, Inc.
%% Copyright (c) 2007-2020 Pivotal Software, Inc. All rights reserved.
%%
-include("amqp_client.hrl").
-define(PROTOCOL_VERSION_MAJOR, 0).
-define(PROTOCOL_VERSION_MINOR, 9).
-define(PROTOCOL_HEADER, <<"AMQP", 0, 0, 9, 1>>).
-define(PROTOCOL, rabbit_framing_amqp_0_9_1).
-define(MAX_CHANNEL_NUMBER, 65535).
-define(LOG_DEBUG(Format), error_logger:info_msg(Format)).
-define(LOG_INFO(Format, Args), error_logger:info_msg(Format, Args)).
-define(LOG_WARN(Format, Args), error_logger:warning_msg(Format, Args)).
-define(LOG_ERR(Format, Args), error_logger:error_msg(Format, Args)).
-define(CLIENT_CAPABILITIES,
[{<<"publisher_confirms">>, bool, true},
{<<"exchange_exchange_bindings">>, bool, true},
{<<"basic.nack">>, bool, true},
{<<"consumer_cancel_notify">>, bool, true},
{<<"connection.blocked">>, bool, true},
{<<"authentication_failure_close">>, bool, true}]).
-define(CALL_TIMEOUT, rabbit_misc:get_env(amqp_client, gen_server_call_timeout,
60000)).
%% The contents of this file are subject to the Mozilla Public License
%% Version 1.1 (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License at
%% http://www.mozilla.org/MPL/
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
%% License for the specific language governing rights and limitations
%% under the License.
%%
%% The Original Code is RabbitMQ.
%%
%% The Initial Developer of the Original Code is GoPivotal, Inc.
%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved.
%%
-include("amqp_client.hrl").
-type state() :: any().
-type consume() :: #'basic.consume'{}.
-type consume_ok() :: #'basic.consume_ok'{}.
-type cancel() :: #'basic.cancel'{}.
-type cancel_ok() :: #'basic.cancel_ok'{}.
-type deliver() :: #'basic.deliver'{}.
-type from() :: any().
-type reason() :: any().
-type ok_error() :: {ok, state()} | {error, reason(), state()}.
-spec init([any()]) -> {ok, state()}.
-spec handle_consume(consume(), pid(), state()) -> ok_error().
-spec handle_consume_ok(consume_ok(), consume(), state()) ->
ok_error().
-spec handle_cancel(cancel(), state()) -> ok_error().
-spec handle_server_cancel(cancel(), state()) -> ok_error().
-spec handle_cancel_ok(cancel_ok(), cancel(), state()) -> ok_error().
-spec handle_deliver(deliver(), #amqp_msg{}, state()) -> ok_error().
-spec handle_info(any(), state()) -> ok_error().
-spec handle_call(any(), from(), state()) ->
{reply, any(), state()} | {noreply, state()} |
{error, reason(), state()}.
-spec terminate(any(), state()) -> state().
%% The contents of this file are subject to the Mozilla Public License
%% Version 1.1 (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License
%% at http://www.mozilla.org/MPL/
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and
%% limitations under the License.
%%
%% The Original Code is RabbitMQ.
%%
%% The Initial Developer of the Original Code is GoPivotal, Inc.
%% Copyright (c) 2011-2015 Pivotal Software, Inc. All rights reserved.
%%
-define(QUEUE_PREFIX, "/queue").
-define(TOPIC_PREFIX, "/topic").
-define(EXCHANGE_PREFIX, "/exchange").
-define(AMQQUEUE_PREFIX, "/amq/queue").
-define(TEMP_QUEUE_PREFIX, "/temp-queue").
%% reply queues names can have slashes in the content so no further
%% parsing happens.
-define(REPLY_QUEUE_PREFIX, "/reply-queue/").
{application,cowboy,
[{description,"Small, fast, modern HTTP server."},
{vsn,"2.6.1"},
{modules, ['cowboy','cowboy_app','cowboy_bstr','cowboy_children','cowboy_clear','cowboy_clock','cowboy_compress_h','cowboy_constraints','cowboy_handler','cowboy_http','cowboy_http2','cowboy_loop','cowboy_metrics_h','cowboy_middleware','cowboy_req','cowboy_rest','cowboy_router','cowboy_static','cowboy_stream','cowboy_stream_h','cowboy_sub_protocol','cowboy_sup','cowboy_tls','cowboy_tracer_h','cowboy_websocket']},
{registered,[cowboy_sup,cowboy_clock]},
{applications,[kernel,stdlib,crypto,cowlib,ranch]},
{mod,{cowboy_app,[]}},
{env,[]}]}.
{application, 'cowlib', [
{description, "Support library for manipulating Web protocols."},
{vsn, "2.7.0"},
{id, "v3.7.27-1-g1d21a2f"},
{modules, ['cow_base64url','cow_cookie','cow_date','cow_hpack','cow_http','cow_http2','cow_http2_machine','cow_http_hd','cow_http_te','cow_iolists','cow_mimetypes','cow_multipart','cow_qs','cow_spdy','cow_sse','cow_uri','cow_ws']},
{registered, []},
{applications, [kernel,stdlib,crypto]},
{env, []}
]}.
\ No newline at end of file
%% Copyright (c) 2014-2018, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-ifndef(COW_INLINE_HRL).
-define(COW_INLINE_HRL, 1).
%% LC(Character)
-define(LC(C), case C of
$A -> $a;
$B -> $b;
$C -> $c;
$D -> $d;
$E -> $e;
$F -> $f;
$G -> $g;
$H -> $h;
$I -> $i;
$J -> $j;
$K -> $k;
$L -> $l;
$M -> $m;
$N -> $n;
$O -> $o;
$P -> $p;
$Q -> $q;
$R -> $r;
$S -> $s;
$T -> $t;
$U -> $u;
$V -> $v;
$W -> $w;
$X -> $x;
$Y -> $y;
$Z -> $z;
_ -> C
end).
%% LOWER(Bin)
%%
%% Lowercase the entire binary string in a binary comprehension.
-define(LOWER(Bin), << << ?LC(C) >> || << C >> <= Bin >>).
%% LOWERCASE(Function, Rest, Acc, ...)
%%
%% To be included at the end of a case block.
%% Defined for up to 10 extra arguments.
-define(LOWER(Function, Rest, Acc), case C of
$A -> Function(Rest, << Acc/binary, $a >>);
$B -> Function(Rest, << Acc/binary, $b >>);
$C -> Function(Rest, << Acc/binary, $c >>);
$D -> Function(Rest, << Acc/binary, $d >>);
$E -> Function(Rest, << Acc/binary, $e >>);
$F -> Function(Rest, << Acc/binary, $f >>);
$G -> Function(Rest, << Acc/binary, $g >>);
$H -> Function(Rest, << Acc/binary, $h >>);
$I -> Function(Rest, << Acc/binary, $i >>);
$J -> Function(Rest, << Acc/binary, $j >>);
$K -> Function(Rest, << Acc/binary, $k >>);
$L -> Function(Rest, << Acc/binary, $l >>);
$M -> Function(Rest, << Acc/binary, $m >>);
$N -> Function(Rest, << Acc/binary, $n >>);
$O -> Function(Rest, << Acc/binary, $o >>);
$P -> Function(Rest, << Acc/binary, $p >>);
$Q -> Function(Rest, << Acc/binary, $q >>);
$R -> Function(Rest, << Acc/binary, $r >>);
$S -> Function(Rest, << Acc/binary, $s >>);
$T -> Function(Rest, << Acc/binary, $t >>);
$U -> Function(Rest, << Acc/binary, $u >>);
$V -> Function(Rest, << Acc/binary, $v >>);
$W -> Function(Rest, << Acc/binary, $w >>);
$X -> Function(Rest, << Acc/binary, $x >>);
$Y -> Function(Rest, << Acc/binary, $y >>);
$Z -> Function(Rest, << Acc/binary, $z >>);
C -> Function(Rest, << Acc/binary, C >>)
end).
-define(LOWER(Function, Rest, A0, Acc), case C of
$A -> Function(Rest, A0, << Acc/binary, $a >>);
$B -> Function(Rest, A0, << Acc/binary, $b >>);
$C -> Function(Rest, A0, << Acc/binary, $c >>);
$D -> Function(Rest, A0, << Acc/binary, $d >>);
$E -> Function(Rest, A0, << Acc/binary, $e >>);
$F -> Function(Rest, A0, << Acc/binary, $f >>);
$G -> Function(Rest, A0, << Acc/binary, $g >>);
$H -> Function(Rest, A0, << Acc/binary, $h >>);
$I -> Function(Rest, A0, << Acc/binary, $i >>);
$J -> Function(Rest, A0, << Acc/binary, $j >>);
$K -> Function(Rest, A0, << Acc/binary, $k >>);
$L -> Function(Rest, A0, << Acc/binary, $l >>);
$M -> Function(Rest, A0, << Acc/binary, $m >>);
$N -> Function(Rest, A0, << Acc/binary, $n >>);
$O -> Function(Rest, A0, << Acc/binary, $o >>);
$P -> Function(Rest, A0, << Acc/binary, $p >>);
$Q -> Function(Rest, A0, << Acc/binary, $q >>);
$R -> Function(Rest, A0, << Acc/binary, $r >>);
$S -> Function(Rest, A0, << Acc/binary, $s >>);
$T -> Function(Rest, A0, << Acc/binary, $t >>);
$U -> Function(Rest, A0, << Acc/binary, $u >>);
$V -> Function(Rest, A0, << Acc/binary, $v >>);
$W -> Function(Rest, A0, << Acc/binary, $w >>);
$X -> Function(Rest, A0, << Acc/binary, $x >>);
$Y -> Function(Rest, A0, << Acc/binary, $y >>);
$Z -> Function(Rest, A0, << Acc/binary, $z >>);
C -> Function(Rest, A0, << Acc/binary, C >>)
end).
-define(LOWER(Function, Rest, A0, A1, Acc), case C of
$A -> Function(Rest, A0, A1, << Acc/binary, $a >>);
$B -> Function(Rest, A0, A1, << Acc/binary, $b >>);
$C -> Function(Rest, A0, A1, << Acc/binary, $c >>);
$D -> Function(Rest, A0, A1, << Acc/binary, $d >>);
$E -> Function(Rest, A0, A1, << Acc/binary, $e >>);
$F -> Function(Rest, A0, A1, << Acc/binary, $f >>);
$G -> Function(Rest, A0, A1, << Acc/binary, $g >>);
$H -> Function(Rest, A0, A1, << Acc/binary, $h >>);
$I -> Function(Rest, A0, A1, << Acc/binary, $i >>);
$J -> Function(Rest, A0, A1, << Acc/binary, $j >>);
$K -> Function(Rest, A0, A1, << Acc/binary, $k >>);
$L -> Function(Rest, A0, A1, << Acc/binary, $l >>);
$M -> Function(Rest, A0, A1, << Acc/binary, $m >>);
$N -> Function(Rest, A0, A1, << Acc/binary, $n >>);
$O -> Function(Rest, A0, A1, << Acc/binary, $o >>);
$P -> Function(Rest, A0, A1, << Acc/binary, $p >>);
$Q -> Function(Rest, A0, A1, << Acc/binary, $q >>);
$R -> Function(Rest, A0, A1, << Acc/binary, $r >>);
$S -> Function(Rest, A0, A1, << Acc/binary, $s >>);
$T -> Function(Rest, A0, A1, << Acc/binary, $t >>);
$U -> Function(Rest, A0, A1, << Acc/binary, $u >>);
$V -> Function(Rest, A0, A1, << Acc/binary, $v >>);
$W -> Function(Rest, A0, A1, << Acc/binary, $w >>);
$X -> Function(Rest, A0, A1, << Acc/binary, $x >>);
$Y -> Function(Rest, A0, A1, << Acc/binary, $y >>);
$Z -> Function(Rest, A0, A1, << Acc/binary, $z >>);
C -> Function(Rest, A0, A1, << Acc/binary, C >>)
end).
-define(LOWER(Function, Rest, A0, A1, A2, Acc), case C of
$A -> Function(Rest, A0, A1, A2, << Acc/binary, $a >>);
$B -> Function(Rest, A0, A1, A2, << Acc/binary, $b >>);
$C -> Function(Rest, A0, A1, A2, << Acc/binary, $c >>);
$D -> Function(Rest, A0, A1, A2, << Acc/binary, $d >>);
$E -> Function(Rest, A0, A1, A2, << Acc/binary, $e >>);
$F -> Function(Rest, A0, A1, A2, << Acc/binary, $f >>);
$G -> Function(Rest, A0, A1, A2, << Acc/binary, $g >>);
$H -> Function(Rest, A0, A1, A2, << Acc/binary, $h >>);
$I -> Function(Rest, A0, A1, A2, << Acc/binary, $i >>);
$J -> Function(Rest, A0, A1, A2, << Acc/binary, $j >>);
$K -> Function(Rest, A0, A1, A2, << Acc/binary, $k >>);
$L -> Function(Rest, A0, A1, A2, << Acc/binary, $l >>);
$M -> Function(Rest, A0, A1, A2, << Acc/binary, $m >>);
$N -> Function(Rest, A0, A1, A2, << Acc/binary, $n >>);
$O -> Function(Rest, A0, A1, A2, << Acc/binary, $o >>);
$P -> Function(Rest, A0, A1, A2, << Acc/binary, $p >>);
$Q -> Function(Rest, A0, A1, A2, << Acc/binary, $q >>);
$R -> Function(Rest, A0, A1, A2, << Acc/binary, $r >>);
$S -> Function(Rest, A0, A1, A2, << Acc/binary, $s >>);
$T -> Function(Rest, A0, A1, A2, << Acc/binary, $t >>);
$U -> Function(Rest, A0, A1, A2, << Acc/binary, $u >>);
$V -> Function(Rest, A0, A1, A2, << Acc/binary, $v >>);
$W -> Function(Rest, A0, A1, A2, << Acc/binary, $w >>);
$X -> Function(Rest, A0, A1, A2, << Acc/binary, $x >>);
$Y -> Function(Rest, A0, A1, A2, << Acc/binary, $y >>);
$Z -> Function(Rest, A0, A1, A2, << Acc/binary, $z >>);
C -> Function(Rest, A0, A1, A2, << Acc/binary, C >>)
end).
-define(LOWER(Function, Rest, A0, A1, A2, A3, Acc), case C of
$A -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $a >>);
$B -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $b >>);
$C -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $c >>);
$D -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $d >>);
$E -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $e >>);
$F -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $f >>);
$G -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $g >>);
$H -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $h >>);
$I -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $i >>);
$J -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $j >>);
$K -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $k >>);
$L -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $l >>);
$M -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $m >>);
$N -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $n >>);
$O -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $o >>);
$P -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $p >>);
$Q -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $q >>);
$R -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $r >>);
$S -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $s >>);
$T -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $t >>);
$U -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $u >>);
$V -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $v >>);
$W -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $w >>);
$X -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $x >>);
$Y -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $y >>);
$Z -> Function(Rest, A0, A1, A2, A3, << Acc/binary, $z >>);
C -> Function(Rest, A0, A1, A2, A3, << Acc/binary, C >>)
end).
-define(LOWER(Function, Rest, A0, A1, A2, A3, A4, Acc), case C of
$A -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $a >>);
$B -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $b >>);
$C -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $c >>);
$D -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $d >>);
$E -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $e >>);
$F -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $f >>);
$G -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $g >>);
$H -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $h >>);
$I -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $i >>);
$J -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $j >>);
$K -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $k >>);
$L -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $l >>);
$M -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $m >>);
$N -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $n >>);
$O -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $o >>);
$P -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $p >>);
$Q -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $q >>);
$R -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $r >>);
$S -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $s >>);
$T -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $t >>);
$U -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $u >>);
$V -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $v >>);
$W -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $w >>);
$X -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $x >>);
$Y -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $y >>);
$Z -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, $z >>);
C -> Function(Rest, A0, A1, A2, A3, A4, << Acc/binary, C >>)
end).
-define(LOWER(Function, Rest, A0, A1, A2, A3, A4, A5, Acc), case C of
$A -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $a >>);
$B -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $b >>);
$C -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $c >>);
$D -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $d >>);
$E -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $e >>);
$F -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $f >>);
$G -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $g >>);
$H -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $h >>);
$I -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $i >>);
$J -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $j >>);
$K -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $k >>);
$L -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $l >>);
$M -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $m >>);
$N -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $n >>);
$O -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $o >>);
$P -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $p >>);
$Q -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $q >>);
$R -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $r >>);
$S -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $s >>);
$T -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $t >>);
$U -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $u >>);
$V -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $v >>);
$W -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $w >>);
$X -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $x >>);
$Y -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $y >>);
$Z -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, $z >>);
C -> Function(Rest, A0, A1, A2, A3, A4, A5, << Acc/binary, C >>)
end).
-define(LOWER(Function, Rest, A0, A1, A2, A3, A4, A5, A6, Acc), case C of
$A -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $a >>);
$B -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $b >>);
$C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $c >>);
$D -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $d >>);
$E -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $e >>);
$F -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $f >>);
$G -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $g >>);
$H -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $h >>);
$I -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $i >>);
$J -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $j >>);
$K -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $k >>);
$L -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $l >>);
$M -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $m >>);
$N -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $n >>);
$O -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $o >>);
$P -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $p >>);
$Q -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $q >>);
$R -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $r >>);
$S -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $s >>);
$T -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $t >>);
$U -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $u >>);
$V -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $v >>);
$W -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $w >>);
$X -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $x >>);
$Y -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $y >>);
$Z -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, $z >>);
C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, << Acc/binary, C >>)
end).
-define(LOWER(Function, Rest, A0, A1, A2, A3, A4, A5, A6, A7, Acc), case C of
$A -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $a >>);
$B -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $b >>);
$C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $c >>);
$D -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $d >>);
$E -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $e >>);
$F -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $f >>);
$G -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $g >>);
$H -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $h >>);
$I -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $i >>);
$J -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $j >>);
$K -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $k >>);
$L -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $l >>);
$M -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $m >>);
$N -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $n >>);
$O -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $o >>);
$P -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $p >>);
$Q -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $q >>);
$R -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $r >>);
$S -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $s >>);
$T -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $t >>);
$U -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $u >>);
$V -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $v >>);
$W -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $w >>);
$X -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $x >>);
$Y -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $y >>);
$Z -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, $z >>);
C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, << Acc/binary, C >>)
end).
-define(LOWER(Function, Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, Acc), case C of
$A -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $a >>);
$B -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $b >>);
$C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $c >>);
$D -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $d >>);
$E -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $e >>);
$F -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $f >>);
$G -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $g >>);
$H -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $h >>);
$I -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $i >>);
$J -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $j >>);
$K -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $k >>);
$L -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $l >>);
$M -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $m >>);
$N -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $n >>);
$O -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $o >>);
$P -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $p >>);
$Q -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $q >>);
$R -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $r >>);
$S -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $s >>);
$T -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $t >>);
$U -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $u >>);
$V -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $v >>);
$W -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $w >>);
$X -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $x >>);
$Y -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $y >>);
$Z -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, $z >>);
C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, << Acc/binary, C >>)
end).
-define(LOWER(Function, Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, Acc), case C of
$A -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $a >>);
$B -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $b >>);
$C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $c >>);
$D -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $d >>);
$E -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $e >>);
$F -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $f >>);
$G -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $g >>);
$H -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $h >>);
$I -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $i >>);
$J -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $j >>);
$K -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $k >>);
$L -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $l >>);
$M -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $m >>);
$N -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $n >>);
$O -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $o >>);
$P -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $p >>);
$Q -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $q >>);
$R -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $r >>);
$S -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $s >>);
$T -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $t >>);
$U -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $u >>);
$V -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $v >>);
$W -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $w >>);
$X -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $x >>);
$Y -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $y >>);
$Z -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, $z >>);
C -> Function(Rest, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, << Acc/binary, C >>)
end).
-endif.
%% Copyright (c) 2015-2018, Loïc Hoguin <essen@ninenines.eu>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-ifndef(COW_PARSE_HRL).
-define(COW_PARSE_HRL, 1).
-define(IS_ALPHA(C),
(C =:= $a) or (C =:= $b) or (C =:= $c) or (C =:= $d) or (C =:= $e) or
(C =:= $f) or (C =:= $g) or (C =:= $h) or (C =:= $i) or (C =:= $j) or
(C =:= $k) or (C =:= $l) or (C =:= $m) or (C =:= $n) or (C =:= $o) or
(C =:= $p) or (C =:= $q) or (C =:= $r) or (C =:= $s) or (C =:= $t) or
(C =:= $u) or (C =:= $v) or (C =:= $w) or (C =:= $x) or (C =:= $y) or
(C =:= $z) or
(C =:= $A) or (C =:= $B) or (C =:= $C) or (C =:= $D) or (C =:= $E) or
(C =:= $F) or (C =:= $G) or (C =:= $H) or (C =:= $I) or (C =:= $J) or
(C =:= $K) or (C =:= $L) or (C =:= $M) or (C =:= $N) or (C =:= $O) or
(C =:= $P) or (C =:= $Q) or (C =:= $R) or (C =:= $S) or (C =:= $T) or
(C =:= $U) or (C =:= $V) or (C =:= $W) or (C =:= $X) or (C =:= $Y) or
(C =:= $Z)
).
-define(IS_ALPHANUM(C), ?IS_ALPHA(C) or ?IS_DIGIT(C)).
-define(IS_CHAR(C), C > 0, C < 128).
-define(IS_DIGIT(C),
(C =:= $0) or (C =:= $1) or (C =:= $2) or (C =:= $3) or (C =:= $4) or
(C =:= $5) or (C =:= $6) or (C =:= $7) or (C =:= $8) or (C =:= $9)).
-define(IS_ETAGC(C), C =:= 16#21; C >= 16#23, C =/= 16#7f).
-define(IS_HEX(C),
?IS_DIGIT(C) or
(C =:= $a) or (C =:= $b) or (C =:= $c) or
(C =:= $d) or (C =:= $e) or (C =:= $f) or
(C =:= $A) or (C =:= $B) or (C =:= $C) or
(C =:= $D) or (C =:= $E) or (C =:= $F)).
-define(IS_LHEX(C),
?IS_DIGIT(C) or
(C =:= $a) or (C =:= $b) or (C =:= $c) or
(C =:= $d) or (C =:= $e) or (C =:= $f)).
-define(IS_TOKEN(C),
?IS_ALPHA(C) or ?IS_DIGIT(C) or
(C =:= $!) or (C =:= $#) or (C =:= $$) or (C =:= $%) or (C =:= $&) or
(C =:= $') or (C =:= $*) or (C =:= $+) or (C =:= $-) or (C =:= $.) or
(C =:= $^) or (C =:= $_) or (C =:= $`) or (C =:= $|) or (C =:= $~)).
-define(IS_TOKEN68(C),
?IS_ALPHA(C) or ?IS_DIGIT(C) or
(C =:= $-) or (C =:= $.) or (C =:= $_) or
(C =:= $~) or (C =:= $+) or (C =:= $/)).
-define(IS_URI_UNRESERVED(C),
?IS_ALPHA(C) or ?IS_DIGIT(C) or
(C =:= $-) or (C =:= $.) or (C =:= $_) or (C =:= $~)).
-define(IS_URI_SUB_DELIMS(C),
(C =:= $!) or (C =:= $$) or (C =:= $&) or (C =:= $') or
(C =:= $() or (C =:= $)) or (C =:= $*) or (C =:= $+) or
(C =:= $,) or (C =:= $;) or (C =:= $=)).
-define(IS_VCHAR(C), C =:= $\t; C > 31, C < 127).
-define(IS_VCHAR_OBS(C), C =:= $\t; C > 31, C =/= 127).
-define(IS_WS(C), (C =:= $\s) or (C =:= $\t)).
-define(IS_WS_COMMA(C), ?IS_WS(C) or (C =:= $,)).
-endif.
{application, 'rabbitmq_management', [
{description, "RabbitMQ Management Console"},
{vsn, "3.7.28"},
{id, "v3.7.27-1-g1d21a2f"},
{modules, ['rabbit_mgmt_app','rabbit_mgmt_cors','rabbit_mgmt_csp','rabbit_mgmt_db','rabbit_mgmt_db_cache','rabbit_mgmt_db_cache_sup','rabbit_mgmt_dispatcher','rabbit_mgmt_extension','rabbit_mgmt_headers','rabbit_mgmt_hsts','rabbit_mgmt_load_definitions','rabbit_mgmt_reset_handler','rabbit_mgmt_stats','rabbit_mgmt_sup','rabbit_mgmt_sup_sup','rabbit_mgmt_util','rabbit_mgmt_wm_aliveness_test','rabbit_mgmt_wm_binding','rabbit_mgmt_wm_bindings','rabbit_mgmt_wm_channel','rabbit_mgmt_wm_channels','rabbit_mgmt_wm_channels_vhost','rabbit_mgmt_wm_cluster_name','rabbit_mgmt_wm_connection','rabbit_mgmt_wm_connection_channels','rabbit_mgmt_wm_connections','rabbit_mgmt_wm_connections_vhost','rabbit_mgmt_wm_consumers','rabbit_mgmt_wm_definitions','rabbit_mgmt_wm_exchange','rabbit_mgmt_wm_exchange_publish','rabbit_mgmt_wm_exchanges','rabbit_mgmt_wm_extensions','rabbit_mgmt_wm_feature_flag_enable','rabbit_mgmt_wm_feature_flags','rabbit_mgmt_wm_global_parameter','rabbit_mgmt_wm_global_parameters','rabbit_mgmt_wm_healthchecks','rabbit_mgmt_wm_limit','rabbit_mgmt_wm_limits','rabbit_mgmt_wm_login','rabbit_mgmt_wm_node','rabbit_mgmt_wm_node_memory','rabbit_mgmt_wm_node_memory_ets','rabbit_mgmt_wm_nodes','rabbit_mgmt_wm_operator_policies','rabbit_mgmt_wm_operator_policy','rabbit_mgmt_wm_overview','rabbit_mgmt_wm_parameter','rabbit_mgmt_wm_parameters','rabbit_mgmt_wm_permission','rabbit_mgmt_wm_permissions','rabbit_mgmt_wm_permissions_user','rabbit_mgmt_wm_permissions_vhost','rabbit_mgmt_wm_policies','rabbit_mgmt_wm_policy','rabbit_mgmt_wm_queue','rabbit_mgmt_wm_queue_actions','rabbit_mgmt_wm_queue_get','rabbit_mgmt_wm_queue_purge','rabbit_mgmt_wm_queues','rabbit_mgmt_wm_redirect','rabbit_mgmt_wm_reset','rabbit_mgmt_wm_static','rabbit_mgmt_wm_topic_permission','rabbit_mgmt_wm_topic_permissions','rabbit_mgmt_wm_topic_permissions_user','rabbit_mgmt_wm_topic_permissions_vhost','rabbit_mgmt_wm_user','rabbit_mgmt_wm_users','rabbit_mgmt_wm_users_bulk_delete','rabbit_mgmt_wm_vhost','rabbit_mgmt_wm_vhost_restart','rabbit_mgmt_wm_vhosts','rabbit_mgmt_wm_whoami']},
{registered, [rabbitmq_management_sup]},
{applications, [kernel,stdlib,mnesia,ranch,ssl,crypto,public_key,rabbit_common,rabbit,amqp_client,cowboy,cowlib,rabbitmq_web_dispatch,rabbitmq_management_agent]},
{mod, {rabbit_mgmt_app, []}},
{env, [
{http_log_dir, none},
{load_definitions, none},
{management_db_cache_multiplier, 5},
{process_stats_gc_timeout, 300000},
{stats_event_max_backlog, 250},
{cors_allow_origins, []},
{cors_max_age, 1800},
{content_security_policy, "script-src 'self' 'unsafe-eval' 'unsafe-inline'; object-src 'self'"}
]},
{broker_version_requirements, []}
]}.
\ No newline at end of file
%% The contents of this file are subject to the Mozilla Public License
%% Version 1.1 (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License at
%% https://www.mozilla.org/MPL/
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
%% License for the specific language governing rights and limitations
%% under the License.
%%
%% The Original Code is RabbitMQ Management Console.
%%
%% The Initial Developer of the Original Code is GoPivotal, Inc.
%% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
%%
-define(AUTH_REALM, "Basic realm=\"RabbitMQ Management\"").
%% ----------------------------------------------------------------------------
%% RabbitMQ Management Plugin
%%
%% See https://www.rabbitmq.com/management.html for details
%% ----------------------------------------------------------------------------
%% Load definitions from a JSON file or directory of files. See
%% https://www.rabbitmq.com/management.html#load-definitions
%%
%% {load_definitions, "/path/to/schema.json"},
%% {load_definitions, "/path/to/schemas"},
{mapping, "management.load_definitions", "rabbitmq_management.load_definitions",
[{datatype, string},
{validators, ["file_accessible"]}]}.
%% Log all requests to the management HTTP API to a file.
%%
%% {http_log_dir, "/path/to/access.log"},
{mapping, "management.http_log_dir", "rabbitmq_management.http_log_dir",
[{datatype, string}]}.
%% HTTP (TCP) listener options ========================================================
%% HTTP listener consistent with Web STOMP and Web MQTT.
%%
%% {tcp_config, [{port, 15672},
%% {ip, "127.0.0.1"}]}
{mapping, "management.tcp.port", "rabbitmq_management.tcp_config.port",
[{datatype, integer}]}.
{mapping, "management.tcp.ip", "rabbitmq_management.tcp_config.ip",
[{datatype, string},
{validators, ["is_ip"]}]}.
{mapping, "management.tcp.compress", "rabbitmq_management.tcp_config.cowboy_opts.compress",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.tcp.idle_timeout", "rabbitmq_management.tcp_config.cowboy_opts.idle_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.tcp.inactivity_timeout", "rabbitmq_management.tcp_config.cowboy_opts.inactivity_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.tcp.request_timeout", "rabbitmq_management.tcp_config.cowboy_opts.request_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.tcp.shutdown_timeout", "rabbitmq_management.tcp_config.cowboy_opts.shutdown_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.tcp.max_keepalive", "rabbitmq_management.tcp_config.cowboy_opts.max_keepalive",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
%% HTTPS (TLS) listener options ========================================================
%% HTTPS listener consistent with Web STOMP and Web MQTT.
%%
%% {ssl_config, [{port, 15671},
%% {ip, "127.0.0.1"},
%% {cacertfile, "/path/to/cacert.pem"},
%% {certfile, "/path/to/cert.pem"},
%% {keyfile, "/path/to/key.pem"}]}
{mapping, "management.ssl.port", "rabbitmq_management.ssl_config.port",
[{datatype, integer}]}.
{mapping, "management.ssl.backlog", "rabbitmq_management.ssl_config.backlog",
[{datatype, integer}]}.
{mapping, "management.ssl.ip", "rabbitmq_management.ssl_config.ip",
[{datatype, string}, {validators, ["is_ip"]}]}.
{mapping, "management.ssl.certfile", "rabbitmq_management.ssl_config.certfile",
[{datatype, string}, {validators, ["file_accessible"]}]}.
{mapping, "management.ssl.keyfile", "rabbitmq_management.ssl_config.keyfile",
[{datatype, string}, {validators, ["file_accessible"]}]}.
{mapping, "management.ssl.cacertfile", "rabbitmq_management.ssl_config.cacertfile",
[{datatype, string}, {validators, ["file_accessible"]}]}.
{mapping, "management.ssl.password", "rabbitmq_management.ssl_config.password",
[{datatype, string}]}.
{mapping, "management.ssl.verify", "rabbitmq_management.ssl_config.verify", [
{datatype, {enum, [verify_peer, verify_none]}}]}.
{mapping, "management.ssl.fail_if_no_peer_cert", "rabbitmq_management.ssl_config.fail_if_no_peer_cert", [
{datatype, {enum, [true, false]}}]}.
{mapping, "management.ssl.honor_cipher_order", "rabbitmq_management.ssl_config.honor_cipher_order",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.ssl.honor_ecc_order", "rabbitmq_management.ssl_config.honor_ecc_order",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.ssl.reuse_sessions", "rabbitmq_management.ssl_config.reuse_sessions",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.ssl.secure_renegotiate", "rabbitmq_management.ssl_config.secure_renegotiate",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.ssl.client_renegotiation", "rabbitmq_management.ssl_config.client_renegotiation",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.ssl.depth", "rabbitmq_management.ssl_config.depth",
[{datatype, integer}, {validators, ["byte"]}]}.
{mapping, "management.ssl.versions.$version", "rabbitmq_management.ssl_config.versions",
[{datatype, atom}]}.
{translation, "rabbitmq_management.ssl_config.versions",
fun(Conf) ->
Settings = cuttlefish_variable:filter_by_prefix("management.ssl.versions", Conf),
[V || {_, V} <- Settings]
end}.
{mapping, "management.ssl.ciphers.$cipher", "rabbitmq_management.ssl_config.ciphers",
[{datatype, string}]}.
{translation, "rabbitmq_management.ssl_config.ciphers",
fun(Conf) ->
Settings = cuttlefish_variable:filter_by_prefix("management.ssl.ciphers", Conf),
lists:reverse([V || {_, V} <- Settings])
end}.
{mapping, "management.ssl.compress", "rabbitmq_management.ssl_config.cowboy_opts.compress",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.ssl.idle_timeout", "rabbitmq_management.ssl_config.cowboy_opts.idle_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.ssl.inactivity_timeout", "rabbitmq_management.ssl_config.cowboy_opts.inactivity_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.ssl.request_timeout", "rabbitmq_management.ssl_config.cowboy_opts.request_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.ssl.shutdown_timeout", "rabbitmq_management.ssl_config.cowboy_opts.shutdown_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.ssl.max_keepalive", "rabbitmq_management.ssl_config.cowboy_opts.max_keepalive",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
%% Legacy listener options ========================================================
%% Legacy (pre-3.7.9) TCP listener format.
%%
%% {listener, [{port, 12345},
%% {ip, "127.0.0.1"},
%% {ssl, true},
%% {ssl_opts, [{cacertfile, "/path/to/cacert.pem"},
%% {certfile, "/path/to/cert.pem"},
%% {keyfile, "/path/to/key.pem"}]}]},
{mapping, "management.listener.port", "rabbitmq_management.listener.port",
[{datatype, integer}]}.
{mapping, "management.listener.ip", "rabbitmq_management.listener.ip",
[{datatype, string},
{validators, ["is_ip"]}]}.
{mapping, "management.listener.ssl", "rabbitmq_management.listener.ssl",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.listener.server.compress", "rabbitmq_management.listener.cowboy_opts.compress",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.listener.server.idle_timeout", "rabbitmq_management.listener.cowboy_opts.idle_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.listener.server.inactivity_timeout", "rabbitmq_management.listener.cowboy_opts.inactivity_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.listener.server.request_timeout", "rabbitmq_management.listener.cowboy_opts.request_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.listener.server.shutdown_timeout", "rabbitmq_management.listener.cowboy_opts.shutdown_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.listener.server.max_keepalive", "rabbitmq_management.listener.cowboy_opts.max_keepalive",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
%% Legacy HTTPS listener options ========================================================
{mapping, "management.listener.ssl_opts", "rabbitmq_management.listener.ssl_opts", [
{datatype, {enum, [none]}}
]}.
{translation, "rabbitmq_management.listener.ssl_opts",
fun(Conf) ->
case cuttlefish:conf_get("management.listener.ssl_opts", Conf, undefined) of
none -> [];
_ -> cuttlefish:invalid("Invalid management.listener.ssl_opts")
end
end}.
{mapping, "management.listener.ssl_opts.verify", "rabbitmq_management.listener.ssl_opts.verify", [
{datatype, {enum, [verify_peer, verify_none]}}]}.
{mapping, "management.listener.ssl_opts.fail_if_no_peer_cert", "rabbitmq_management.listener.ssl_opts.fail_if_no_peer_cert", [
{datatype, {enum, [true, false]}}]}.
{mapping, "management.listener.ssl_opts.cacertfile", "rabbitmq_management.listener.ssl_opts.cacertfile",
[{datatype, string}, {validators, ["file_accessible"]}]}.
{mapping, "management.listener.ssl_opts.certfile", "rabbitmq_management.listener.ssl_opts.certfile",
[{datatype, string}, {validators, ["file_accessible"]}]}.
{mapping, "management.listener.ssl_opts.cacerts.$name", "rabbitmq_management.listener.ssl_opts.cacerts",
[{datatype, string}]}.
{translation, "rabbitmq_management.listener.ssl_opts.cacerts",
fun(Conf) ->
Settings = cuttlefish_variable:filter_by_prefix("management.listener.ssl_opts.cacerts", Conf),
[ list_to_binary(V) || {_, V} <- Settings ]
end}.
{mapping, "management.listener.ssl_opts.honor_cipher_order", "rabbitmq_management.listener.ssl_opts.honor_cipher_order",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.listener.ssl_opts.honor_ecc_order", "rabbitmq_management.listener.ssl_opts.honor_ecc_order",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.listener.ssl_opts.reuse_sessions", "rabbitmq_management.listener.ssl_opts.reuse_sessions",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.listener.ssl_opts.secure_renegotiate", "rabbitmq_management.listener.ssl_opts.secure_renegotiate",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.listener.ssl_opts.client_renegotiation", "rabbitmq_management.listener.ssl_opts.client_renegotiation",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.listener.ssl_opts.versions.$version", "rabbitmq_management.listener.ssl_opts.versions",
[{datatype, atom}]}.
{translation, "rabbitmq_management.listener.ssl_opts.versions",
fun(Conf) ->
Settings = cuttlefish_variable:filter_by_prefix("management.listener.ssl_opts.versions", Conf),
[ V || {_, V} <- Settings ]
end}.
{mapping, "management.listener.ssl_opts.cert", "rabbitmq_management.listener.ssl_opts.cert",
[{datatype, string}]}.
{translation, "rabbitmq_management.listener.ssl_opts.cert",
fun(Conf) ->
list_to_binary(cuttlefish:conf_get("management.listener.ssl_opts.cert", Conf))
end}.
{mapping, "management.listener.ssl_opts.crl_check", "rabbitmq_management.listener.ssl_opts.crl_check",
[{datatype, [{enum, [true, false, peer, best_effort]}]}]}.
{mapping, "management.listener.ssl_opts.depth", "rabbitmq_management.listener.ssl_opts.depth",
[{datatype, integer}, {validators, ["byte"]}]}.
{mapping, "management.listener.ssl_opts.dh", "rabbitmq_management.listener.ssl_opts.dh",
[{datatype, string}]}.
{translation, "rabbitmq_management.listener.ssl_opts.dh",
fun(Conf) ->
list_to_binary(cuttlefish:conf_get("management.listener.ssl_opts.dh", Conf))
end}.
{mapping, "management.listener.ssl_opts.dhfile", "rabbitmq_management.listener.ssl_opts.dhfile",
[{datatype, string}, {validators, ["file_accessible"]}]}.
{mapping, "management.listener.ssl_opts.key.RSAPrivateKey", "rabbitmq_management.listener.ssl_opts.key",
[{datatype, string}]}.
{mapping, "management.listener.ssl_opts.key.DSAPrivateKey", "rabbitmq_management.listener.ssl_opts.key",
[{datatype, string}]}.
{mapping, "management.listener.ssl_opts.key.PrivateKeyInfo", "rabbitmq_management.listener.ssl_opts.key",
[{datatype, string}]}.
{translation, "rabbitmq_management.listener.ssl_opts.key",
fun(Conf) ->
case cuttlefish_variable:filter_by_prefix("management.listener.ssl_opts.key", Conf) of
[{[_,_,Key], Val}|_] -> {list_to_atom(Key), list_to_binary(Val)};
_ -> undefined
end
end}.
{mapping, "management.listener.ssl_opts.keyfile", "rabbitmq_management.listener.ssl_opts.keyfile",
[{datatype, string}, {validators, ["file_accessible"]}]}.
{mapping, "management.listener.ssl_opts.log_alert", "rabbitmq_management.listener.ssl_opts.log_alert",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.listener.ssl_opts.password", "rabbitmq_management.listener.ssl_opts.password",
[{datatype, string}]}.
{mapping, "management.listener.ssl_opts.psk_identity", "rabbitmq_management.listener.ssl_opts.psk_identity",
[{datatype, string}]}.
%% A custom path prefix for all HTTP request handlers.
%%
%% {path_prefix, "/a/prefix"},
{mapping, "management.path_prefix", "rabbitmq_management.path_prefix",
[{datatype, string}]}.
%% Login session timeout in minutes
{mapping, "management.login_session_timeout", "rabbitmq_management.login_session_timeout", [
{datatype, integer}, {validators, ["non_negative_integer"]}
]}.
%% CORS
{mapping, "management.cors.allow_origins", "rabbitmq_management.cors_allow_origins", [
{datatype, {enum, [none]}}
]}.
{mapping, "management.cors.allow_origins.$name", "rabbitmq_management.cors_allow_origins", [
{datatype, string}
]}.
{translation, "rabbitmq_management.cors_allow_origins",
fun(Conf) ->
case cuttlefish:conf_get("management.cors.allow_origins", Conf, undefined) of
none -> [];
_ ->
Settings = cuttlefish_variable:filter_by_prefix("management.cors.allow_origins", Conf),
[V || {_, V} <- Settings]
end
end}.
{mapping, "management.cors.max_age", "rabbitmq_management.cors_max_age", [
{datatype, integer}, {validators, ["non_negative_integer"]}
]}.
{translation, "rabbitmq_management.cors_max_age",
fun(Conf) ->
case cuttlefish:conf_get("management.cors.max_age", Conf, undefined) of
undefined -> cuttlefish:unset();
Value -> Value
end
end}.
%% CSP (https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
{mapping, "management.csp.policy", "rabbitmq_management.content_security_policy", [
{datatype, string}
]}.
{translation, "rabbitmq_management.content_security_policy",
fun(Conf) ->
case cuttlefish:conf_get("management.csp.policy", Conf, undefined) of
undefined -> cuttlefish:unset();
Value -> Value
end
end}.
%% HSTS (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security)
{mapping, "management.hsts.policy", "rabbitmq_management.strict_transport_security", [
{datatype, string}
]}.
{translation, "rabbitmq_management.strict_transport_security",
fun(Conf) ->
case cuttlefish:conf_get("management.hsts.policy", Conf, undefined) of
undefined -> cuttlefish:unset();
Value -> Value
end
end}.
%% ===========================================================================
%% One of 'basic', 'detailed' or 'none'. See
%% https://www.rabbitmq.com/management.html#fine-stats for more details.
%% {rates_mode, basic},
{mapping, "management.rates_mode", "rabbitmq_management.rates_mode",
[{datatype, {enum, [basic, detailed, none]}}]}.
%% Configure how long aggregated data (such as message rates and queue
%% lengths) is retained. Please read the plugin's documentation in
%% https://www.rabbitmq.com/management.html#configuration for more
%% details.
%%
%% {sample_retention_policies,
%% [{global, [{60, 5}, {3600, 60}, {86400, 1200}]},
%% {basic, [{60, 5}, {3600, 60}]},
%% {detailed, [{10, 5}]}]}
% ]},
{mapping, "management.sample_retention_policies.$section.$interval",
"rabbitmq_management.sample_retention_policies",
[{datatype, integer}]}.
{translation, "rabbitmq_management.sample_retention_policies",
fun(Conf) ->
Global = cuttlefish_variable:filter_by_prefix("management.sample_retention_policies.global", Conf),
Basic = cuttlefish_variable:filter_by_prefix("management.sample_retention_policies.basic", Conf),
Detailed = cuttlefish_variable:filter_by_prefix("management.sample_retention_policies.detailed", Conf),
TranslateKey = fun("minute") -> 60;
("hour") -> 3600;
("day") -> 86400;
(Other) -> list_to_integer(Other)
end,
TranslatePolicy = fun(Section) ->
[ {TranslateKey(Key), Val} || {[_,_,_,Key], Val} <- Section ]
end,
[{global, TranslatePolicy(Global)},
{basic, TranslatePolicy(Basic)},
{detailed, TranslatePolicy(Detailed)}]
end}.
{validator, "is_dir", "is not directory",
fun(File) ->
ReadFile = file:list_dir(File),
element(1, ReadFile) == ok
end}.
<html>
<head>
<title>RabbitMQ Management HTTP API</title>
<style>
body { font: 12px Verdana,sans-serif; color: #444; padding: 8px 35px; }
td, th { font: 12px Verdana,sans-serif; color: #444; }
h1 { font-size: 2em; }
h2 { font-size: 1.5em; }
td.path { font-family: monospace; }
th { font-size 1em; font-weight: bold; }
table { border-collapse: collapse; }
table th, table td { vertical-align: top; border: 1px solid #bbb; padding: 5px; }
code { background: #ffa; }
pre { background: black; color: #0f0; padding: 10px; word-wrap: break-word;}
table pre { background: #ffa; color: black; }
</style>
</head>
<body>
<h1>RabbitMQ Management HTTP API</h1>
<h2>Introduction</h2>
<p>Apart from this help page, all URIs will serve only resources
of type <code>application/json</code>, and will require HTTP basic
authentication (using the standard RabbitMQ user database). The
default user is guest/guest.</p>
<p>Many URIs require the name of a virtual host as part of the
path, since names only uniquely identify objects within a virtual
host. As the default virtual host is called "<code>/</code>", this
will need to be encoded as "<code>%2F</code>".</p>
<p>PUTing a resource creates it. The JSON object you upload must
have certain mandatory keys (documented below) and may have
optional keys. Other keys are ignored. Missing mandatory keys
constitute an error.</p>
<p>Since bindings do not have names or IDs in AMQP we synthesise
one based on all its properties. Since predicting this name is
hard in the general case, you can also create bindings by POSTing
to a factory URI. See the example below.</p>
<p>Many URIs return lists. Such URIs can have the query string
parameters <code>sort</code> and <code>sort_reverse</code>
added. <code>sort</code> allows you to select a primary field to
sort by, and <code>sort_reverse</code> will reverse the sort order
if set to <code>true</code>. The <code>sort</code> parameter can
contain subfields separated by dots. This allows you to sort by a
nested component of the listed items; it does not allow you to
sort by more than one field. See the example below.</p>
<p>You can also restrict what information is returned per item
with the <code>columns</code> parameter. This is a comma-separated
list of subfields separated by dots. See the example below.</p>
<p>Most of the GET queries return many fields per
object. The second part of this guide covers those.</p>
<h2>Examples</h2>
<p>A few quick examples for Windows and Unix, using the command line
tool <code>curl</code>:</p>
<ul>
<li>
Get a list of vhosts:
<pre>:: Windows
C:\&gt; curl -i -u guest:guest http://localhost:15672/api/vhosts
# Unix
$ curl -i -u guest:guest http://localhost:15672/api/vhosts
HTTP/1.1 200 OK
Server: MochiWeb/1.1 WebMachine/1.10.0 (never breaks eye contact)
Date: Mon, 16 Sep 2013 12:00:02 GMT
Content-Type: application/json
Content-Length: 30
[{"name":"/","tracing":false}]
</pre>
</li>
<li>
Get a list of channels, fast publishers first, restricting the info
items we get back:
<pre>:: Windows
C:\&gt; curl -i -u guest:guest "http://localhost:15672/api/channels?sort=message_stats.publish_details.rate&amp;sort_reverse=true&amp;columns=name,message_stats.publish_details.rate,message_stats.deliver_get_details.rate"
# Unix
$ curl -i -u guest:guest 'http://localhost:15672/api/channels?sort=message_stats.publish_details.rate&amp;sort_reverse=true&amp;columns=name,message_stats.publish_details.rate,message_stats.deliver_get_details.rate'
HTTP/1.1 200 OK
Server: MochiWeb/1.1 WebMachine/1.10.0 (never breaks eye contact)
Date: Mon, 16 Sep 2013 12:01:17 GMT
Content-Type: application/json
Content-Length: 219
Cache-Control: no-cache
[{"message_stats":{"publish_details":{"rate" <i>... (remainder elided)</i></pre>
</li>
<li>
Create a new vhost:
<pre>:: Windows
C:\&gt; curl -i -u guest:guest -H "content-type:application/json" ^
-XPUT http://localhost:15672/api/vhosts/foo
# Unix
$ curl -i -u guest:guest -H "content-type:application/json" \
-XPUT http://localhost:15672/api/vhosts/foo
HTTP/1.1 204 No Content
Server: MochiWeb/1.1 WebMachine/1.10.0 (never breaks eye contact)
Date: Mon, 16 Sep 2013 12:03:00 GMT
Content-Type: application/json
Content-Length: 0</pre>
<p>Note: you must specify <code>application/json</code> as the
mime type.</p>
<p>Note: the name of the object is not needed in the JSON
object uploaded, since it is in the URI. As a virtual host
has no properties apart from its name, this means you do not
need to specify a body at all!</p>
</li>
<li>
Create a new exchange in the default virtual host:
<pre>:: Windows
C:\&gt; curl -i -u guest:guest -H "content-type:application/json" ^
-XPUT -d"{""type"":""direct"",""durable"":true}" ^
http://localhost:15672/api/exchanges/%2F/my-new-exchange
# Unix
$ curl -i -u guest:guest -H "content-type:application/json" \
-XPUT -d'{"type":"direct","durable":true}' \
http://localhost:15672/api/exchanges/%2F/my-new-exchange
HTTP/1.1 204 No Content
Server: MochiWeb/1.1 WebMachine/1.10.0 (never breaks eye contact)
Date: Mon, 16 Sep 2013 12:04:00 GMT
Content-Type: application/json
Content-Length: 0</pre>
<p>Note: we never return a body in response to a PUT or
DELETE, unless it fails.</p>
</li>
<li>
And delete it again:
<pre>:: Windows
C:\&gt; curl -i -u guest:guest -H "content-type:application/json" ^
-XDELETE http://localhost:15672/api/exchanges/%2F/my-new-exchange
# Unix
$ curl -i -u guest:guest -H "content-type:application/json" \
-XDELETE http://localhost:15672/api/exchanges/%2F/my-new-exchange
HTTP/1.1 204 No Content
Server: MochiWeb/1.1 WebMachine/1.10.0 (never breaks eye contact)
Date: Mon, 16 Sep 2013 12:05:30 GMT
Content-Type: application/json
Content-Length: 0</pre>
</li>
</ul>
<h2>Reference</h2>
<table>
<tr>
<th>GET</th>
<th>PUT</th>
<th>DELETE</th>
<th>POST</th>
<th>Path</th>
<th>Description</th>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/overview</td>
<td>Various random bits of information that describe the whole
system.</td>
</tr>
<tr>
<td>X</td>
<td>X</td>
<td></td>
<td></td>
<td class="path">/api/cluster-name</td>
<td>Name identifying this RabbitMQ cluster.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/nodes</td>
<td>A list of nodes in the RabbitMQ cluster.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/nodes/<i>name</i></td>
<td>
An individual node in the RabbitMQ cluster. Add
"?memory=true" to get memory statistics, and "?binary=true"
to get a breakdown of binary memory use (may be expensive if
there are many small binaries in the system).
</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/extensions</td>
<td>A list of extensions to the management plugin.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td>X</td>
<td class="path">/api/definitions<br/>
/api/all-configuration <em>(deprecated)</em>
</td>
<td>
The server definitions - exchanges, queues, bindings, users,
virtual hosts, permissions, topic permissions, and parameters. Everything apart from
messages. POST to upload an existing set of definitions. Note
that:
<ul>
<li>
The definitions are merged. Anything already existing on
the server but not in the uploaded definitions is
untouched.
</li>
<li>
Conflicting definitions on immutable objects (exchanges,
queues and bindings) will cause an error.
</li>
<li>
Conflicting definitions on mutable objects will cause
the object in the server to be overwritten with the
object from the definitions.
</li>
<li>
In the event of an error you will be left with a
part-applied set of definitions.
</li>
</ul>
For convenience you may upload a file from a browser to this
URI (i.e. you can use <code>multipart/form-data</code> as
well as <code>application/json</code>) in which case the
definitions should be uploaded as a form field named
"file".
</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td>X</td>
<td class="path">/api/definitions/<i>vhost</i><br/>
</td>
<td>
The server definitions for a given virtual host -
exchanges, queues, bindings and policies.
POST to upload an existing set of definitions. Note that:
<ul>
<li>
The definitions are merged. Anything already existing on
the server but not in the uploaded definitions is
untouched.
</li>
<li>
Conflicting definitions on immutable objects (exchanges,
queues and bindings) will cause an error.
</li>
<li>
Conflicting definitions on mutable objects will cause
the object in the server to be overwritten with the
object from the definitions.
</li>
<li>
In the event of an error you will be left with a
part-applied set of definitions.
</li>
</ul>
For convenience you may upload a file from a browser to this
URI (i.e. you can use <code>multipart/form-data</code> as
well as <code>application/json</code>) in which case the
definitions should be uploaded as a form field named
"file".
</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/connections</td>
<td>A list of all open connections.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/vhosts/<i>vhost</i>/connections</td>
<td>A list of all open connections in a specific vhost.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td>X</td>
<td></td>
<td class="path">/api/connections/<i>name</i></td>
<td>
An individual connection. DELETEing it will close the
connection. Optionally set the "X-Reason" header when
DELETEing to provide a reason.
</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/connections/<i>name</i>/channels</td>
<td>
List of all channels for a given connection.
</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/channels</td>
<td>A list of all open channels.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/vhosts/<i>vhost</i>/channels</td>
<td>A list of all open channels in a specific vhost.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/channels/<i>channel</i></td>
<td>Details about an individual channel.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/consumers</td>
<td>A list of all consumers.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/consumers/<i>vhost</i></td>
<td>A list of all consumers in a given virtual host.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/exchanges</td>
<td>A list of all exchanges.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/exchanges/<i>vhost</i></td>
<td>A list of all exchanges in a given virtual host.</td>
</tr>
<tr>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td class="path">/api/exchanges/<i>vhost</i>/<i>name</i></td>
<td>
An individual exchange. To PUT an exchange, you will need a body looking something like this:
<pre>{"type":"direct","auto_delete":false,"durable":true,"internal":false,"arguments":{}}</pre>
The <code>type</code> key is mandatory; other keys are optional.
<p>
When DELETEing an exchange you can add the query string
parameter <code>if-unused=true</code>. This prevents the
delete from succeeding if the exchange is bound to a queue
or as a source to another exchange.
</p>
</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/exchanges/<i>vhost</i>/<i>name</i>/bindings/source</td>
<td>A list of all bindings in which a given exchange is the source.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/exchanges/<i>vhost</i>/<i>name</i>/bindings/destination</td>
<td>A list of all bindings in which a given exchange is the destination.</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td>X</td>
<td class="path">/api/exchanges/<i>vhost</i>/<i>name</i>/publish</td>
<td>
Publish a message to a given exchange. You will need a body
looking something like:
<pre>{"properties":{},"routing_key":"my key","payload":"my body","payload_encoding":"string"}</pre>
All keys are mandatory. The <code>payload_encoding</code>
key should be either "string" (in which case the payload
will be taken to be the UTF-8 encoding of the payload field)
or "base64" (in which case the payload field is taken to be
base64 encoded).<br/>
If the message is published successfully, the response will
look like:
<pre>{"routed": true}</pre>
<code>routed</code> will be true if the message was sent to
at least one queue.
<p>
Please note that the HTTP API is not ideal for high
performance publishing; the need to create a new TCP
connection for each message published can limit message
throughput compared to AMQP or other protocols using
long-lived connections.
</p>
</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/queues</td>
<td>A list of all queues.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/queues/<i>vhost</i></td>
<td>A list of all queues in a given virtual host.</td>
</tr>
<tr>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td class="path">/api/queues/<i>vhost</i>/<i>name</i></td>
<td>
An individual queue. To PUT a queue, you will need a body looking something like this:
<pre>{"auto_delete":false,"durable":true,"arguments":{},"node":"rabbit@smacmullen"}</pre>
All keys are optional.
<p>
When DELETEing a queue you can add the query string
parameters <code>if-empty=true</code> and /
or <code>if-unused=true</code>. These prevent the delete
from succeeding if the queue contains messages, or has
consumers, respectively.
</p>
</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/queues/<i>vhost</i>/<i>name</i>/bindings</td>
<td>A list of all bindings on a given queue.</td>
</tr>
<tr>
<td></td>
<td></td>
<td>X</td>
<td></td>
<td class="path">/api/queues/<i>vhost</i>/<i>name</i>/contents</td>
<td>Contents of a queue. DELETE to purge. Note you can't GET this.</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td>X</td>
<td class="path">/api/queues/<i>vhost</i>/<i>name</i>/actions</td>
<td>
Actions that can be taken on a queue. POST a body like:
<pre>{"action":"sync"}</pre> Currently the actions which are
supported are <code>sync</code> and <code>cancel_sync</code>.
</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td>X</td>
<td class="path">/api/queues/<i>vhost</i>/<i>name</i>/get</td>
<td>
Get messages from a queue. (This is not an HTTP GET as it
will alter the state of the queue.) You should post a body looking like:
<pre>{"count":5,"ackmode":"ack_requeue_true","encoding":"auto","truncate":50000}</pre>
<ul>
<li><code>count</code> controls the maximum number of
messages to get. You may get fewer messages than this if
the queue cannot immediately provide them.</li>
<li><code>ackmode</code> determines whether the messages will be
removed from the queue. If ackmode is ack_requeue_true or reject_requeue_true they will be requeued -
if ackmode is ack_requeue_false or reject_requeue_false they will be removed.
<li><code>encoding</code> must be either "auto" (in which case the
payload will be returned as a string if it is valid UTF-8, and
base64 encoded otherwise), or "base64" (in which case the payload
will always be base64 encoded).</li>
<li>If <code>truncate</code> is present it will truncate the
message payload if it is larger than the size given (in bytes).</li>
</ul>
<p><code>truncate</code> is optional; all other keys are mandatory.</p>
<p>
Please note that the get path in the HTTP API is intended
for diagnostics etc - it does not implement reliable
delivery and so should be treated as a sysadmin's tool
rather than a general API for messaging.
</p>
</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/bindings</td>
<td>A list of all bindings.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/bindings/<i>vhost</i></td>
<td>A list of all bindings in a given virtual host.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td>X</td>
<td class="path">/api/bindings/<i>vhost</i>/e/<i>exchange</i>/q/<i>queue</i></td>
<td>
<p>
A list of all bindings between an exchange and a
queue. Remember, an exchange and a queue can be bound
together many times!
</p>
<p>
To create a new binding, POST to this
URI. Request body should be a JSON object optionally containing
two fields, <code>routing_key</code> (a string) and <code>arguments</code> (a map of optional arguments):
<pre>{"routing_key":"my_routing_key", "arguments":{"x-arg": "value"}}</pre>
All keys are optional.
The response will contain a <code>Location</code> header
telling you the URI of your new binding.
</p>
</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td>X</td>
<td></td>
<td class="path">/api/bindings/<i>vhost</i>/e/<i>exchange</i>/q/<i>queue</i>/<i>props</i></td>
<td>An individual binding between an exchange and a queue.
The <i>props</i> part of the URI is a "name" for the binding
composed of its routing key and a hash of its
arguments. <i>props</i> is the field named "properties_key"
from a bindings listing response.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td>X</td>
<td class="path">/api/bindings/<i>vhost</i>/e/<i>source</i>/e/<i>destination</i></td>
<td>
<p>
A list of all bindings between two exchanges, similar to
the list of all bindings between an exchange and a queue,
above.
</p>
<p>
<p>
To create a new binding, POST to this
URI. Request body should be a JSON object optionally containing
two fields, <code>routing_key</code> (a string) and <code>arguments</code> (a map of optional arguments):
<pre>{"routing_key":"my_routing_key", "arguments":{"x-arg": "value"}}</pre>
All keys are optional.
The response will contain a <code>Location</code> header
telling you the URI of your new binding.
</p>
</p>
</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td>X</td>
<td></td>
<td class="path">/api/bindings/<i>vhost</i>/e/<i>source</i>/e/<i>destination</i>/<i>props</i></td>
<td>
An individual binding between two exchanges. Similar to
the individual binding between an exchange and a queue,
above.
</tD>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/vhosts</td>
<td>A list of all vhosts.</td>
</tr>
<tr>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td class="path">/api/vhosts/<i>name</i></td>
<td>An individual virtual host. As a virtual host usually only
has a name, you do not need an HTTP body when PUTing one of
these. To enable / disable tracing, provide a body looking like:
<pre>{"tracing":true}</pre></td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/vhosts/<i>name</i>/permissions</td>
<td>A list of all permissions for a given virtual host.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/vhosts/<i>name</i>/topic-permissions</td>
<td>A list of all topic permissions for a given virtual host.</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td>X</td>
<td class="path">/api/vhosts/<i>name</i>/start/<i>node</i></td>
<td>Starts virtual host <i>name</i> on node <i>node</i>.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/users/</td>
<td>A list of all users.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/users/without-permissions</td>
<td>A list of users that do not have access to any virtual host.</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td>X</td>
<td class="path">/api/users/bulk-delete</td>
<td>Bulk deletes a list of users. Request body must contain the list:
<pre>{"users" : ["user1", "user2", "user3"]}</pre></td>
</tr>
<tr>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td class="path">/api/users/<i>name</i></td>
<td>An individual user. To PUT a user, you will need a body looking something like this:
<pre>{"password":"secret","tags":"administrator"}</pre>
or:
<pre>{"password_hash":"2lmoth8l4H0DViLaK9Fxi6l9ds8=", "tags":"administrator"}</pre>
The <code>tags</code> key is mandatory. Either
<code>password</code> or <code>password_hash</code>
can be set. If neither are set the user will not be able to log in with a password,
but other mechanisms like client certificates may be used.
Setting <code>password_hash</code> to <code>""</code> will ensure the
user cannot use a password to log in. <code>tags</code> is a
comma-separated list of tags for the user. Currently recognised tags
are <code>administrator</code>, <code>monitoring</code> and <code>management</code>.
<code>password_hash</code> must be generated using the algorithm described
<a href="https://rabbitmq.com/passwords.html#computing-password-hash">here</a>.
You may also specify the hash function being used by adding the <code>hashing_algorithm</code>
key to the body. Currently recognised algorithms are <code>rabbit_password_hashing_sha256</code>,
<code>rabbit_password_hashing_sha512</code>, and <code>rabbit_password_hashing_md5</code>.
</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/users/<i>user</i>/permissions</td>
<td>A list of all permissions for a given user.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/users/<i>user</i>/topic-permissions</td>
<td>A list of all topic permissions for a given user.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/whoami</td>
<td>Details of the currently authenticated user.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/permissions</td>
<td>A list of all permissions for all users.</td>
</tr>
<tr>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td class="path">/api/permissions/<i>vhost</i>/<i>user</i></td>
<td>An individual permission of a user and virtual host. To PUT a permission, you will need a body looking something like this:
<pre>{"configure":".*","write":".*","read":".*"}</pre>
All keys are mandatory.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/topic-permissions</td>
<td>A list of all topic permissions for all users.</td>
</tr>
<tr>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td class="path">/api/topic-permissions/<i>vhost</i>/<i>user</i></td>
<td>Topic permissions for a user and virtual host. To PUT a topic permission, you will need a body looking something like this:
<pre>{"exchange":"amq.topic","write":"^a","read":".*"}</pre>
All keys are mandatory.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/parameters</td>
<td>A list of all vhost-scoped parameters.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/parameters/<i>component</i></td>
<td>A list of all vhost-scoped parameters for a given component.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/parameters/<i>component</i>/<i>vhost</i></td>
<td>A list of all vhost-scoped parameters for a given component and virtual host.</td>
</tr>
<tr>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td class="path">/api/parameters/<i>component</i>/<i>vhost</i>/<i>name</i></td>
<td>An individual vhost-scoped parameter. To PUT a parameter, you will need a body looking something like this:
<pre>{"vhost": "/","component":"federation","name":"local_username","value":"guest"}</pre>
</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/global-parameters</td>
<td>A list of all global parameters.</td>
</tr>
<tr>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td class="path">/api/global-parameters/<i>name</i></td>
<td>An individual global parameter. To PUT a parameter, you will need a body looking something like this:
<pre>{"name":"user_vhost_mapping","value":{"guest":"/","rabbit":"warren"}}</pre>
</td>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/policies</td>
<td>A list of all policies.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/policies/<i>vhost</i></td>
<td>A list of all policies in a given virtual host.</td>
</tr>
<tr>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td class="path">/api/policies/<i>vhost</i>/<i>name</i></td>
<td>
An individual policy. To PUT a policy, you will need a body looking something like this:
<pre>{"pattern":"^amq.", "definition": {"federation-upstream-set":"all"}, "priority":0, "apply-to": "all"}</pre>
<code>pattern</code> and <code>definition</code> are mandatory, <code>priority</code> and <code>apply-to</code> are optional.
</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/operator-policies</td>
<td>A list of all operator policy overrides.</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/operator-policies/<i>vhost</i></td>
<td>A list of all operator policy overrides in a given virtual host.</td>
</tr>
<tr>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td class="path">/api/operator-policies/<i>vhost</i>/<i>name</i></td>
<td>
An individual operator policy. To PUT a policy, you will need a body looking something like this:
<pre>{"pattern":"^amq.", "definition": {"expires":100}, "priority":0, "apply-to": "queues"}</pre>
<code>pattern</code> and <code>definition</code> are mandatory, <code>priority</code> and <code>apply-to</code> are optional.
</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/aliveness-test/<i>vhost</i></td>
<td>
Declares a test queue, then publishes and consumes a
message. Intended for use by monitoring tools. If everything
is working correctly, will return HTTP status 200 with
body: <pre>{"status":"ok"}</pre> Note: the test queue will
not be deleted (to to prevent queue churn if this is
repeatedly pinged).
</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/healthchecks/node</td>
<td>
Runs basic healthchecks in the current node. Checks that the rabbit
application is running, channels and queues can be listed successfully, and
that no alarms are in effect. If everything is working correctly, will
return HTTP status 200 with body: <pre>{"status":"ok"}</pre> If
something fails, will return HTTP status 200 with the body of
<pre>{"status":"failed","reason":"string"}</pre>
</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/healthchecks/node/<i>node</i></td>
<td>
Runs basic healthchecks in the given node. Checks that the rabbit
application is running, list_channels and list_queues return, and
that no alarms are raised. If everything is working correctly, will
return HTTP status 200 with body: <pre>{"status":"ok"}</pre> If
something fails, will return HTTP status 200 with the body of
<pre>{"status":"failed","reason":"string"}</pre>
</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/vhost-limits</td>
<td>
Lists per-vhost limits for all vhosts.
</td>
</tr>
<tr>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td class="path">/api/vhost-limits/<i>vhost</i></td>
<td>
Lists per-vhost limits for specific vhost.
</td>
</tr>
<tr>
<td></td>
<td>X</td>
<td>X</td>
<td></td>
<td class="path">/api/vhost-limits/<i>vhost</i>/<i>name</i></td>
<td>
Set or delete per-vhost limit for <code>vhost</code>. The <code>name</code> URL path element
refers to the name of the limit (<code>max-connections</code>, <code>max-queues</code>).
Limits are set using a JSON document in the body: <pre>{"value": 100}</pre>. Example
request:</br>
<pre>curl -4u 'guest:guest' -H 'content-type:application/json' -X PUT localhost:15672/api/vhost-limits/my-vhost/max-connections -d '{"value": 50}'</pre>
</td>
</tr>
</table>
<h2>HTTP API Stats</h2>
<p>
Most of the GET requests you can issue to the HTTP API return
JSON objects with a large number of keys. While a few of these
keys represent things you set yourself in a PUT request or AMQP
command (e.g. queue durability or arguments), most of them
represent statistics to do with the object in question. This
page attempts to document them.
</p>
<p>
It should be read in conjunction with the manual page
for <code>rabbitmqctl</code> (see your installation if on Unix / Linux,
or <a href="https://www.rabbitmq.com/rabbitmqctl.8.html">the
RabbitMQ website</a> for the latest version). Any field which can
be returned by a command of the form <code>rabbitmqctl
list_<i>something</i></code> will also be returned in the
equivalent part of the HTTP API, so all those keys are not
documented here. However, the HTTP API also adds a lot of extra
fields which are not available in <code>rabbitmqctl</code>.
</p>
<h2>_details objects</h2>
<p>
Many fields represent a count of some kind: queue length,
messages acknowledged, bytes received and so on. Such absolute
counts returned by the HTTP API will often have a
corresponding <code>_details</code> object which offers
information on how this count has changed. So for example, from
a queue:
</p>
<pre> "messages": 123619,
"messages_details": {
"avg": 41206.333333333336,
"avg_rate": 1030.1583333333333,
"rate": 24723.8,
"samples": [
{
"sample": 123619,
"timestamp": 1400680560000
},
{
"sample": 0,
"timestamp": 1400680500000
},
{
"sample": 0,
"timestamp": 1400680440000
}
]
}</pre>
<p>
Here we have a <code>messages</code> count (the total messages
in the queue), with some additional data:
</p>
<table>
<tr>
<td><code>avg</code></td>
<td>
The average value for the requested time period (see below).
</td>
</tr>
<tr>
<td><code>avg_rate</code></td>
<td>
The average rate for the requested time period.
</td>
</tr>
<tr>
<td><code>rate</code></td>
<td>
How much the count has changed per second in the most recent
sampling interval.
</td>
</tr>
<tr>
<td><code>samples</code></td>
<td>
Snapshots showing how the value has changed over the
requested time period.
</td>
</tr>
</table>
<p>
<code>avg</code>, <code>avg_rate</code> and <code>samples</code>
will only appear if you request a specific time period by
appending query parameters to the URL. To do this you need to
set an age and an increment for the samples you want. The end of
the range returned will always correspond to the present.
</p>
<p>
Different types of data take different query parameters to
return samples, as in the following table. You can specify more
than one set of parameters if the resource you are requesting
can generate more than one type of sample (for example, queues
can return message rates and queue lengths).
</p>
<table>
<tr>
<td>Messages sent and received</td>
<td><code>msg_rates_age</code> / <code>msg_rates_incr</code></td>
</tr>
<tr>
<td>Bytes sent and received</td>
<td><code>data_rates_age</code> / <code>data_rates_incr</code>
</td>
</tr>
<tr>
<td>Queue lengths</td>
<td><code>lengths_age</code> / <code>lengths_incr</code></td>
</tr>
<tr>
<td>Node statistics (e.g. file descriptors, disk space free)</td>
<td><code>node_stats_age</code> / <code>node_stats_incr</code></td>
</tr>
</table>
<p>
For example,
appending <code>?lengths_age=3600&lengths_incr=60</code> will
return the last hour's data on queue lengths, with a sample for
every minute.
</p>
<h2>message_stats objects</h2>
<p>
Many objects (including queues, exchanges and channels) will
return counts of messages passing through them. These are
included in a <code>message_stats</code> object (which in turn
will contain <code>_details</code> objects for each count, as
described above).
</p>
<p>
These can contain:
</p>
<table>
<tr>
<td><code>publish</code></td>
<td>
Count of messages published.
</td>
</tr>
<tr>
<td><code>publish_in</code></td>
<td>
Count of messages published "in" to an exchange, i.e. not
taking account of routing.
</td>
</tr>
<tr>
<td><code>publish_out</code></td>
<td>
Count of messages published "out" of an exchange,
i.e. taking account of routing.
</td>
</tr>
<tr>
<td><code>confirm</code></td>
<td>
Count of messages confirmed.
</td>
</tr>
<tr>
<td><code>deliver</code></td>
<td>
Count of messages delivered in acknowledgement mode to consumers.
</td>
</tr>
<tr>
<td><code>deliver_no_ack</code></td>
<td>
Count of messages delivered in no-acknowledgement mode to consumers.
</td>
</tr>
<tr>
<td><code>get</code></td>
<td>
Count of messages delivered in acknowledgement mode in
response to basic.get.
</td>
</tr>
<tr>
<td><code>get_no_ack</code></td>
<td>
Count of messages delivered in no-acknowledgement mode in
response to basic.get.
</td>
</tr>
<tr>
<td><code>deliver_get</code></td>
<td>
Sum of all four of the above.
</td>
</tr>
<tr>
<td><code>redeliver</code></td>
<td>
Count of subset of messages in <code>deliver_get</code>
which had the redelivered flag set.
</td>
</tr>
<tr>
<td><code>return_unroutable</code></td>
<td>
Count of messages returned to publisher as unroutable.
</td>
</tr>
</table>
<p>
Only fields for which some activity has taken place will appear.
</p>
<h2>Detailed message stats objects</h2>
<p>
In addition, queues, exchanges and channels can return a
breakdown of message stats for each of their neighbours
(i.e. adjacent objects in the chain: channel -> exchange ->
queue -> channel). This will only happen if
the <code>rates_mode</code> configuration item has been switched
to <code>detailed</code> from its default of <code>basic</code>.
</p>
<p>
As this possibly constitutes a large quantity of data, it is also
only returned when querying a single channel, queue or exchange
rather than a list. Note also that the default sample retention
policy means that these detailed message stats do not retain
historical data for more than a few seconds.
</p>
<p>
The detailed message stats objects have different names
depending on where they are (documented below). Each set of
detailed stats consists of a list of objects with two fields,
one identifying the partner object and one <code>stats</code>
which is a message_stats object as described above.
</p>
<p>
For example, from a queue:
</p>
<pre> "incoming": [
{
"stats": {
"publish": 352593,
"publish_details": {
"rate": 100.2
}
},
"exchange": {
"name": "my-exchange",
"vhost": "/"
}
}
{
"stats": {
"publish": 543784,
"publish_details": {
"rate": 54.6
}
},
"exchange": {
"name": "amq.topic",
"vhost": "/"
}
}
],</pre>
<p>
This queue is currently receiving messages from two exchanges:
100.2 msg/s from "my-exchange" and 54.6 msg/s from "amq.topic".
</p>
<h2>/api/overview</h2>
<p>
This has the following fields:
</p>
<table>
<tr>
<td><code>cluster_name</code></td>
<td>
The name of the entire cluster, as set with <code>rabbitmqctl
set_cluster_name</code>.
</td>
</tr>
<tr>
<td><code>contexts</code></td>
<td>
A list of web application contexts in the cluster.
</td>
</tr>
<tr>
<td><code>erlang_full_version</code></td>
<td>
A string with extended detail about the Erlang VM and how it
was compiled, for the node connected to.
</td>
</tr>
<tr>
<td><code>erlang_version</code></td>
<td>
A string with the Erlang version of the node connected
to. As clusters should all run the same version this can be
taken as representing the cluster.
</td>
</tr>
<tr>
<td><code>exchange_types</code></td>
<td>
A list of all exchange types available.
</td>
</tr>
<tr>
<td><code>listeners</code></td>
<td>
All (non-HTTP) network listeners for all nodes in the
cluster. (See <code>contexts</code>
in <code>/api/nodes</code> for HTTP).
</td>
</tr>
<tr>
<td><code>management_version</code></td>
<td>
Version of the management plugin in use.
</td>
</tr>
<tr>
<td><code>message_stats</code></td>
<td>
A message_stats object for everything the user can see - for
all vhosts regardless of permissions in the case
of <code>monitoring</code> and <code>administrator</code>
users, and for all vhosts the user has access to for other
users.
</td>
</tr>
<tr>
<td><code>node</code></td>
<td>
The name of the cluster node this management plugin instance
is running on.
</td>
</tr>
<tr>
<td><code>object_totals</code></td>
<td>
An object containing global counts of all connections,
channels, exchanges, queues and consumers, subject to the
same visibility rules as for <code>message_stats</code>.
</td>
</tr>
<tr>
<td><code>queue_totals</code></td>
<td>
An object containing sums of
the <code>messages</code>, <code>messages_ready</code>
and <code>messages_unacknowledged</code> fields for all
queues, again subject to the same visibility rules as
for <code>message_stats</code>.
</td>
</tr>
<tr>
<td><code>rabbitmq_version</code></td>
<td>
Version of RabbitMQ on the node which processed this request.
</td>
</tr>
<tr>
<td><code>rates_mode</code></td>
<td>
'none', 'basic' or 'detailed'.
</td>
</tr>
<tr>
<td><code>statistics_db_event_queue</code></td>
<td>
Number of outstanding statistics events yet to be processed
by the database.
</td>
</tr>
<tr>
<td><code>statistics_db_node</code></td>
<td>
Name of the cluster node hosting the management statistics database.
</td>
</tr>
</table>
<h2>/api/nodes</h2>
<p>
This has the following fields:
</p>
<table>
<tr>
<td><code>applications</code></td>
<td>
List of all Erlang applications running on the node.
</td>
</tr>
<tr>
<td><code>auth_mechanisms</code></td>
<td>
List of all SASL authentication mechanisms installed on the node.
</td>
</tr>
<tr>
<td><code>cluster_links</code></td>
<td>
A list of the other nodes in the cluster. For each node,
there are details of the TCP connection used to connect to
it and statistics on data that has been transferred.
</td>
</tr>
<tr>
<td><code>config_files</code></td>
<td>
List of config files read by the node.
</td>
</tr>
<tr>
<td><code>contexts</code></td>
<td>
List of all HTTP listeners on the node.
</td>
</tr>
<tr>
<td><code>db_dir</code></td>
<td>
Location of the persistent storage used by the node.
</td>
</tr>
<tr>
<td><code>disk_free</code></td>
<td>
Disk free space in bytes.
</td>
</tr>
<tr>
<td><code>disk_free_alarm</code></td>
<td>
Whether the disk alarm has gone off.
</td>
</tr>
<tr>
<td><code>disk_free_limit</code></td>
<td>
Point at which the disk alarm will go off.
</td>
</tr>
<tr>
<td><code>enabled_plugins</code></td>
<td>
List of plugins which are both explicitly enabled and running.
</td>
</tr>
<tr>
<td><code>exchange_types</code></td>
<td>
Exchange types available on the node.
</td>
</tr>
<tr>
<td><code>fd_total</code></td>
<td>
File descriptors available.
</td>
</tr>
<tr>
<td><code>fd_used</code></td>
<td>
Used file descriptors.
</td>
</tr>
<tr>
<td><code>io_read_avg_time</code></td>
<td>
Average wall time (milliseconds) for each disk read operation in
the last statistics interval.
</td>
</tr>
<tr>
<td><code>io_read_bytes</code></td>
<td>
Total number of bytes read from disk by the persister.
</td>
</tr>
<tr>
<td><code>io_read_count</code></td>
<td>
Total number of read operations by the persister.
</td>
</tr>
<tr>
<td><code>io_reopen_count</code></td>
<td>
Total number of times the persister has needed to recycle
file handles between queues. In an ideal world this number
will be zero; if the number is large, performance might be
improved by increasing the number of file handles available
to RabbitMQ.
</td>
</tr>
<tr>
<td><code>io_seek_avg_time</code></td>
<td>
Average wall time (milliseconds) for each seek operation in
the last statistics interval.
</td>
</tr>
</tr>
<tr>
<td><code>io_seek_count</code></td>
<td>
Total number of seek operations by the persister.
</td>
</tr>
<tr>
<td><code>io_sync_avg_time</code></td>
<td>
Average wall time (milliseconds) for each fsync() operation in
the last statistics interval.
</td>
</tr>
</tr>
<tr>
<td><code>io_sync_count</code></td>
<td>
Total number of fsync() operations by the persister.
</td>
</tr>
<tr>
<td><code>io_write_avg_time</code></td>
<td>
Average wall time (milliseconds) for each disk write operation in
the last statistics interval.
</td>
</tr>
<tr>
<td><code>io_write_bytes</code></td>
<td>
Total number of bytes written to disk by the persister.
</td>
</tr>
<tr>
<td><code>io_write_count</code></td>
<td>
Total number of write operations by the persister.
</td>
</tr>
<tr>
<td><code>log_files</code></td>
<td>
List of log files used by the node. If the node also sends
messages to stdout, "<code>&lt;stdout&gt;</code>" is also
reported in the list.
</td>
</tr>
<tr>
<td><code>mem_used</code></td>
<td>
Memory used in bytes.
</td>
</tr>
<tr>
<td><code>mem_alarm</code></td>
<td>
Whether the memory alarm has gone off.
</td>
</tr>
<tr>
<td><code>mem_limit</code></td>
<td>
Point at which the memory alarm will go off.
</td>
</tr>
<tr>
<td><code>mnesia_disk_tx_count</code></td>
<td>
Number of Mnesia transactions which have been performed that
required writes to disk. (e.g. creating a durable
queue). Only transactions which originated on this node are
included.
</td>
</tr>
<tr>
<td><code>mnesia_ram_tx_count</code></td>
<td>
Number of Mnesia transactions which have been performed that
did not require writes to disk. (e.g. creating a transient
queue). Only transactions which originated on this node are
included.
</td>
</tr>
<tr>
<td><code>msg_store_read_count</code></td>
<td>
Number of messages which have been read from the message store.
</td>
</tr>
<tr>
<td><code>msg_store_write_count</code></td>
<td>
Number of messages which have been written to the message store.
</td>
</tr>
<tr>
<td><code>name</code></td>
<td>
Node name.
</td>
</tr>
<tr>
<td><code>net_ticktime</code></td>
<td>
Current kernel net_ticktime setting for the node.
</td>
</tr>
<tr>
<td><code>os_pid</code></td>
<td>
Process identifier for the Operating System under which this
node is running.
</td>
</tr>
<tr>
<td><code>partitions</code></td>
<td>
List of network partitions this node is seeing.
</td>
</tr>
<tr>
<td><code>proc_total</code></td>
<td>
Maximum number of Erlang processes.
</td>
</tr>
<tr>
<td><code>proc_used</code></td>
<td>
Number of Erlang processes in use.
</td>
</tr>
<tr>
<td><code>processors</code></td>
<td>
Number of cores detected and usable by Erlang.
</td>
</tr>
<tr>
<td><code>queue_index_journal_write_count</code></td>
<td>
Number of records written to the queue index journal. Each
record represents a message being published to a queue,
being delivered from a queue, and being acknowledged in a
queue.
</td>
</tr>
<tr>
<td><code>queue_index_read_count</code></td>
<td>
Number of records read from the queue index.
</td>
</tr>
<tr>
<td><code>queue_index_write_count</code></td>
<td>
Number of records written to the queue index.
</td>
</tr>
<tr>
<td><code>rates_mode</code></td>
<td>
'none', 'basic' or 'detailed'.
</td>
</tr>
<tr>
<td><code>run_queue</code></td>
<td>
Average number of Erlang processes waiting to run.
</td>
</tr>
<tr>
<td><code>running</code></td>
<td>
Boolean for whether this node is up. Obviously if this is
false, most other stats will be missing.
</td>
</tr>
<tr>
<td><code>sasl_log_file</code></td>
<td>
Location of <a href="https://www.erlang.org/doc/man/sasl_app.html">sasl</a> log file.
</td>
</tr>
<tr>
<td><code>sockets_total</code></td>
<td>
File descriptors available for use as sockets.
</td>
</tr>
<tr>
<td><code>sockets_used</code></td>
<td>
File descriptors used as sockets.
</td>
</tr>
<tr>
<td><code>type</code></td>
<td>
'disc' or 'ram'.
</td>
</tr>
<tr>
<td><code>uptime</code></td>
<td>
Time since the Erlang VM started, in milliseconds.
</td>
</tr>
</table>
<h2>/api/nodes/(name)</h2>
<p>
All of the above, plus:
</p>
<table>
<tr>
<td><code>memory</code></td>
<td>
Detailed memory use statistics. Only appears
if <code>?memory=true</code> is appended to the URL.
</td>
</tr>
<tr>
<td><code>binary</code></td>
<td>
Detailed breakdown of the owners of binary memory. Only
appears if <code>?binary=true</code> is appended to the
URL. Note that this can be an expensive query if there are
many small binaries in the system.
</td>
</tr>
</table>
<h2>/api/connections</h2>
<h2>/api/connections/(name)</h2>
<p>
See documentation for <code>rabbitmqctl
list_connections</code>. No additional fields,
although <code>pid</code> is replaced by <code>node</code>.
</p>
<p>
Note also that while non-AMQP connections will appear in this
list (unlike <code>rabbitmqctl list_connections</code>), they
will omit many of the connection-level statistics.
</p>
<h2>/api/connections/(name)/channels</h2>
<h2>/api/channels</h2>
<p>
See documentation for <code>rabbitmqctl list_channels</code>,
with <code>pid</code> replaced by <code>node</code>, plus:
</p>
<table>
<tr>
<td><code>connection_details</code></td>
<td>
Some basic details about the owning connection.
</td>
</tr>
<tr>
<td><code>message_stats</code></td>
<td>
See the section on message_stats above.
</td>
</tr>
</table>
<h2>/api/channels/(name)</h2>
<p>
All the above, plus
</p>
<table>
<tr>
<td><code>publishes</code></td>
<td>
Detailed message stats (see section above) for publishes to
exchanges.
</td>
</tr>
<tr>
<td><code>deliveries</code></td>
<td>
Detailed message stats for deliveries from queues.
</td>
</tr>
<tr>
<td><code>consumer_details</code></td>
<td>
List of consumers on this channel, with some details on each.
</td>
</tr>
</table>
<h2>/api/exchanges</h2>
<h2>/api/exchanges/(vhost)</h2>
<p>
See documentation for <code>rabbitmqctl list_exchanges</code>, plus:
</p>
<table>
<tr>
<td><code>message_stats</code></td>
<td>
See the section on message_stats above.
</td>
</tr>
</table>
<h2>/api/exchanges/(vhost)/(name)</h2>
<p>
All the above, plus:
</p>
<table>
<tr>
<td><code>incoming</code></td>
<td>
Detailed message stats (see section above) for publishes
from channels into this exchange.
</td>
</tr>
<tr>
<td><code>outgoing</code></td>
<td>
Detailed message stats for publishes from this exchange into
queues.
</td>
</tr>
</table>
<h2>/api/queues</h2>
<h2>/api/queues/(vhost)</h2>
<p>
See documentation for <code>rabbitmqctl list_queues</code>, with
all references to <code>pid</code>s replaced by <code>node</code>s
plus:
</p>
<table>
<tr>
<td><code>message_stats</code></td>
<td>
See the section on message_stats above.
</td>
</tr>
</table>
<h2>/api/queues/(vhost)/(name)</h2>
<p>
All the above, plus:
</p>
<table>
<tr>
<td><code>incoming</code></td>
<td>
Detailed message stats (see section above) for
publishes from exchanges into this queue.
</td>
</tr>
<tr>
<td><code>deliveries</code></td>
<td>
Detailed message stats for deliveries from this queue into
channels.
</td>
</tr>
<tr>
<td><code>consumer_details</code></td>
<td>
List of consumers on this channel, with some details on each.
</td>
</tr>
</table>
<h2>/api/vhosts/</h2>
<h2>/api/vhosts/(name)</h2>
<p>
All the fields from <code>rabbitmqctl list_vhosts</code>
(i.e. <code>name</code> and <code>tracing</code>) plus:
</p>
<table>
<tr>
<td><code>message_stats</code></td>
<td>
Global message_stats for this vhost. Note that activity for
other users in this vhost <b>is</b> shown, even for users
without the <code>monitoring</code> tag.
</td>
</tr>
<tr>
<td><code>messages</code> <code>messages_ready</code> <code>messages_acknowledged</code></td>
<td>
Sum of these fields for all queues in the vhost.
</td>
</tr>
<tr>
<td><code>recv_oct</code> <code>send_oct</code></td>
<td>
Sum of these fields for all connections to the vhost.
</td>
</tr>
</table>
</body>
</html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>rabbitmqadmin</title>
<style>
body { font: 12px Verdana,sans-serif; color: #444; padding: 8px 35px; }
h1 { font-size: 2em; }
code { background: #ffa; }
</style>
</head>
<body>
<h1>rabbitmqadmin</h1>
<p>
<code>rabbitmqadmin</code> is an HTTP API-based management tool. It uses the same API as RabbitMQ management UI
and provides access to the subset of the functionality in the UI from the command line.
It is not meant to be a replacement for <a href="https://www.rabbitmq.com/man/rabbitmqctl.1.man.html">rabbitmqctl</a>
as there are operations that HTTP API intentionally does not expose.
</p>
<p>
To use it, <a href="rabbitmqadmin">download it</a> from this node (right click,
Save As), make sure it is executable, and drop it in your <code>PATH</code>. Note that
many browsers will rename the
file <code>rabbitmqadmin.txt</code>. <code>rabbitmqadmin</code> requires Python
2.6 or later (both 2.x and 3.x series are supported).
To use <code>rabbitmqadmin</code> with HTTPS, Python 2.7.9 is the minimum supported version.
</p>
<p>
See <a href="https://www.rabbitmq.com/management-cli.html">the
rabbitmqadmin page on the website</a> for more information on
its use, or invoke <code>rabbitmqadmin --help</code> for usage
instructions.
</p>
<p>
Windows users will need to ensure Python is on
their <code>PATH</code>, and invoke rabbitmqadmin as <code>python.exe
rabbitmqadmin</code>.
</p>
</body>
</html>
#!/usr/bin/env python3
# The contents of this file are subject to the Mozilla Public License
# Version 1.1 (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# https://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
# License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is RabbitMQ Management Plugin.
#
# The Initial Developer of the Original Code is GoPivotal, Inc.
# Copyright (c) 2007-2018 Pivotal Software, Inc. All rights reserved.
from __future__ import print_function
from optparse import OptionParser, TitledHelpFormatter
import base64
import copy
import json
import os
import socket
import ssl
import traceback
try:
from signal import signal, SIGPIPE, SIG_DFL
signal(SIGPIPE, SIG_DFL)
except ImportError:
pass
import sys
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
if sys.version_info[0] < 2 or (sys.version_info[0] == 2 and sys.version_info[1] < 6):
eprint("Sorry, rabbitmqadmin requires at least Python 2.6 (2.7.9 when HTTPS is enabled).")
sys.exit(1)
if sys.version_info[0] == 2:
from ConfigParser import ConfigParser, NoSectionError
import httplib
import urlparse
from urllib import quote_plus
from urllib import quote
def b64(s):
return base64.b64encode(s)
else:
from configparser import ConfigParser, NoSectionError
import http.client as httplib
import urllib.parse as urlparse
from urllib.parse import quote_plus
from urllib.parse import quote
def b64(s):
return base64.b64encode(s.encode('utf-8')).decode('utf-8')
if sys.version_info[0] == 2:
class ConnectionError(OSError):
pass
class ConnectionRefusedError(ConnectionError):
pass
VERSION = '3.7.28'
LISTABLE = {'connections': {'vhost': False, 'cols': ['name', 'user', 'channels']},
'channels': {'vhost': False, 'cols': ['name', 'user']},
'consumers': {'vhost': True},
'exchanges': {'vhost': True, 'cols': ['name', 'type']},
'queues': {'vhost': True, 'cols': ['name', 'messages']},
'bindings': {'vhost': True, 'cols': ['source', 'destination',
'routing_key']},
'users': {'vhost': False},
'vhosts': {'vhost': False, 'cols': ['name', 'messages']},
'permissions': {'vhost': False},
'nodes': {'vhost': False, 'cols': ['name', 'type', 'mem_used']},
'parameters': {'vhost': False, 'json': ['value']},
'policies': {'vhost': False, 'json': ['definition']},
'operator_policies': {'vhost': False, 'json': ['definition']},
'vhost_limits': {'vhost': False, 'json': ['value']}}
SHOWABLE = {'overview': {'vhost': False, 'cols': ['rabbitmq_version',
'cluster_name',
'queue_totals.messages',
'object_totals.queues']}}
PROMOTE_COLUMNS = ['vhost', 'name', 'type',
'source', 'destination', 'destination_type', 'routing_key']
URIS = {
'exchange': '/exchanges/{vhost}/{name}',
'queue': '/queues/{vhost}/{name}',
'binding': '/bindings/{vhost}/e/{source}/{destination_char}/{destination}',
'binding_del': '/bindings/{vhost}/e/{source}/{destination_char}/{destination}/{properties_key}',
'vhost': '/vhosts/{name}',
'user': '/users/{name}',
'permission': '/permissions/{vhost}/{user}',
'parameter': '/parameters/{component}/{vhost}/{name}',
'policy': '/policies/{vhost}/{name}',
'operator_policy': '/operator-policies/{vhost}/{name}',
'vhost_limit': '/vhost-limits/{vhost}/{name}'
}
DECLARABLE = {
'exchange': {'mandatory': ['name', 'type'],
'json': ['arguments'],
'optional': {'auto_delete': 'false', 'durable': 'true',
'internal': 'false', 'arguments': {}}},
'queue': {'mandatory': ['name'],
'json': ['arguments'],
'optional': {'auto_delete': 'false', 'durable': 'true',
'arguments': {}, 'node': None}},
'binding': {'mandatory': ['source', 'destination'],
'json': ['arguments'],
'optional': {'destination_type': 'queue',
'routing_key': '', 'arguments': {}}},
'vhost': {'mandatory': ['name'],
'optional': {'tracing': None}},
'user': {'mandatory': ['name', ['password', 'password_hash'], 'tags'],
'optional': {'hashing_algorithm': None}},
'permission': {'mandatory': ['vhost', 'user', 'configure', 'write', 'read'],
'optional': {}},
'parameter': {'mandatory': ['component', 'name', 'value'],
'json': ['value'],
'optional': {}},
# priority has to be converted to an integer
'policy': {'mandatory': ['name', 'pattern', 'definition'],
'json': ['definition', 'priority'],
'optional': {'priority': 0, 'apply-to': None}},
'operator_policy': {'mandatory': ['name', 'pattern', 'definition'],
'json': ['definition', 'priority'],
'optional': {'priority': 0, 'apply-to': None}},
'vhost_limit': {'mandatory': ['vhost', 'name', 'value'],
'json': ['value'],
'optional': {}},
}
DELETABLE = {
'exchange': {'mandatory': ['name']},
'queue': {'mandatory': ['name']},
'binding': {'mandatory': ['source', 'destination_type', 'destination'],
'optional': {'properties_key': '~'}},
'vhost': {'mandatory': ['name']},
'user': {'mandatory': ['name']},
'permission': {'mandatory': ['vhost', 'user']},
'parameter': {'mandatory': ['component', 'name']},
'policy': {'mandatory': ['name']},
'operator_policy': {'mandatory': ['name']},
'vhost_limit': {'mandatory': ['vhost', 'name']}
}
CLOSABLE = {
'connection': {'mandatory': ['name'],
'optional': {},
'uri': '/connections/{name}'}
}
PURGABLE = {
'queue': {'mandatory': ['name'],
'optional': {},
'uri': '/queues/{vhost}/{name}/contents'}
}
EXTRA_VERBS = {
'publish': {'mandatory': ['routing_key'],
'optional': {'payload': None,
'properties': {},
'exchange': 'amq.default',
'payload_encoding': 'string'},
'json': ['properties'],
'uri': '/exchanges/{vhost}/{exchange}/publish'},
'get': {'mandatory': ['queue'],
'optional': {'count': '1', 'ackmode': 'ack_requeue_true',
'payload_file': None, 'encoding': 'auto'},
'uri': '/queues/{vhost}/{queue}/get'}
}
for k in DECLARABLE:
DECLARABLE[k]['uri'] = URIS[k]
for k in DELETABLE:
DELETABLE[k]['uri'] = URIS[k]
DELETABLE[k]['optional'] = DELETABLE[k].get('optional', {})
DELETABLE['binding']['uri'] = URIS['binding_del']
def short_usage():
return "rabbitmqadmin [options] subcommand"
def title(name):
return "\n%s\n%s\n\n" % (name, '=' * len(name))
def subcommands_usage():
usage = """Usage
=====
""" + short_usage() + """
where subcommand is one of:
""" + title("Display")
for l in LISTABLE:
usage += " list {0} [<column>...]\n".format(l)
for s in SHOWABLE:
usage += " show {0} [<column>...]\n".format(s)
usage += title("Object Manipulation")
usage += fmt_usage_stanza(DECLARABLE, 'declare')
usage += fmt_usage_stanza(DELETABLE, 'delete')
usage += fmt_usage_stanza(CLOSABLE, 'close')
usage += fmt_usage_stanza(PURGABLE, 'purge')
usage += title("Broker Definitions")
usage += """ export <file>
import <file>
"""
usage += title("Publishing and Consuming")
usage += fmt_usage_stanza(EXTRA_VERBS, '')
usage += """
* If payload is not specified on publish, standard input is used
* If payload_file is not specified on get, the payload will be shown on
standard output along with the message metadata
* If payload_file is specified on get, count must not be set
"""
return usage
def config_usage():
usage = "Usage\n=====\n" + short_usage()
usage += "\n" + title("Configuration File")
usage += """ It is possible to specify a configuration file from the command line.
Hosts can be configured easily in a configuration file and called
from the command line.
"""
usage += title("Example")
usage += """ # rabbitmqadmin.conf.example START
[host_normal]
hostname = localhost
port = 15672
username = guest
password = guest
declare_vhost = / # Used as default for declare / delete only
vhost = / # Used as default for declare / delete / list
[host_ssl]
hostname = otherhost
port = 15672
username = guest
password = guest
ssl = True
ssl_key_file = /path/to/key.pem
ssl_cert_file = /path/to/cert.pem
# rabbitmqadmin.conf.example END
"""
usage += title("Use")
usage += """ rabbitmqadmin -c rabbitmqadmin.conf.example -N host_normal ..."""
return usage
def more_help():
return """
More Help
=========
For more help use the help subcommand:
rabbitmqadmin help subcommands # For a list of available subcommands
rabbitmqadmin help config # For help with the configuration file
"""
def fmt_required_flag(val):
# when one of the options is required, e.g.
# password vs. password_hash
if type(val) is list:
# flag1=... OR flag2=... OR flag3=...
return "=... OR ".join(val)
else:
return val
def fmt_optional_flag(val):
return val
def fmt_usage_stanza(root, verb):
def fmt_args(args):
res = " ".join(["{0}=...".format(fmt_required_flag(a)) for a in args['mandatory']])
opts = " ".join("{0}=...".format(fmt_optional_flag(o)) for o in args['optional'].keys())
if opts != "":
res += " [{0}]".format(opts)
return res
text = ""
if verb != "":
verb = " " + verb
for k in root.keys():
text += " {0} {1} {2}\n".format(verb, k, fmt_args(root[k]))
return text
default_options = {"hostname": "localhost",
"port": "15672",
# default config file section name
"node": "default",
"path_prefix": "",
"declare_vhost": "/",
"username": "guest",
"password": "guest",
"ssl": False,
"request_timeout": 120,
"verbose": True,
"format": "table",
"depth": 1,
"bash_completion": False}
class MyFormatter(TitledHelpFormatter):
def format_epilog(self, epilog):
return epilog
parser = OptionParser(usage=short_usage(),
formatter=MyFormatter(),
epilog=more_help())
def make_parser():
def add(*args, **kwargs):
key = kwargs['dest']
if key in default_options:
default = " [default: %s]" % default_options[key]
kwargs['help'] = kwargs['help'] + default
parser.add_option(*args, **kwargs)
add("-c", "--config", dest="config",
help="configuration file [default: ~/.rabbitmqadmin.conf]",
metavar="CONFIG")
add("-N", "--node", dest="node",
help="node described in the configuration file [default: 'default' only if configuration file is specified]",
metavar="NODE")
add("-H", "--host", dest="hostname",
help="connect to host HOST",
metavar="HOST")
add("-P", "--port", dest="port",
help="connect to port PORT",
metavar="PORT")
add("--path-prefix", dest="path_prefix",
help="use specific URI path prefix for the RabbitMQ HTTP API. /api and operation path will be appended to it. (default: blank string)")
add("-V", "--vhost", dest="vhost",
help="connect to vhost VHOST [default: all vhosts for list, '/' for declare]",
metavar="VHOST")
add("-u", "--username", dest="username",
help="connect using username USERNAME",
metavar="USERNAME")
add("-p", "--password", dest="password",
help="connect using password PASSWORD",
metavar="PASSWORD")
add("-U", "--base-uri", dest="base_uri",
help="connect using a base HTTP API URI. /api and operation path will be appended to it. Path will be ignored. --vhost has to be provided separately.",
metavar="URI")
add("-q", "--quiet", action="store_false", dest="verbose",
help="suppress status messages")
add("-s", "--ssl", action="store_true", dest="ssl",
help="connect with ssl")
add("--ssl-key-file", dest="ssl_key_file",
help="PEM format key file for SSL")
add("--ssl-cert-file", dest="ssl_cert_file",
help="PEM format certificate file for SSL")
add("--ssl-ca-cert-file", dest="ssl_ca_cert_file",
help="PEM format CA certificate file for SSL")
add("--ssl-disable-hostname-verification", dest="ssl_disable_hostname_verification",
help="Disables peer hostname verification", default=False, action="store_true")
add("-k", "--ssl-insecure", dest="ssl_insecure",
help="Disables all SSL validations like curl's '-k' argument", default=False, action="store_true")
add("-t", "--request-timeout", dest="request_timeout",
help="HTTP request timeout in seconds", type="int")
add("-f", "--format", dest="format",
help="format for listing commands - one of [" + ", ".join(FORMATS.keys()) + "]")
add("-S", "--sort", dest="sort", help="sort key for listing queries")
add("-R", "--sort-reverse", action="store_true", dest="sort_reverse",
help="reverse the sort order")
add("-d", "--depth", dest="depth",
help="maximum depth to recurse for listing tables")
add("--bash-completion", action="store_true",
dest="bash_completion",
help="Print bash completion script")
add("--version", action="store_true",
dest="version",
help="Display version and exit")
def default_config():
home = os.getenv('USERPROFILE') or os.getenv('HOME')
if home is not None:
config_file = home + os.sep + ".rabbitmqadmin.conf"
if os.path.isfile(config_file):
return config_file
return None
def make_configuration():
make_parser()
(cli_options, args) = parser.parse_args()
if cli_options.version:
print_version()
setattr(cli_options, "declare_vhost", None)
final_options = copy.copy(cli_options)
# Resolve config file path
if cli_options.config is None:
config_file = default_config()
if config_file is not None:
setattr(final_options, "config", config_file)
else:
if not os.path.isfile(cli_options.config):
assert_usage(False, "Could not read config file '%s'" % cli_options.config)
final_options = merge_default_options(cli_options, final_options)
final_options = merge_config_file_options(cli_options, final_options)
final_options = expand_base_uri_options(cli_options, final_options)
return (final_options, args)
def merge_default_options(cli_options, final_options):
for (key, default_val) in default_options.items():
if getattr(cli_options, key) is None:
setattr(final_options, key, default_val)
return final_options
def merge_config_file_options(cli_options, final_options):
# Parse config file and load it, making sure that CLI flags
# take precedence
if final_options.config is not None:
config_parser = ConfigParser()
try:
config_parser.read(final_options.config)
section_settings = dict(config_parser.items(final_options.node))
except NoSectionError as error:
# Report if an explicitly provided section (node) does not exist in the file
if final_options.node == "default":
pass
else:
msg = "Could not read section '%s' in config file '%s':\n %s" % (final_options.node, final_options.config, error)
assert_usage(False, msg)
else:
for key, section_val in section_settings.items():
# special case --ssl
if key == 'ssl':
setattr(final_options, key, section_val == "True")
else:
# if CLI options do not contain this key, set it from the config file
if getattr(cli_options, key) is None:
setattr(final_options, key, section_val)
return final_options
def expand_base_uri_options(cli_options, final_options):
# if --base-uri is passed, set connection parameters from it
if final_options.base_uri is not None:
u = urlparse.urlparse(final_options.base_uri)
for key in ["hostname", "port", "username", "password"]:
if getattr(u, key) is not None:
setattr(final_options, key, getattr(u, key))
if u.path is not None and (u.path != "") and (u.path != "/"):
eprint("WARNING: path in --base-uri is ignored. Please specify --vhost and/or --path-prefix separately.\n")
return final_options
def assert_usage(expr, error):
if not expr:
eprint("\nERROR: {0}\n".format(error))
eprint("{0} --help for help\n".format(os.path.basename(sys.argv[0])))
sys.exit(1)
def print_version():
print("rabbitmqadmin {0}".format(VERSION))
sys.exit(0)
def column_sort_key(col):
if col in PROMOTE_COLUMNS:
return (1, PROMOTE_COLUMNS.index(col))
else:
return (2, col)
def main():
(options, args) = make_configuration()
if options.bash_completion:
print_bash_completion()
sys.exit(0)
assert_usage(len(args) > 0, 'Action not specified')
mgmt = Management(options, args[1:])
mode = "invoke_" + args[0]
assert_usage(hasattr(mgmt, mode),
'Action {0} not understood'.format(args[0]))
method = getattr(mgmt, "invoke_%s" % args[0])
method()
def die(s):
eprint("*** {0}\n".format(s))
sys.exit(1)
def maybe_utf8(s):
if isinstance(s, int):
# s can be also an int for ex messages count
return str(s)
if isinstance(s, float):
# s can be also a float for message rate
return str(s)
if sys.version_info[0] == 3:
# It will have an encoding, which Python will respect
return s
else:
# It won't have an encoding, and Python will pick ASCII by default
return s.encode('utf-8')
class Management:
def __init__(self, options, args):
self.options = options
self.args = args
def get(self, path):
return self.http("GET", "%s/api%s" % (self.options.path_prefix, path), "")
def put(self, path, body):
return self.http("PUT", "%s/api%s" % (self.options.path_prefix, path), body)
def post(self, path, body):
return self.http("POST", "%s/api%s" % (self.options.path_prefix, path), body)
def delete(self, path):
return self.http("DELETE", "%s/api%s" % (self.options.path_prefix, path), "")
def __initialize_connection(self, hostname, port):
if self.options.ssl:
return self.__initialize_https_connection(hostname, port)
else:
return httplib.HTTPConnection(hostname, port, timeout=self.options.request_timeout)
def __initialize_https_connection(self, hostname, port):
# Python 2.7.9+
if hasattr(ssl, 'create_default_context'):
return httplib.HTTPSConnection(hostname, port, context=self.__initialize_tls_context())
# Python < 2.7.8, note: those versions still have SSLv3 enabled
# and other limitations. See rabbitmq/rabbitmq-management#225
else:
eprint("WARNING: rabbitmqadmin requires Python 2.7.9+ when HTTPS is used.")
return httplib.HTTPSConnection(hostname, port,
cert_file=self.options.ssl_cert_file,
key_file=self.options.ssl_key_file)
def __initialize_tls_context(self):
# Python 2.7.9+ only
ssl_ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
ssl_ctx.options &= ~ssl.OP_NO_SSLv3
ssl_insecure = self.options.ssl_insecure
ssl_disable_hostname_verification = ssl_insecure or self.options.ssl_disable_hostname_verification
# Note: you must set check_hostname prior to verify_mode
if ssl_disable_hostname_verification:
ssl_ctx.check_hostname = False
if ssl_insecure:
ssl_ctx.verify_mode = ssl.CERT_NONE
if self.options.ssl_key_file:
ssl_ctx.load_cert_chain(self.options.ssl_cert_file,
self.options.ssl_key_file)
if self.options.ssl_ca_cert_file:
ssl_ctx.load_verify_locations(self.options.ssl_ca_cert_file)
return ssl_ctx
def http(self, method, path, body):
conn = self.__initialize_connection(self.options.hostname, self.options.port)
auth = (self.options.username + ":" + self.options.password)
headers = {"Authorization": "Basic " + b64(auth)}
if body != "":
headers["Content-Type"] = "application/json"
try:
conn.request(method, path, body, headers)
except ConnectionRefusedError as e:
die("Could not connect: {0}".format(e))
except socket.error as e:
traceback.print_exc()
die("Could not connect: {0}".format(e))
try:
resp = conn.getresponse()
except socket.timeout:
die("Timed out getting HTTP response (request timeout: {0} seconds)".format(
self.options.request_timeout))
except (KeyboardInterrupt, SystemExit):
raise
except (Exception):
e_fmt = traceback.format_exc()
die("Error getting HTTP response:\n\n{0}".format(e_fmt))
if resp.status == 400:
die(json.loads(resp.read())['reason'])
if resp.status == 401:
die("Access refused: {0}".format(path))
if resp.status == 404:
die("Not found: {0}".format(path))
if resp.status == 301:
url = urlparse.urlparse(resp.getheader('location'))
[host, port] = url.netloc.split(':')
self.options.hostname = host
self.options.port = int(port)
return self.http(method, url.path + '?' + url.query, body)
if resp.status > 400:
raise Exception("Received response %d %s for path %s\n%s"
% (resp.status, resp.reason, path, resp.read()))
return resp.read().decode('utf-8')
def verbose(self, string):
if self.options.verbose:
print(string)
def get_arg(self):
assert_usage(len(self.args) == 1, 'Exactly one argument required')
return self.args[0]
def use_cols(self):
# Deliberately do not cast to int here; we only care about the
# default, not explicit setting.
return self.options.depth == 1 and ('json' not in self.options.format)
def invoke_help(self):
if len(self.args) == 0:
parser.print_help()
else:
help_cmd = self.get_arg()
if help_cmd == 'subcommands':
usage = subcommands_usage()
elif help_cmd == 'config':
usage = config_usage()
else:
assert_usage(False, """help topic must be one of:
subcommands
config""")
print(usage)
sys.exit(0)
def invoke_publish(self):
(uri, upload) = self.parse_args(self.args, EXTRA_VERBS['publish'])
if 'payload' not in upload:
data = sys.stdin.read()
upload['payload'] = b64(data)
upload['payload_encoding'] = 'base64'
resp = json.loads(self.post(uri, json.dumps(upload)))
if resp['routed']:
self.verbose("Message published")
else:
self.verbose("Message published but NOT routed")
def invoke_get(self):
(uri, upload) = self.parse_args(self.args, EXTRA_VERBS['get'])
payload_file = 'payload_file' in upload and upload['payload_file'] or None
assert_usage(not payload_file or upload['count'] == '1',
'Cannot get multiple messages using payload_file')
result = self.post(uri, json.dumps(upload))
if payload_file:
write_payload_file(payload_file, result)
columns = ['routing_key', 'exchange', 'message_count',
'payload_bytes', 'redelivered']
format_list(result, columns, {}, self.options)
else:
format_list(result, [], {}, self.options)
def invoke_export(self):
path = self.get_arg()
uri = "/definitions"
if self.options.vhost:
uri += "/%s" % quote_plus(self.options.vhost)
definitions = self.get(uri)
with open(path, 'wb') as f:
f.write(definitions.encode())
self.verbose("Exported definitions for %s to \"%s\""
% (self.options.hostname, path))
def invoke_import(self):
path = self.get_arg()
with open(path, 'rb') as f:
definitions = f.read()
uri = "/definitions"
if self.options.vhost:
uri += "/%s" % quote_plus(self.options.vhost)
self.post(uri, definitions)
self.verbose("Uploaded definitions from \"%s\" to %s. The import process may take some time. Consult server logs to track progress."
% (self.options.hostname, path))
def invoke_list(self):
(uri, obj_info, cols) = self.list_show_uri(LISTABLE, 'list')
format_list(self.get(uri), cols, obj_info, self.options)
def invoke_show(self):
(uri, obj_info, cols) = self.list_show_uri(SHOWABLE, 'show')
format_list('[{0}]'.format(self.get(uri)), cols, obj_info, self.options)
def _list_path_for_obj_type(self, obj_type):
# This returns a URL path for given object type, e.g.
# replaces underscores in command names with
# dashes that HTTP API endpoints use
return obj_type.replace("_", "-")
def list_show_uri(self, obj_types, verb):
obj_type = self.args[0]
assert_usage(obj_type in obj_types,
"Don't know how to {0} {1}".format(verb, obj_type))
obj_info = obj_types[obj_type]
uri = "/%s" % self._list_path_for_obj_type(obj_type)
query = []
if obj_info['vhost'] and self.options.vhost:
uri += "/%s" % quote_plus(self.options.vhost)
cols = self.args[1:]
if cols == [] and 'cols' in obj_info and self.use_cols():
cols = obj_info['cols']
if cols != []:
query.append("columns=" + ",".join(cols))
sort = self.options.sort
if sort:
query.append("sort=" + sort)
if self.options.sort_reverse:
query.append("sort_reverse=true")
query = "&".join(query)
if query != "":
uri += "?" + query
return (uri, obj_info, cols)
def invoke_declare(self):
(obj_type, uri, upload) = self.declare_delete_parse(DECLARABLE)
if obj_type == 'binding':
self.post(uri, json.dumps(upload))
else:
self.put(uri, json.dumps(upload))
self.verbose("{0} declared".format(obj_type))
def invoke_delete(self):
(obj_type, uri, upload) = self.declare_delete_parse(DELETABLE)
self.delete(uri)
self.verbose("{0} deleted".format(obj_type))
def invoke_close(self):
(obj_type, uri, upload) = self.declare_delete_parse(CLOSABLE)
self.delete(uri)
self.verbose("{0} closed".format(obj_type))
def invoke_purge(self):
(obj_type, uri, upload) = self.declare_delete_parse(PURGABLE)
self.delete(uri)
self.verbose("{0} purged".format(obj_type))
def declare_delete_parse(self, root):
assert_usage(len(self.args) > 0, 'Type not specified')
obj_type = self.args[0]
assert_usage(obj_type in root,
'Type {0} not recognised'.format(obj_type))
obj = root[obj_type]
(uri, upload) = self.parse_args(self.args[1:], obj)
return (obj_type, uri, upload)
def assert_mandatory_keys(self, mandatory, upload):
for m in mandatory:
if type(m) is list:
a_set = set(m)
b_set = set(upload.keys())
assert_usage((a_set & b_set),
'one of mandatory arguments "{0}" is required'.format(m))
else:
assert_usage(m in upload.keys(),
'mandatory argument "{0}" is required'.format(m))
def parse_args(self, args, obj):
mandatory = obj['mandatory']
optional = obj['optional']
uri_template = obj['uri']
upload = {}
for k in optional.keys():
if optional[k] is not None:
upload[k] = optional[k]
for arg in args:
assert_usage("=" in arg,
'Argument "{0}" not in the name=value format'.format(arg))
(name, value) = arg.split("=", 1)
# flatten the list of mandatory keys
mandatory_keys = []
for key in mandatory:
if type(key) is list:
for subkey in key:
mandatory_keys.append(subkey)
else:
mandatory_keys.append(key)
assert_usage(name in mandatory_keys or name in optional.keys(),
'Argument "{0}" is not recognised'.format(name))
if 'json' in obj and name in obj['json']:
upload[name] = self.parse_json(value)
else:
upload[name] = value
self.assert_mandatory_keys(mandatory, upload)
if 'vhost' not in mandatory:
upload['vhost'] = self.options.vhost or self.options.declare_vhost
uri_args = {}
for k in upload:
v = upload[k]
if v and isinstance(v, (str, bytes)):
uri_args[k] = quote(v, '')
if k == 'destination_type':
uri_args['destination_char'] = v[0]
uri = uri_template.format(**uri_args)
return (uri, upload)
def parse_json(self, text):
try:
return json.loads(text)
except ValueError:
eprint("ERROR: Could not parse JSON:\n {0}".format(text))
sys.exit(1)
def format_list(json_list, columns, args, options):
format = options.format
formatter = None
if format == "raw_json":
print(json_list)
return
elif format == "pretty_json":
json_list_parsed = json.loads(json_list)
print(json.dumps(json_list_parsed,
skipkeys=False, ensure_ascii=False, check_circular=True,
allow_nan=True, sort_keys=True, indent=2))
return
else:
formatter = FORMATS[format]
assert_usage(formatter is not None,
"Format {0} not recognised".format(format))
formatter_instance = formatter(columns, args, options)
formatter_instance.display(json_list)
class Lister:
def verbose(self, string):
if self.options.verbose:
print(string)
def display(self, json_list):
depth = sys.maxsize
if len(self.columns) == 0:
depth = int(self.options.depth)
(columns, table) = self.list_to_table(json.loads(json_list), depth)
if len(table) > 0:
self.display_list(columns, table)
else:
self.verbose("No items")
def list_to_table(self, items, max_depth):
columns = {}
column_ix = {}
row = None
table = []
def add(prefix, depth, item, fun):
for key in item:
column = prefix == '' and key or (prefix + '.' + key)
subitem = item[key]
if type(subitem) == dict:
if 'json' in self.obj_info and key in self.obj_info['json']:
fun(column, json.dumps(subitem))
else:
if depth < max_depth:
add(column, depth + 1, subitem, fun)
elif type(subitem) == list:
# The first branch has mirrors in queues in
# mind (which come out looking decent); the second
# one has applications in nodes (which look less
# so, but what would look good?).
if [x for x in subitem if type(x) != str] == []:
serialised = " ".join(subitem)
else:
serialised = json.dumps(subitem)
fun(column, serialised)
else:
fun(column, subitem)
def add_to_columns(col, val):
columns[col] = True
def add_to_row(col, val):
if col in column_ix:
if val is not None:
row[column_ix[col]] = maybe_utf8(val)
else:
row[column_ix[col]] = None
if len(self.columns) == 0:
for item in items:
add('', 1, item, add_to_columns)
columns = list(columns.keys())
columns.sort(key=column_sort_key)
else:
columns = self.columns
for i in range(0, len(columns)):
column_ix[columns[i]] = i
for item in items:
row = len(columns) * ['']
add('', 1, item, add_to_row)
table.append(row)
return (columns, table)
class TSVList(Lister):
def __init__(self, columns, obj_info, options):
self.columns = columns
self.obj_info = obj_info
self.options = options
def display_list(self, columns, table):
head = "\t".join(columns)
self.verbose(head)
for row in table:
line = "\t".join(row)
print(line)
class LongList(Lister):
def __init__(self, columns, obj_info, options):
self.columns = columns
self.obj_info = obj_info
self.options = options
def display_list(self, columns, table):
sep = "\n" + "-" * 80 + "\n"
max_width = 0
for col in columns:
max_width = max(max_width, len(col))
fmt = "{0:>" + str(max_width) + "}: {1}"
print(sep)
for i in range(0, len(table)):
for j in range(0, len(columns)):
print(fmt.format(columns[j], table[i][j]))
print(sep)
class TableList(Lister):
def __init__(self, columns, obj_info, options):
self.columns = columns
self.obj_info = obj_info
self.options = options
def display_list(self, columns, table):
total = [columns]
total.extend(table)
self.ascii_table(total)
def ascii_table(self, rows):
col_widths = [0] * len(rows[0])
for i in range(0, len(rows[0])):
for j in range(0, len(rows)):
col_widths[i] = max(col_widths[i], len(rows[j][i]))
self.ascii_bar(col_widths)
self.ascii_row(col_widths, rows[0], "^")
self.ascii_bar(col_widths)
for row in rows[1:]:
self.ascii_row(col_widths, row, "<")
self.ascii_bar(col_widths)
def ascii_row(self, col_widths, row, align):
txt = "|"
for i in range(0, len(col_widths)):
fmt = " {0:" + align + str(col_widths[i]) + "} "
txt += fmt.format(row[i]) + "|"
print(txt)
def ascii_bar(self, col_widths):
txt = "+"
for w in col_widths:
txt += ("-" * (w + 2)) + "+"
print(txt)
class KeyValueList(Lister):
def __init__(self, columns, obj_info, options):
self.columns = columns
self.obj_info = obj_info
self.options = options
def display_list(self, columns, table):
for i in range(0, len(table)):
row = []
for j in range(0, len(columns)):
row.append("{0}=\"{1}\"".format(columns[j], table[i][j]))
print(" ".join(row))
# TODO handle spaces etc in completable names
class BashList(Lister):
def __init__(self, columns, obj_info, options):
self.columns = columns
self.obj_info = obj_info
self.options = options
def display_list(self, columns, table):
ix = None
for i in range(0, len(columns)):
if columns[i] == 'name':
ix = i
if ix is not None:
res = []
for row in table:
res.append(row[ix])
print(" ".join(res))
FORMATS = {
# Special cased
'raw_json': None,
# Ditto
'pretty_json': None,
'tsv': TSVList,
'long': LongList,
'table': TableList,
'kvp': KeyValueList,
'bash': BashList
}
def write_payload_file(payload_file, json_list):
result = json.loads(json_list)[0]
payload = result['payload']
payload_encoding = result['payload_encoding']
with open(payload_file, 'wb') as f:
if payload_encoding == 'base64':
data = base64.b64decode(payload)
else:
data = payload
f.write(data.encode("utf-8"))
def print_bash_completion():
script = """# This is a bash completion script for rabbitmqadmin.
# Redirect it to a file, then source it or copy it to /etc/bash_completion.d
# to get tab completion. rabbitmqadmin must be on your PATH for this to work.
_rabbitmqadmin()
{
local cur prev opts base
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts="list show declare delete close purge import export get publish help"
fargs="--help --host --port --vhost --username --password --format --depth --sort --sort-reverse"
case "${prev}" in
list)
COMPREPLY=( $(compgen -W '""" + " ".join(LISTABLE) + """' -- ${cur}) )
return 0
;;
show)
COMPREPLY=( $(compgen -W '""" + " ".join(SHOWABLE) + """' -- ${cur}) )
return 0
;;
declare)
COMPREPLY=( $(compgen -W '""" + " ".join(DECLARABLE.keys()) + """' -- ${cur}) )
return 0
;;
delete)
COMPREPLY=( $(compgen -W '""" + " ".join(DELETABLE.keys()) + """' -- ${cur}) )
return 0
;;
close)
COMPREPLY=( $(compgen -W '""" + " ".join(CLOSABLE.keys()) + """' -- ${cur}) )
return 0
;;
purge)
COMPREPLY=( $(compgen -W '""" + " ".join(PURGABLE.keys()) + """' -- ${cur}) )
return 0
;;
export)
COMPREPLY=( $(compgen -f ${cur}) )
return 0
;;
import)
COMPREPLY=( $(compgen -f ${cur}) )
return 0
;;
help)
opts="subcommands config"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
-H)
COMPREPLY=( $(compgen -A hostname ${cur}) )
return 0
;;
--host)
COMPREPLY=( $(compgen -A hostname ${cur}) )
return 0
;;
-V)
opts="$(rabbitmqadmin -q -f bash list vhosts)"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
--vhost)
opts="$(rabbitmqadmin -q -f bash list vhosts)"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
-u)
opts="$(rabbitmqadmin -q -f bash list users)"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
--username)
opts="$(rabbitmqadmin -q -f bash list users)"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
-f)
COMPREPLY=( $(compgen -W \"""" + " ".join(FORMATS.keys()) + """\" -- ${cur}) )
return 0
;;
--format)
COMPREPLY=( $(compgen -W \"""" + " ".join(FORMATS.keys()) + """\" -- ${cur}) )
return 0
;;
"""
for l in LISTABLE:
key = l[0:len(l) - 1]
script += " " + key + """)
opts="$(rabbitmqadmin -q -f bash list """ + l + """)"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
"""
script += """ *)
;;
esac
COMPREPLY=($(compgen -W "${opts} ${fargs}" -- ${cur}))
return 0
}
complete -F _rabbitmqadmin rabbitmqadmin
"""
print(script)
if __name__ == "__main__":
main()
body { font: 12px Verdana, sans-serif; color: #484848; padding: 0; margin: 0; }
input, button, a.button { font: 12px Verdana, sans-serif; }
a { font-weight: bold; color: #444; text-decoration: none; }
a:hover { color: #F60; }
#outer { padding: 0 0 1em 0; width: 95%; margin: auto; }
#login table { margin: auto; }
#login p { text-align: center; }
#logo { padding: 0 0 2em 0; }
#logo img { margin: 1em 0 -0.3em 1em; border: none; }
#versions abbr { background: #f0f0f0; margin: 0 0 0 1em; }
.status-ok { }
.status-error { color: #F00; }
.status-timeout { color: #99EBFF; }
#debug { position: fixed; bottom: 0; z-index: 9; width: 100%; text-align: center; padding: 0; margin: 0; }
#debug p { background: #F60; color: white; margin: 0; padding: 1em; font-size: 2em; }
#header { background: white; position: fixed; z-index: 1; width: 95%; margin: auto; padding: 1em 0 0 0; border-bottom: 1px solid #666; }
#topnav { float: right; padding: 0; margin: 0; list-style-type: none; }
#topnav form { display: inline; }
#topnav input[type=submit] { padding: 3px 7px; display: inline; }
#topnav li { text-align: right; padding: 2px 0; }
#menu ul { padding: 0; margin: 0; overflow: auto; }
#menu li { float: left; list-style-type: none; padding: 0 0.1em 0 0; }
#menu li a { display: block; padding: 0.7em 1.3em; margin-right: 5px; }
#menu a:hover { background-color: #F60; color: white; -moz-border-radius: 8px 8px 0 0; border-radius: 8px 8px 0 0; }
#menu a.selected { background-color: #666; color: white; -moz-border-radius: 8px 8px 0 0; border-radius: 8px 8px 0 0; }
#vhost-form { float: right; padding: 0; margin: 0; }
#main { padding-top: 10em; }
#main.with-rhs { margin-right: 210px; }
#rhs { float: right; width: 200px; background-color: white; position: relative; padding-top: 10em; }
#rhs ul { padding: 0; margin: 10px 0 0 0; }
#rhs li { list-style-type: none; padding: 0; margin-bottom: 5px; }
#rhs a { display: block; padding: 0.7em; font-weight: bold; text-decoration: none; }
#rhs a:hover { background-color: #F60; color: white; -moz-border-radius: 8px 0 0 8px; border-radius: 8px 0 0 8px; }
#rhs a.selected { background-color: #666; color: white; -moz-border-radius: 8px 0 0 8px; border-radius: 8px 0 0 8px; }
h1 { font-size: 2em; font-weight: normal; padding: 0; margin-bottom: 0; }
b, dt { color: black; font-weight: normal; }
dd { margin-bottom: 5px; }
div.box, div.section, div.section-hidden { overflow: auto; width: 100%; }
.left { float: left; }
.right { float: right; }
.clear { clear: both; }
.shortinput { width: 50px; text-align: right; }
.help:after { content: '?'; }
.help,
.popup-options-link { background-color: #E4E4E4; padding: 2px 4px; cursor: pointer; }
table th .help,
table th .popup-options-link { border: none; }
.help:hover,
.popup-options-link:hover,
.popup-owner { background-color: #F60; color: white; }
.rate-visibility-option { cursor: pointer; padding: 4px; background: #fafafa; border: 1px solid #f0f0f0; border-radius: 3px; display:block; }
.rate-visibility-option:hover { background: #ddf;
background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #ddf),color-stop(1, #bbf));
border: 1px solid #88d;
border-radius: 3px; }
.rate-visibility-option-hidden { text-decoration: line-through; color: #888; }
table.legend { float: left; }
table.legend th { padding: 4px 10px 4px 0; width: 80px; }
table.legend td { padding: 4px 0 4px 10px; width: 130px; }
.tag-link, .argument-link { color: #444; cursor: pointer; font-weight: bold; }
.tag-link:hover, .argument-link:hover { color: #F60; }
.filter { overflow: auto; width: 100%; margin-bottom: 10px; }
.filter table { float: left; }
.filter label { margin-top: 4px;}
.filter input#filter-regex-mode { vertical-align: middle; }
.filter p#filter-truncate { float: right; padding: 4px; margin: 0; }
.filter p.filter-warning { border-radius: 5px; background: #ff8; }
.filter-active { background: #99EBFF; border-radius: 5px; }
.filter-highlight { background: #99EBFF; }
input#truncate { width: 50px; text-align: right; }
table { border-collapse: collapse; }
table th { font-weight: normal; color: black; }
table th, table td { font: 12px Verdana, sans-serif; padding: 2px 4px; }
table.list th, table.list td { vertical-align: top; min-width: 5em; width: auto; }
table.list { border-width: 1px; margin-bottom: 1em; }
table.list th, table.list td { border: 1px solid #ccc; }
table.list th { text-align: left; }
table.list th.plus-minus { border: none; min-width: 2em; }
table.list td a { display: block; color: black; text-decoration: none; font-weight: bold; }
table.list td a:hover { color: #F60; }
table.list th a.sort { display: block; width: 100%; cursor: pointer; color: black; font-weight: bold; }
table.list th a.sort .arrow { color: #F60; }
table.list td p { margin: 0; padding: 1px 0 0 0; }
table.list td p.warning { margin: 0; padding: 5px; }
table.list td.plain, table.list td.plain td, table.list td.plain th { border: none; background: none; }
table.list th.plain { border-left: none; border-top: none; border-right: none; background: none; }
table.list th.plain h3 { margin: 0; border: 0; }
#main .internal-purpose, #main .internal-purpose * { color: #aaa; }
div.section table.list, div.section-hidden table.list { margin-bottom: 0; }
div.memory-bar { margin: 10px 0 5px 0; border-radius: 5px; border: 1px solid #ddd; float: left; }
div.memory-section { float: left; height: 30px; }
div.colour-key { float: left; width: 10px; height: 10px; margin: 3px 5px 0 0;}
div.memory-info { float: left; padding: 10px 10px 0 0; }
button.memory-button { margin-top: 10px; }
div.memory_queue { background: #bd4688; }
div.memory_binary { background: url(../img/bg-binary.png); }
div.memory_conn { background: #dada66; }
div.memory_proc { background: #6abf59; }
div.memory_table { background: #6679da; }
div.memory_system { background: #999; }
div.memory_unused { background: #955; }
div.memory-bar div.memory_queue { border-right: solid 1px #eb50a6; }
div.memory-bar div.memory_binary { border-right: solid 1px #eb50a6; }
div.memory-bar div.memory_conn { border-right: solid 1px #ebeb8d; }
div.memory-bar div.memory_proc { border-right: solid 1px #79da66; }
div.memory-bar div.memory_table { border-right: solid 1px #8d9ceb; }
div.memory-bar div.memory_system { border-right: solid 1px #bbb; }
div.memory-bar div.memory_unused { border-right: solid 1px #bbb; }
sub { display: block; font-size: 0.8em; color: #888; }
small { font-size: 0.8em; color: #888; }
#main sub a { color: #888; }
#main sub a:hover { color: #444; }
table.argument-links { color: #888; }
table.argument-links td { vertical-align: top; }
.unknown { color: #888; }
table.facts { float: left; }
table.facts th, table.legend th { color: black; text-align: right; border-right: 1px solid #ccc; }
table.facts th, table.facts td { vertical-align: top; padding: 0 10px 10px 10px; }
table.facts th.horizontal { border-right: none; padding: 0 10px 5px 10px; }
table.facts-long th { text-align: right; font-weight: bold; }
table.facts-long th, table.facts-long td { vertical-align: top; }
table.facts-l { margin-right: 50px; }
table.mini th { border: none; padding: 0 2px 2px 2px; text-align: right; }
table.mini td { border: none; padding: 0 2px 2px 2px; }
tr.alt1>td {
background: #eee;
background: -moz-linear-gradient(center top, #f0f0f0 0%,#e0e0e0 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #f0f0f0),color-stop(1, #e0e0e0));
}
tr.alt2>td {
background: #fff;
background: -moz-linear-gradient(center top, #F8F8F8 0%,#ffffff 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #F8F8F8),color-stop(1, #ffffff));
}
td span,
td abbr {
display: inline-block;
padding: 3px 5px;
margin: 0 0 3px 0;
}
div.status-bar, div.status-red, div.status-yellow, div.status-green, div.status-grey { text-align: center; }
div.status-bar-main, div.status-red, div.status-yellow, div.status-green, div.status-grey { border-radius: 3px; -moz-border-radius: 3px; padding: 3px; }
div.status-bar sub { white-space: nowrap; }
div.status-bar .grey, div.status-grey { background: #ddd; }
div.status-bar .red, div.status-red { background: #ff7a7a; color: white; }
div.status-bar .yellow, div.status-yellow { background: #ffff7b; }
div.status-bar .green, div.status-green { background: #98f898; }
div.status-bar .red-dark { background: #e24545; color: white; }
/* yellow-dark and green-dark can never happen */
div.status-bar .red *, div.status-bar .red-dark *, div.status-red * { color: white; }
div.status-key-grey { background: #ddd; }
div.status-key-red { background: #ff7a7a; color: white; }
div.status-key-yellow { background: #ffff7b; }
div.status-key-green { background: #98f898; }
.l { text-align: left !important; }
.c { text-align: center !important; }
.r { text-align: right !important; }
.t { vertical-align: top !important; }
div.form-popup-warn,
div.form-popup-info,
div.form-popup-help,
div.form-popup-options {
-moz-border-radius: 5px 0 0 5px;
background: #EEE;
border-radius: 5px 0 0 5px;
border: 1px solid #ccc;
right: 0;
margin: 10px 0 10px 0;
padding: 15px;
position: fixed;
top: 0;
}
div.form-popup-warn,
div.form-popup-info,
div.form-popup-help {
-moz-border-radius: 5px;
border-radius: 5px;
left: 50%;
margin-left: -250px;
text-align: left;
top: 25%;
width: 500px;
z-index: 2;
}
p.warning, div.form-popup-warn { background: #FF9; }
div.form-popup-options { z-index: 3; }
div.form-popup-warn span,
div.form-popup-info span,
div.form-popup-help span,
div.form-popup-options span {
color: white;
background-color: #666;
cursor: pointer;
padding: 4px 8px;
border-radius: 5px;
-moz-border-radius: 5px;
}
div.form-popup-warn span:hover,
div.form-popup-info span:hover,
div.form-popup-help span:hover,
div.form-popup-options span:hover {
background-color: #F60;
cursor: pointer;
}
p.warning { padding: 15px; border-radius: 5px; -moz-border-radius: 5px; text-align: center; }
.highlight { min-width: 120px; font-size: 120%; text-align:center; padding:10px; background-color: #ddd; margin: 0 20px 0 0; color: #888; border-radius: 5px; -moz-border-radius: 5px; }
.highlight strong { font-size: 2em; display: block; color: #444; font-weight: normal; }
.highlight { float: left; }
.chart { margin: 0 20px 20px 0; float: left; }
.chart-small { width: 400px; height: 100px; }
.chart-medium { width: 600px; height: 200px; }
.chart-large { width: 800px; height: 300px; }
ul#global-counts { overflow: hidden; list-style-type: none; margin: 0; padding: 0; }
ul#global-counts li { float: left; margin: 0 1em 0 0; }
div.section, div.section-hidden { margin: 0 0 1em 0; }
div.section-invisible div.hider { display: none; }
div.section div.hider, div.section-hidden div.hider { padding: 0.5em 0; }
div.section h2, div.section-hidden h2 { font-size: 1em; padding: 5px 5px 5px 25px; cursor: pointer; margin: 0; }
div.section h2:hover, div.section-hidden h2:hover { color: black; }
div.section-invisible h2 { background: white; background-image: url(../img/collapse.png); background-repeat:no-repeat; background-position:4px 4px; }
div.section-visible h2 { background: #F8F8F8; background-image: url(../img/expand.png); background-repeat:no-repeat; background-position:4px 4px; }
form { margin: 0; }
form.inline-form { float: left; }
form.inline-form-right { float: right; padding-left: 5px; }
input, select { padding: 0.2em; }
input[type=text], input[type=password] { font: 1.1em Andale Mono, Lucidatypewriter, Courier New, Courier, monospace; border: 1px solid #ccc; }
textarea { width: 600px; height: 200px; border: 1px solid #ccc; }
.mand { color: #f88; padding: 0 5px;}
input[type=submit].wait { cursor: wait; }
table.form { margin-bottom: 0.5em; }
table.form th { text-align: right; vertical-align: top; }
table.form input[type=text], table.form input[type=password] { width: 200px; }
table.form input[type=text].wide, table.form input[type=password].wide { width: 300px; }
table.form select { width: auto; }
table.form select.narrow { width: 110px; }
table.form .multifield { margin: 0; padding: 0; }
table.form .multifield td { margin: 0; padding: 0; vertical-align: top; }
table.form .multifield td.equals { padding: 3px; }
table.form .multifield td input { float: left; }
table.form .multifield td select { width: auto; display: block; float: left; margin-left: 5px; }
table.form label { margin-top: 5px; display: block; }
table.form table.subform { margin-bottom: 5px; }
table.form table.subform th { text-align: left; }
table.form table.subform th, table.form table.subform td { padding: 0; }
.multifield-sub { border: 1px solid #ddd; background: #F8F8F8; padding: 10px; border-radius: 5px; -moz-border-radius: 5px; float: left; margin-bottom: 10px; }
label.radio, label.checkbox { padding: 5px; cursor: pointer; border-radius: 5px; -moz-border-radius: 5px; border: 1px solid #ccc; }
table.two-col-layout { width: 100%; }
table.two-col-layout > tbody > tr > td { width: 50%; vertical-align: top; }
input[type=submit], button, a.button { padding: 8px; border-radius: 5px; -moz-border-radius: 5px; text-decoration: none !important; cursor: pointer; display: block; font-weight: normal !important; }
table.list input[type=submit], table.list button { padding: 3px 7px; margin: 0 0 3px 0; }
table.list input[type=submit], table.list button, table.list a.button { padding: 3px 7px; margin: 0 0 3px 0; }
input[type=submit], button, a.button {
background: #666;
color: #FFF !important;
border: 0;
}
input[type=submit]:hover, button:hover, a.button:hover {
background: #F60;
text-decoration: none !important;
}
input[type=submit][disabled], button[disabled], a.button.disabled { pointer-events: none; background: #aaa; }
input[type=submit][disabled]:hover, button[disabled]:hover, a.button.disabled { background: #aaa; }
h3 { padding: 0 0 2px 0; margin: 1em 0 1em 0; font-size: 1em; border-bottom: 1px solid #E4E4E4; font-weight: normal; }
abbr { background: #99EBFF; padding: 3px 5px; border-radius: 5px; -moz-border-radius: 5px; border: none; cursor: default; text-decoration: none; }
table.list td abbr a { display: inline; width: auto; }
abbr.warning { background: red; }
.status-red abbr, .status-yellow abbr, .status-green abbr, .status-grey abbr, small abbr, abbr.normal { background: none; color: inherit; padding: 0; border-bottom: 1px dotted; cursor: default; }
abbr.status-grey { background: #ddd; }
abbr.status-green { background: #98f898; }
abbr.status-yellow { background: #ffff7b; }
abbr.status-red { background: #ff7a7a; color: white; }
abbr.type { background: none; color: inherit; padding: 0; border-bottom: 1px dotted #ddd; cursor: default; }
div.bindings-wrapper { display: inline-block; }
div.bindings-wrapper table { margin: auto; }
div.bindings-wrapper p { margin: 10px; text-align: center; }
div.bindings-wrapper span.exchange { border: 1px solid #bbb; padding: 10px; border-radius: 5px; -moz-border-radius: 5px; }
div.bindings-wrapper span.queue { border: 1px solid #666; padding: 10px; }
div.bindings-wrapper td span.exchange, div.bindings-wrapper td span.queue { background: white; display: block; }
div.bindings-wrapper span.exchange a, div.bindings-wrapper span.queue a { font-weight: normal !important; }
div.bindings-wrapper p.arrow { font-size: 200%; }
#footer { overflow: auto; width: 100%; border-top: 1px solid #666; }
#footer ul { list-style-type: none; padding: 0; margin: 0; }
#footer ul li { float: left; }
#footer ul li a { display: block; padding: 0.7em 1em; }
#scratch { display: none; }
.highlight, .mini-highlight, .micro-highlight {
background: -moz-linear-gradient(center top, #f0f0f0 0%,#e0e0e0 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #f0f0f0),color-stop(1, #e0e0e0));
border: 1px solid #e0e0e0;
}
table.dynamic-shovels td label {width: 200px; margin-right:10px;padding: 4px 0px 5px 0px}
<!doctype html>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<html>
<head>
<title>RabbitMQ Management</title>
<script src="js/ejs-1.0.min.js" type="text/javascript"></script>
<script src="js/jquery-3.5.1.min.js"></script>
<script src="js/jquery.flot-0.8.1.min.js" type="text/javascript"></script>
<script src="js/jquery.flot-0.8.1.time.min.js" type="text/javascript"></script>
<script src="js/sammy-0.7.6.min.js" type="text/javascript"></script>
<script src="js/json2-2016.10.28.js" type="text/javascript"></script>
<script src="js/base64.js" type="text/javascript"></script>
<script src="js/global.js" type="text/javascript"></script>
<script src="js/main.js" type="text/javascript"></script>
<script src="js/prefs.js" type="text/javascript"></script>
<script src="js/formatters.js" type="text/javascript"></script>
<script src="js/charts.js" type="text/javascript"></script>
<link href="css/main.css" rel="stylesheet" type="text/css"/>
<link href="favicon.ico" rel="shortcut icon" type="image/x-icon"/>
<!--[if lte IE 8]>
<script src="js/excanvas.min.js" type="text/javascript"></script>
<link href="css/evil.css" rel="stylesheet" type="text/css"/>
<![endif]-->
</head>
<body>
<div id="outer"></div>
<div id="debug"></div>
<div id="scratch"></div>
</body>
</html>
/*
* Copyright (c) 2010 Nick Galbreath
* https://code.google.com/p/stringencoders/source/browse/#svn/trunk/javascript
*
* 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.
*/
/* base64 encode/decode compatible with window.btoa/atob
*
* window.atob/btoa is a Firefox extension to convert binary data (the "b")
* to base64 (ascii, the "a").
*
* It is also found in Safari and Chrome. It is not available in IE.
*
* if (!window.btoa) window.btoa = base64.encode
* if (!window.atob) window.atob = base64.decode
*
* The original spec's for atob/btoa are a bit lacking
* https://developer.mozilla.org/en/DOM/window.atob
* https://developer.mozilla.org/en/DOM/window.btoa
*
* window.btoa and base64.encode takes a string where charCodeAt is [0,255]
* If any character is not [0,255], then an exception is thrown.
*
* window.atob and base64.decode take a base64-encoded string
* If the input length is not a multiple of 4, or contains invalid characters
* then an exception is thrown.
*/
base64 = {};
base64.PADCHAR = '=';
base64.ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
base64.getbyte64 = function(s,i) {
// This is oddly fast, except on Chrome/V8.
// Minimal or no improvement in performance by using a
// object with properties mapping chars to value (eg. 'A': 0)
var idx = base64.ALPHA.indexOf(s.charAt(i));
if (idx == -1) {
throw "Cannot decode base64";
}
return idx;
}
base64.decode = function(s) {
// convert to string
s = "" + s;
var getbyte64 = base64.getbyte64;
var pads, i, b10;
var imax = s.length
if (imax == 0) {
return s;
}
if (imax % 4 != 0) {
throw "Cannot decode base64";
}
pads = 0
if (s.charAt(imax -1) == base64.PADCHAR) {
pads = 1;
if (s.charAt(imax -2) == base64.PADCHAR) {
pads = 2;
}
// either way, we want to ignore this last block
imax -= 4;
}
var x = [];
for (i = 0; i < imax; i += 4) {
b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) |
(getbyte64(s,i+2) << 6) | getbyte64(s,i+3);
x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 0xff, b10 & 0xff));
}
switch (pads) {
case 1:
b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) | (getbyte64(s,i+2) << 6)
x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 0xff));
break;
case 2:
b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12);
x.push(String.fromCharCode(b10 >> 16));
break;
}
return x.join('');
}
base64.getbyte = function(s,i) {
var x = s.charCodeAt(i);
if (x > 255) {
throw "INVALID_CHARACTER_ERR: DOM Exception 5";
}
return x;
}
base64.encode = function(s) {
if (arguments.length != 1) {
throw "SyntaxError: Not enough arguments";
}
var padchar = base64.PADCHAR;
var alpha = base64.ALPHA;
var getbyte = base64.getbyte;
var i, b10;
var x = [];
// convert to string
s = "" + s;
var imax = s.length - s.length % 3;
if (s.length == 0) {
return s;
}
for (i = 0; i < imax; i += 3) {
b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8) | getbyte(s,i+2);
x.push(alpha.charAt(b10 >> 18));
x.push(alpha.charAt((b10 >> 12) & 0x3F));
x.push(alpha.charAt((b10 >> 6) & 0x3f));
x.push(alpha.charAt(b10 & 0x3f));
}
switch (s.length - imax) {
case 1:
b10 = getbyte(s,i) << 16;
x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
padchar + padchar);
break;
case 2:
b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8);
x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
alpha.charAt((b10 >> 6) & 0x3f) + padchar);
break;
}
return x.join('');
}
//
// Formatting side
//
function message_rates(id, stats) {
var items = [['Publish', 'publish'],
['Publisher confirm', 'confirm'],
['Publish (In)', 'publish_in'],
['Publish (Out)', 'publish_out'],
['Deliver (manual ack)', 'deliver'],
['Deliver (auto ack)', 'deliver_no_ack'],
['Consumer ack', 'ack'],
['Redelivered', 'redeliver'],
['Get (manual ack)', 'get'],
['Get (auto ack)', 'get_no_ack'],
['Return', 'return_unroutable'],
['Disk read', 'disk_reads'],
['Disk write', 'disk_writes']];
return rates_chart_or_text(id, stats, items, fmt_rate, fmt_rate_axis, true, 'Message rates', 'message-rates');
}
function queue_lengths(id, stats) {
var items = [['Ready', 'messages_ready'],
['Unacked', 'messages_unacknowledged'],
['Total', 'messages']];
return rates_chart_or_text(id, stats, items, fmt_num_thousands, fmt_plain_axis, false, 'Queued messages', 'queued-messages');
}
function data_rates(id, stats) {
var items = [['From client', 'recv_oct'], ['To client', 'send_oct']];
return rates_chart_or_text(id, stats, items, fmt_rate_bytes, fmt_rate_bytes_axis, true, 'Data rates');
}
function data_reductions(id, stats) {
var items = [['Reductions', 'reductions']];
return rates_chart_or_text(id, stats, items, fmt_rate, fmt_rate_axis, true, 'Reductions (per second)', 'process-reductions');
}
function rates_chart_or_text(id, stats, items, fmt, axis_fmt, chart_rates,
heading, heading_help) {
var prefix = chart_h3(id, heading, heading_help);
return prefix + rates_chart_or_text_no_heading(
id, id, stats, items, fmt, axis_fmt, chart_rates);
}
function rates_chart_or_text_no_heading(type_id, id, stats, items,
fmt, axis_fmt, chart_rates) {
var mode = get_pref('rate-mode-' + type_id);
var range = get_pref('chart-range');
var res;
if (keys(stats).length > 0) {
if (mode == 'chart') {
res = rates_chart(
type_id, id, items, stats, fmt, axis_fmt, 'full', chart_rates);
}
else {
res = rates_text(items, stats, mode, fmt, chart_rates);
}
if (res == "") res = '<p>Waiting for data...</p>';
}
else {
res = '<p>Currently idle</p>';
}
return res;
}
function chart_h3(id, heading, heading_help) {
var mode = get_pref('rate-mode-' + id);
var range = get_pref('chart-range');
return '<h3>' + heading +
' <span class="popup-options-link" title="Click to change" ' +
'type="rate" for="' + id + '">' + prefix_title(mode, range) +
'</span>' + (heading_help == undefined ? '' :
' <span class="help" id="' + heading_help + '"></span>') +
'</h3>';
}
function prefix_title(mode, range) {
var desc = ALL_CHART_RANGES[range];
if (mode == 'chart') {
return desc.toLowerCase();
}
else if (mode == 'curr') {
return 'current value';
}
else {
return 'moving average: ' + desc.toLowerCase();
}
}
function node_stat_count(used_key, limit_key, stats, thresholds) {
var used = stats[used_key];
var limit = stats[limit_key];
if (typeof used == 'number') {
return node_stat(used_key, 'Used', limit_key, 'available', stats,
fmt_plain, fmt_plain_axis,
fmt_color(used / limit, thresholds));
} else {
return used;
}
}
function node_stat_count_bar(used_key, limit_key, stats, thresholds) {
var used = stats[used_key];
var limit = stats[limit_key];
if (typeof used == 'number') {
return node_stat_bar(used_key, limit_key, 'available', stats,
fmt_plain_axis,
fmt_color(used / limit, thresholds));
} else {
return used;
}
}
function node_stat(used_key, used_name, limit_key, suffix, stats, fmt,
axis_fmt, colour, help, invert) {
if (get_pref('rate-mode-node-stats') == 'chart') {
var items = [[used_name, used_key], ['Limit', limit_key]];
add_fake_limit_details(used_key, limit_key, stats);
return rates_chart('node-stats', 'node-stats-' + used_key, items, stats,
fmt, axis_fmt, 'node', false);
} else {
return node_stat_bar(used_key, limit_key, suffix, stats, axis_fmt,
colour, help, invert);
}
}
function add_fake_limit_details(used_key, limit_key, stats) {
var source = stats[used_key + '_details'].samples;
var limit = stats[limit_key];
var dest = [];
for (var i in source) {
dest[i] = {sample: limit, timestamp: source[i].timestamp};
}
stats[limit_key + '_details'] = {samples: dest};
}
function node_stat_bar(used_key, limit_key, suffix, stats, fmt, colour,
help, invert) {
var used = stats[used_key];
var limit = stats[limit_key];
var width = 120;
var res = '';
var other_colour = colour;
var ratio = invert ? (limit / used) : (used / limit);
if (ratio > 1) {
ratio = 1 / ratio;
inverted = true;
colour += '-dark';
}
else {
other_colour += '-dark';
}
var offset = Math.round(width * (1 - ratio));
res += '<div class="status-bar" style="width: ' + width + 'px;">';
res += '<div class="status-bar-main ' + colour + '" style="background-image: url(img/bg-' + other_colour + '.png); background-position: -' + offset + 'px 0px; background-repeat: no-repeat;">';
res += fmt(used);
if (help != null) {
res += ' <span class="help" id="' + help + '"></span>';
}
res += '</div>'; // status-bar-main
res += '<sub>' + fmt(limit) + ' ' + suffix + '</sub>';
res += '</div>'; // status-bar
return res;
}
function node_stats_prefs() {
return chart_h3('node-stats', 'Node statistics');
}
function rates_chart(type_id, id, items, stats, fmt, axis_fmt, type,
chart_rates) {
function show(key) {
return get_pref('chart-line-' + id + key) === 'true';
}
var size = get_pref('chart-size-' + type_id);
var legend = [];
chart_data[id] = {};
chart_data[id]['data'] = {};
chart_data[id]['fmt'] = axis_fmt;
var ix = 0;
for (var i in items) {
var name = items[i][0];
var key = items[i][1];
var key_details = key + '_details';
if (key_details in stats) {
if (show(key)) {
chart_data[id]['data'][name] = stats[key_details];
chart_data[id]['data'][name].ix = ix;
}
var value = chart_rates ? pick_rate(fmt, stats, key) :
pick_abs(fmt, stats, key);
legend.push({name: name,
key: key,
value: value,
show: show(key)});
ix++;
}
}
var html = '<div class="box"><div id="chart-' + id +
'" class="chart chart-' + type + ' chart-' + size +
(chart_rates ? ' chart-rates' : '') + '"></div>';
html += '<table class="legend">';
for (var i = 0; i < legend.length; i++) {
if (i % 3 == 0 && i < legend.length - 1) {
html += '</table><table class="legend">';
}
html += '<tr><th><span title="Click to toggle line" ';
html += 'class="rate-visibility-option';
html += legend[i].show ? '' : ' rate-visibility-option-hidden';
html += '" data-pref="chart-line-' + id + legend[i].key + '">';
html += legend[i].name + '</span></th><td>';
html += '<div class="colour-key" style="background: ' + chart_colors[type][i];
html += ';"></div>' + legend[i].value + '</td></tr>'
}
html += '</table></div>';
return legend.length > 0 ? html : '';
}
function rates_text(items, stats, mode, fmt, chart_rates) {
var res = '';
for (var i in items) {
var name = items[i][0];
var key = items[i][1];
var key_details = key + '_details';
if (key_details in stats) {
var details = stats[key_details];
res += '<div class="highlight">' + name + '<strong>';
res += chart_rates ? pick_rate(fmt, stats, key, mode) :
pick_abs(fmt, stats, key, mode);
res += '</strong></div>';
}
}
return res == '' ? '' : '<div class="box">' + res + '</div>';
}
//
// Rendering side
//
function render_charts() {
$('.chart').map(function() {
render_chart($(this));
});
}
var chart_colors = {full: ['#edc240', '#afd8f8', '#cb4b4b', '#4da74d', '#9440ed', '#666666', '#aaaaaa',
'#7c79c3', '#8e6767', '#67808e', '#e5e4ae', '#4b4a55', '#bba0c1'],
node: ['#6ae26a', '#e24545']};
var chart_chrome = {
series: { lines: { show: true } },
grid: { borderWidth: 2, borderColor: "#aaa" },
xaxis: { tickColor: "#fff", mode: "time", timezone: "browser" },
yaxis: { tickColor: "#eee", min: 0 },
legend: { show: false }
};
function chart_fill(mode, i) {
return mode =='node' && i == 0;
}
function render_chart(div) {
var id = div.attr('id').substring('chart-'.length);
var rate_mode = div.hasClass('chart-rates');
var out_data = [];
var data = chart_data[id]['data'];
var fmt = chart_data[id]['fmt'];
var mode = div.hasClass('chart-full') ? 'full': 'node';
var colors = chart_colors[mode];
for (var name in data) {
var series = data[name];
var samples = series.samples;
var i = series.ix;
var d = [];
for (var j = 1; j < samples.length; j++) {
var x = samples[j].timestamp;
var y;
if (rate_mode) {
// TODO This doesn't work well if you are looking at
// stuff in the browser that is finer granularity than
// the data we have in the DB (and thus we get
// duplicated entries). Do we care? We should just
// never allow that...
y = (samples[j - 1].sample - samples[j].sample) * 1000 /
(samples[j - 1].timestamp - samples[j].timestamp);
}
else {
y = samples[j].sample;
}
d.push([x, y]);
}
out_data.push({data: d, color: colors[i], shadowSize: 0,
lines: {show: true, fill: chart_fill(mode, i)}});
}
chart_data[id] = {};
chart_chrome.yaxis.tickFormatter = fmt_y_axis(fmt);
$.plot(div, out_data, chart_chrome);
}
function fmt_y_axis(fmt) {
return function (val, axis) {
// axis.ticks seems to include the bottom value but not the top
if (axis.max == 1 && axis.ticks.length > 1) {
var newTicks = [axis.ticks[0]];
axis.ticks = newTicks;
}
return fmt(val, axis.max);
}
}
function update_rate_options(sammy) {
var id = sammy.params['id'];
store_pref('rate-mode-' + id, sammy.params['mode']);
store_pref('chart-size-' + id, sammy.params['size']);
store_pref('chart-range', sammy.params['range']);
partial_update();
}
dispatcher_add(function(sammy) {
function path(p, r, t) {
sammy.get(p, function() {
render(r, t, p);
});
}
sammy.get('#/', function() {
var reqs = {'overview': {path: '/overview',
options: {ranges: ['lengths-over',
'msg-rates-over']}},
'vhosts': '/vhosts'};
if (user_monitor) {
reqs['nodes'] = '/nodes';
}
render(reqs, 'overview', '#/');
});
sammy.get('#/login/:username/:password', login_route);
path('#/cluster-name', {'cluster_name': '/cluster-name'}, 'cluster-name');
sammy.put('#/cluster-name', function() {
if (sync_put(this, '/cluster-name')) {
setup_global_vars();
update();
}
return false;
});
sammy.get('#/nodes/:name', function() {
var name = esc(this.params['name']);
render({'node': {path: '/nodes/' + name,
options: {ranges: ['node-stats']}}},
'node', '');
});
sammy.get('#/connections', function() {
renderConnections();
});
sammy.get('#/connections/:name', function() {
var name = esc(this.params['name']);
render({'connection': {path: '/connections/' + name,
options: {ranges: ['data-rates-conn']}},
'channels': '/connections/' + name + '/channels'},
'connection', '#/connections');
});
sammy.del('#/connections', function() {
var options = {headers: {
'X-Reason': this.params['reason']
}};
if (sync_delete(this, '/connections/:name', options)) {
go_to('#/connections');
}
return false;
});
sammy.get('#/channels', function() {
renderChannels();
});
sammy.get('#/channels/:name', function() {
render({'channel': {path: '/channels/' + esc(this.params['name']),
options:{ranges:['data-rates-ch','msg-rates-ch']}}},
'channel', '#/channels');
});
sammy.get('#/exchanges', function() {
renderExchanges();
});
sammy.get('#/exchanges/:vhost/:name', function() {
var path = '/exchanges/' + esc(this.params['vhost']) + '/' + esc(this.params['name']);
render({'exchange': {path: path,
options: {ranges:['msg-rates-x']}},
'bindings_source': path + '/bindings/source',
'bindings_destination': path + '/bindings/destination'},
'exchange', '#/exchanges');
});
sammy.put('#/exchanges', function() {
if (sync_put(this, '/exchanges/:vhost/:name'))
update();
return false;
});
sammy.del('#/exchanges', function() {
if (sync_delete(this, '/exchanges/:vhost/:name'))
go_to('#/exchanges');
return false;
});
sammy.post('#/exchanges/publish', function() {
publish_msg(this.params);
return false;
});
sammy.get('#/queues', function() {
renderQueues();
});
sammy.get('#/queues/:vhost/:name', function() {
var path = '/queues/' + esc(this.params['vhost']) + '/' + esc(this.params['name']);
render({'queue': {path: path,
options: {ranges:['lengths-q', 'msg-rates-q', 'data-rates-q']}},
'bindings': path + '/bindings'}, 'queue', '#/queues');
});
sammy.put('#/queues', function() {
if (sync_put(this, '/queues/:vhost/:name'))
update();
return false;
});
sammy.del('#/queues', function() {
if (this.params['mode'] == 'delete') {
if (sync_delete(this, '/queues/:vhost/:name'))
go_to('#/queues');
}
else if (this.params['mode'] == 'purge') {
if (sync_delete(this, '/queues/:vhost/:name/contents')) {
show_popup('info', "Queue purged");
partial_update();
}
}
return false;
});
sammy.post('#/queues/get', function() {
get_msgs(this.params);
return false;
});
sammy.post('#/queues/actions', function() {
if (sync_post(this, '/queues/:vhost/:name/actions'))
// We can't refresh fast enough, it's racy. So grey
// the button and wait for a normal refresh.
$('#action-button').addClass('wait').prop('disabled', true);
return false;
});
sammy.post('#/bindings', function() {
if (sync_post(this, '/bindings/:vhost/e/:source/:destination_type/:destination'))
update();
return false;
});
sammy.del('#/bindings', function() {
if (sync_delete(this, '/bindings/:vhost/e/:source/:destination_type/:destination/:properties_key'))
update();
return false;
});
path('#/vhosts', {'vhosts': {path: '/vhosts',
options: {sort:true}},
'permissions': '/permissions'}, 'vhosts');
sammy.get('#/vhosts/:id', function() {
render({'vhost': {path: '/vhosts/' + esc(this.params['id']),
options: {ranges: ['lengths-vhost',
'msg-rates-vhost',
'data-rates-vhost']}},
'permissions': '/vhosts/' + esc(this.params['id']) + '/permissions',
'topic_permissions': '/vhosts/' + esc(this.params['id']) + '/topic-permissions',
'users': '/users/',
'exchanges' : '/exchanges/' + esc(this.params['id'])},
'vhost', '#/vhosts');
});
sammy.put('#/vhosts', function() {
if (sync_put(this, '/vhosts/:name')) {
update_vhosts();
update();
}
return false;
});
sammy.del('#/vhosts', function() {
if (sync_delete(this, '/vhosts/:name')) {
update_vhosts();
go_to('#/vhosts');
}
return false;
});
path('#/users', {'users': {path: '/users',
options: {sort:true}},
'permissions': '/permissions'}, 'users');
sammy.get('#/users/:id', function() {
var vhosts = JSON.parse(sync_get('/vhosts'));
render({'user': '/users/' + esc(this.params['id']),
'permissions': '/users/' + esc(this.params['id']) + '/permissions',
'topic_permissions': '/users/' + esc(this.params['id']) + '/topic-permissions',
'vhosts': '/vhosts/',
'exchanges': '/exchanges/' + esc(vhosts[0].name)}, 'user',
'#/users');
});
sammy.put('#/users-add', function() {
res = sync_put(this, '/users/:username');
if (res) {
if (res.http_status === 204) {
username = res.req_params.username;
show_popup('warn', "Updated an existing user: '" + username + "'");
}
update();
}
return false;
});
sammy.put('#/users-modify', function() {
if (sync_put(this, '/users/:username'))
go_to('#/users');
return false;
});
sammy.del('#/users', function() {
if (sync_delete(this, '/users/:username'))
go_to('#/users');
return false;
});
path('#/feature-flags', {'feature_flags': {path: '/feature-flags',
options: {sort:true}},
'permissions': '/permissions'}, 'feature-flags');
sammy.put('#/feature-flags-enable', function() {
if (sync_put(this, '/feature-flags/:name/enable'))
update();
return false;
});
sammy.put('#/permissions', function() {
if (sync_put(this, '/permissions/:vhost/:username'))
update();
return false;
});
sammy.del('#/permissions', function() {
if (sync_delete(this, '/permissions/:vhost/:username'))
update();
return false;
});
sammy.put('#/topic-permissions', function() {
if (sync_put(this, '/topic-permissions/:vhost/:username'))
update();
return false;
});
sammy.del('#/topic-permissions', function() {
if (sync_delete(this, '/topic-permissions/:vhost/:username/:exchange'))
update();
return false;
});
path('#/policies', {'policies': '/policies',
'operator_policies': '/operator-policies',
'vhosts': '/vhosts'}, 'policies');
sammy.get('#/policies/:vhost/:id', function() {
render({'policy': '/policies/' + esc(this.params['vhost'])
+ '/' + esc(this.params['id'])},
'policy', '#/policies');
});
sammy.put('#/policies', function() {
put_cast_params(this, '/policies/:vhost/:name',
['name', 'pattern', 'policy'], ['priority'], []);
return false;
});
sammy.del('#/policies', function() {
if (sync_delete(this, '/policies/:vhost/:name'))
go_to('#/policies');
return false;
});
sammy.put('#/operator_policies', function() {
this.params = rename_multifield(this.params, "definitionop", "definition");
put_cast_params(this, '/operator-policies/:vhost/:name',
['name', 'pattern', 'policy'], ['priority'], []);
return false;
});
sammy.del('#/operator_policies', function() {
if (sync_delete(this, '/operator-policies/:vhost/:name'))
update();
});
sammy.put('#/logout', function() {
// clear a local storage value used by earlier versions
clear_pref('auth');
clear_cookie_value('auth');
location.reload();
});
sammy.put('#/rate-options', function() {
update_rate_options(this);
});
sammy.put('#/column-options', function() {
update_column_options(this);
});
path('#/limits', {'limits': '/vhost-limits',
'vhosts': '/vhosts'}, 'limits');
sammy.put('#/limits', function() {
var valAsInt = parseInt(this.params.value);
if (isNaN(valAsInt)) {
var e = 'Validation failed\n\n' +
this.params.name + ' should be a number, actually was "' +
this.params.value + '"';
show_popup('warn', fmt_escape_html(e));
} else {
this.params.value = valAsInt;
if (sync_put(this, '/vhost-limits/:vhost/:name')) {
update();
}
}
});
sammy.post('#/restart_vhost', function(){
if(sync_post(this, '/vhosts/:vhost/start/:node')) update();
})
sammy.del('#/limits', function() {
if (sync_delete(this, '/vhost-limits/:vhost/:name')) update();
});
sammy.del("#/reset", function(){
if(sync_delete(this, '/reset')){
update();
}
});
sammy.del("#/reset_node", function(){
if(sync_delete(this, '/reset/:node')){
update();
}
});
});
(function(){
var rsplit = function(string, regex) {
var result = regex.exec(string),retArr = new Array(), first_idx, last_idx, first_bit;
while (result != null)
{
first_idx = result.index; last_idx = regex.lastIndex;
if ((first_idx) != 0)
{
first_bit = string.substring(0,first_idx);
retArr.push(string.substring(0,first_idx));
string = string.slice(first_idx);
}
retArr.push(result[0]);
string = string.slice(result[0].length);
result = regex.exec(string);
}
if (! string == '')
{
retArr.push(string);
}
return retArr;
},
chop = function(string){
return string.substr(0, string.length - 1);
},
extend = function(d, s){
for(var n in s){
if(s.hasOwnProperty(n)) d[n] = s[n]
}
}
EJS = function( options ){
options = typeof options == "string" ? {view: options} : options
this.set_options(options);
if(options.precompiled){
this.template = {};
this.template.process = options.precompiled;
EJS.update(this.name, this);
return;
}
if(options.element)
{
if(typeof options.element == 'string'){
var name = options.element
options.element = document.getElementById( options.element )
if(options.element == null) throw name+'does not exist!'
}
if(options.element.value){
this.text = options.element.value
}else{
this.text = options.element.innerHTML
}
this.name = options.element.id
this.type = '['
}else if(options.url){
options.url = EJS.endExt(options.url, this.extMatch);
this.name = this.name ? this.name : options.url;
var url = options.url
//options.view = options.absolute_url || options.view || options.;
var template = EJS.get(this.name /*url*/, this.cache);
if (template) return template;
if (template == EJS.INVALID_PATH) return null;
try{
this.text = EJS.request( url+(this.cache ? '' : '?'+Math.random() ));
}catch(e){}
if(this.text == null){
throw( {type: 'EJS', message: 'There is no template at '+url} );
}
//this.name = url;
}
var template = new EJS.Compiler(this.text, this.type);
template.compile(options, this.name);
EJS.update(this.name, this);
this.template = template;
};
/* @Prototype*/
EJS.prototype = {
/**
* Renders an object with extra view helpers attached to the view.
* @param {Object} object data to be rendered
* @param {Object} extra_helpers an object with additonal view helpers
* @return {String} returns the result of the string
*/
render : function(object, extra_helpers){
object = object || {};
this._extra_helpers = extra_helpers;
var v = new EJS.Helpers(object, extra_helpers || {});
return this.template.process.call(object, object,v);
},
update : function(element, options){
if(typeof element == 'string'){
element = document.getElementById(element)
}
if(options == null){
_template = this;
return function(object){
EJS.prototype.update.call(_template, element, object)
}
}
if(typeof options == 'string'){
params = {}
params.url = options
_template = this;
params.onComplete = function(request){
var object = eval( request.responseText )
EJS.prototype.update.call(_template, element, object)
}
EJS.ajax_request(params)
}else
{
element.innerHTML = this.render(options)
}
},
out : function(){
return this.template.out;
},
/**
* Sets options on this view to be rendered with.
* @param {Object} options
*/
set_options : function(options){
this.type = options.type || EJS.type;
this.cache = options.cache != null ? options.cache : EJS.cache;
this.text = options.text || null;
this.name = options.name || null;
this.ext = options.ext || EJS.ext;
this.extMatch = new RegExp(this.ext.replace(/\./, '\.'));
}
};
EJS.endExt = function(path, match){
if(!path) return null;
match.lastIndex = 0
return path+ (match.test(path) ? '' : this.ext )
}
/* @Static*/
EJS.Scanner = function(source, left, right) {
extend(this,
{left_delimiter: left +'%',
right_delimiter: '%'+right,
double_left: left+'%%',
double_right: '%%'+right,
left_equal: left+'%=',
left_comment: left+'%#'})
this.SplitRegexp = left=='[' ? /(\[%%)|(%%\])|(\[%=)|(\[%#)|(\[%)|(%\]\n)|(%\])|(\n)/ : new RegExp('('+this.double_left+')|(%%'+this.double_right+')|('+this.left_equal+')|('+this.left_comment+')|('+this.left_delimiter+')|('+this.right_delimiter+'\n)|('+this.right_delimiter+')|(\n)') ;
this.source = source;
this.stag = null;
this.lines = 0;
};
EJS.Scanner.to_text = function(input){
if(input == null || input === undefined)
return '';
if(input instanceof Date)
return input.toDateString();
if(input.toString)
return input.toString();
return '';
};
EJS.Scanner.prototype = {
scan: function(block) {
scanline = this.scanline;
regex = this.SplitRegexp;
if (! this.source == '')
{
var source_split = rsplit(this.source, /\n/);
for(var i=0; i<source_split.length; i++) {
var item = source_split[i];
this.scanline(item, regex, block);
}
}
},
scanline: function(line, regex, block) {
this.lines++;
var line_split = rsplit(line, regex);
for(var i=0; i<line_split.length; i++) {
var token = line_split[i];
if (token != null) {
try{
block(token, this);
}catch(e){
throw {type: 'EJS.Scanner', line: this.lines};
}
}
}
}
};
EJS.Buffer = function(pre_cmd, post_cmd) {
this.line = new Array();
this.script = "";
this.pre_cmd = pre_cmd;
this.post_cmd = post_cmd;
for (var i=0; i<this.pre_cmd.length; i++)
{
this.push(pre_cmd[i]);
}
};
EJS.Buffer.prototype = {
push: function(cmd) {
this.line.push(cmd);
},
cr: function() {
this.script = this.script + this.line.join('; ');
this.line = new Array();
this.script = this.script + "\n";
},
close: function() {
if (this.line.length > 0)
{
for (var i=0; i<this.post_cmd.length; i++){
this.push(pre_cmd[i]);
}
this.script = this.script + this.line.join('; ');
line = null;
}
}
};
EJS.Compiler = function(source, left) {
this.pre_cmd = ['var ___ViewO = [];'];
this.post_cmd = new Array();
this.source = ' ';
if (source != null)
{
if (typeof source == 'string')
{
source = source.replace(/\r\n/g, "\n");
source = source.replace(/\r/g, "\n");
this.source = source;
}else if (source.innerHTML){
this.source = source.innerHTML;
}
if (typeof this.source != 'string'){
this.source = "";
}
}
left = left || '<';
var right = '>';
switch(left) {
case '[':
right = ']';
break;
case '<':
break;
default:
throw left+' is not a supported deliminator';
break;
}
this.scanner = new EJS.Scanner(this.source, left, right);
this.out = '';
};
EJS.Compiler.prototype = {
compile: function(options, name) {
options = options || {};
this.out = '';
var put_cmd = "___ViewO.push(";
var insert_cmd = put_cmd;
var buff = new EJS.Buffer(this.pre_cmd, this.post_cmd);
var content = '';
var clean = function(content)
{
content = content.replace(/\\/g, '\\\\');
content = content.replace(/\n/g, '\\n');
content = content.replace(/"/g, '\\"');
return content;
};
this.scanner.scan(function(token, scanner) {
if (scanner.stag == null)
{
switch(token) {
case '\n':
content = content + "\n";
buff.push(put_cmd + '"' + clean(content) + '");');
buff.cr();
content = '';
break;
case scanner.left_delimiter:
case scanner.left_equal:
case scanner.left_comment:
scanner.stag = token;
if (content.length > 0)
{
buff.push(put_cmd + '"' + clean(content) + '")');
}
content = '';
break;
case scanner.double_left:
content = content + scanner.left_delimiter;
break;
default:
content = content + token;
break;
}
}
else {
switch(token) {
case scanner.right_delimiter:
switch(scanner.stag) {
case scanner.left_delimiter:
if (content[content.length - 1] == '\n')
{
content = chop(content);
buff.push(content);
buff.cr();
}
else {
buff.push(content);
}
break;
case scanner.left_equal:
buff.push(insert_cmd + "(EJS.Scanner.to_text(" + content + ")))");
break;
}
scanner.stag = null;
content = '';
break;
case scanner.double_right:
content = content + scanner.right_delimiter;
break;
default:
content = content + token;
break;
}
}
});
if (content.length > 0)
{
// Chould be content.dump in Ruby
buff.push(put_cmd + '"' + clean(content) + '")');
}
buff.close();
this.out = buff.script + ";";
var to_be_evaled = '/*'+name+'*/this.process = function(_CONTEXT,_VIEW) { try { with(_VIEW) { with (_CONTEXT) {'+this.out+" return ___ViewO.join('');}}}catch(e){e.lineNumber=null;throw e;}};";
try{
eval(to_be_evaled);
}catch(e){
if(typeof JSLINT != 'undefined'){
JSLINT(this.out);
for(var i = 0; i < JSLINT.errors.length; i++){
var error = JSLINT.errors[i];
if(error.reason != "Unnecessary semicolon."){
error.line++;
var e = new Error();
e.lineNumber = error.line;
e.message = error.reason;
if(options.view)
e.fileName = options.view;
throw e;
}
}
}else{
throw e;
}
}
}
};
//type, cache, folder
/**
* Sets default options for all views
* @param {Object} options Set view with the following options
* <table class="options">
<tbody><tr><th>Option</th><th>Default</th><th>Description</th></tr>
<tr>
<td>type</td>
<td>'<'</td>
<td>type of magic tags. Options are '&lt;' or '['
</td>
</tr>
<tr>
<td>cache</td>
<td>true in production mode, false in other modes</td>
<td>true to cache template.
</td>
</tr>
</tbody></table>
*
*/
EJS.config = function(options){
EJS.cache = options.cache != null ? options.cache : EJS.cache;
EJS.type = options.type != null ? options.type : EJS.type;
EJS.ext = options.ext != null ? options.ext : EJS.ext;
var templates_directory = EJS.templates_directory || {}; //nice and private container
EJS.templates_directory = templates_directory;
EJS.get = function(path, cache){
if(cache == false) return null;
if(templates_directory[path]) return templates_directory[path];
return null;
};
EJS.update = function(path, template) {
if(path == null) return;
templates_directory[path] = template ;
};
EJS.INVALID_PATH = -1;
};
EJS.config( {cache: true, type: '<', ext: '.ejs' } );
/**
* @constructor
* By adding functions to EJS.Helpers.prototype, those functions will be available in the
* views.
* @init Creates a view helper. This function is called internally. You should never call it.
* @param {Object} data The data passed to the view. Helpers have access to it through this._data
*/
EJS.Helpers = function(data, extras){
this._data = data;
this._extras = extras;
extend(this, extras );
};
/* @prototype*/
EJS.Helpers.prototype = {
/**
* Renders a new view. If data is passed in, uses that to render the view.
* @param {Object} options standard options passed to a new view.
* @param {optional:Object} data
* @return {String}
*/
view: function(options, data, helpers){
if(!helpers) helpers = this._extras
if(!data) data = this._data;
return new EJS(options).render(data, helpers);
},
/**
* For a given value, tries to create a human representation.
* @param {Object} input the value being converted.
* @param {Object} null_text what text should be present if input == null or undefined, defaults to ''
* @return {String}
*/
to_text: function(input, null_text) {
if(input == null || input === undefined) return null_text || '';
if(input instanceof Date) return input.toDateString();
if(input.toString) return input.toString().replace(/\n/g, '<br />').replace(/''/g, "'");
return '';
}
};
EJS.newRequest = function(){
var factories = [function() { return new ActiveXObject("Msxml2.XMLHTTP"); },function() { return new XMLHttpRequest(); },function() { return new ActiveXObject("Microsoft.XMLHTTP"); }];
for(var i = 0; i < factories.length; i++) {
try {
var request = factories[i]();
if (request != null) return request;
}
catch(e) { continue;}
}
}
EJS.request = function(path){
var request = new EJS.newRequest()
request.open("GET", path, false);
try{request.send(null);}
catch(e){return null;}
if ( request.status == 404 || request.status == 2 ||(request.status == 0 && request.responseText == '') ) return null;
return request.responseText
}
EJS.ajax_request = function(params){
params.method = ( params.method ? params.method : 'GET')
var request = new EJS.newRequest();
request.onreadystatechange = function(){
if(request.readyState == 4){
if(request.status == 200){
params.onComplete(request)
}else
{
params.onComplete(request)
}
}
}
request.open(params.method, params.url)
request.send(null)
}
})();
\ No newline at end of file
(function(){var rsplit=function(string,regex){var result=regex.exec(string),retArr=new Array(),first_idx,last_idx,first_bit;while(result!=null){first_idx=result.index;last_idx=regex.lastIndex;if((first_idx)!=0){first_bit=string.substring(0,first_idx);retArr.push(string.substring(0,first_idx));string=string.slice(first_idx)}retArr.push(result[0]);string=string.slice(result[0].length);result=regex.exec(string)}if(!string==""){retArr.push(string)}return retArr},chop=function(string){return string.substr(0,string.length-1)},extend=function(d,s){for(var n in s){if(s.hasOwnProperty(n)){d[n]=s[n]}}};EJS=function(options){options=typeof options=="string"?{view:options}:options;this.set_options(options);if(options.precompiled){this.template={};this.template.process=options.precompiled;EJS.update(this.name,this);return }if(options.element){if(typeof options.element=="string"){var name=options.element;options.element=document.getElementById(options.element);if(options.element==null){throw name+"does not exist!"}}if(options.element.value){this.text=options.element.value}else{this.text=options.element.innerHTML}this.name=options.element.id;this.type="["}else{if(options.url){options.url=EJS.endExt(options.url,this.extMatch);this.name=this.name?this.name:options.url;var url=options.url;var template=EJS.get(this.name,this.cache);if(template){return template}if(template==EJS.INVALID_PATH){return null}try{this.text=EJS.request(url+(this.cache?"":"?"+Math.random()))}catch(e){}if(this.text==null){throw ({type:"EJS",message:"There is no template at "+url})}}}var template=new EJS.Compiler(this.text,this.type);template.compile(options,this.name);EJS.update(this.name,this);this.template=template};EJS.prototype={render:function(object,extra_helpers){object=object||{};this._extra_helpers=extra_helpers;var v=new EJS.Helpers(object,extra_helpers||{});return this.template.process.call(object,object,v)},update:function(element,options){if(typeof element=="string"){element=document.getElementById(element)}if(options==null){_template=this;return function(object){EJS.prototype.update.call(_template,element,object)}}if(typeof options=="string"){params={};params.url=options;_template=this;params.onComplete=function(request){var object=eval(request.responseText);EJS.prototype.update.call(_template,element,object)};EJS.ajax_request(params)}else{element.innerHTML=this.render(options)}},out:function(){return this.template.out},set_options:function(options){this.type=options.type||EJS.type;this.cache=options.cache!=null?options.cache:EJS.cache;this.text=options.text||null;this.name=options.name||null;this.ext=options.ext||EJS.ext;this.extMatch=new RegExp(this.ext.replace(/\./,"."))}};EJS.endExt=function(path,match){if(!path){return null}match.lastIndex=0;return path+(match.test(path)?"":this.ext)};EJS.Scanner=function(source,left,right){extend(this,{left_delimiter:left+"%",right_delimiter:"%"+right,double_left:left+"%%",double_right:"%%"+right,left_equal:left+"%=",left_comment:left+"%#"});this.SplitRegexp=left=="["?/(\[%%)|(%%\])|(\[%=)|(\[%#)|(\[%)|(%\]\n)|(%\])|(\n)/:new RegExp("("+this.double_left+")|(%%"+this.double_right+")|("+this.left_equal+")|("+this.left_comment+")|("+this.left_delimiter+")|("+this.right_delimiter+"\n)|("+this.right_delimiter+")|(\n)");this.source=source;this.stag=null;this.lines=0};EJS.Scanner.to_text=function(input){if(input==null||input===undefined){return""}if(input instanceof Date){return input.toDateString()}if(input.toString){return input.toString()}return""};EJS.Scanner.prototype={scan:function(block){scanline=this.scanline;regex=this.SplitRegexp;if(!this.source==""){var source_split=rsplit(this.source,/\n/);for(var i=0;i<source_split.length;i++){var item=source_split[i];this.scanline(item,regex,block)}}},scanline:function(line,regex,block){this.lines++;var line_split=rsplit(line,regex);for(var i=0;i<line_split.length;i++){var token=line_split[i];if(token!=null){try{block(token,this)}catch(e){throw {type:"EJS.Scanner",line:this.lines}}}}}};EJS.Buffer=function(pre_cmd,post_cmd){this.line=new Array();this.script="";this.pre_cmd=pre_cmd;this.post_cmd=post_cmd;for(var i=0;i<this.pre_cmd.length;i++){this.push(pre_cmd[i])}};EJS.Buffer.prototype={push:function(cmd){this.line.push(cmd)},cr:function(){this.script=this.script+this.line.join("; ");this.line=new Array();this.script=this.script+"\n"},close:function(){if(this.line.length>0){for(var i=0;i<this.post_cmd.length;i++){this.push(pre_cmd[i])}this.script=this.script+this.line.join("; ");line=null}}};EJS.Compiler=function(source,left){this.pre_cmd=["var ___ViewO = [];"];this.post_cmd=new Array();this.source=" ";if(source!=null){if(typeof source=="string"){source=source.replace(/\r\n/g,"\n");source=source.replace(/\r/g,"\n");this.source=source}else{if(source.innerHTML){this.source=source.innerHTML}}if(typeof this.source!="string"){this.source=""}}left=left||"<";var right=">";switch(left){case"[":right="]";break;case"<":break;default:throw left+" is not a supported deliminator";break}this.scanner=new EJS.Scanner(this.source,left,right);this.out=""};EJS.Compiler.prototype={compile:function(options,name){options=options||{};this.out="";var put_cmd="___ViewO.push(";var insert_cmd=put_cmd;var buff=new EJS.Buffer(this.pre_cmd,this.post_cmd);var content="";var clean=function(content){content=content.replace(/\\/g,"\\\\");content=content.replace(/\n/g,"\\n");content=content.replace(/"/g,'\\"');return content};this.scanner.scan(function(token,scanner){if(scanner.stag==null){switch(token){case"\n":content=content+"\n";buff.push(put_cmd+'"'+clean(content)+'");');buff.cr();content="";break;case scanner.left_delimiter:case scanner.left_equal:case scanner.left_comment:scanner.stag=token;if(content.length>0){buff.push(put_cmd+'"'+clean(content)+'")')}content="";break;case scanner.double_left:content=content+scanner.left_delimiter;break;default:content=content+token;break}}else{switch(token){case scanner.right_delimiter:switch(scanner.stag){case scanner.left_delimiter:if(content[content.length-1]=="\n"){content=chop(content);buff.push(content);buff.cr()}else{buff.push(content)}break;case scanner.left_equal:buff.push(insert_cmd+"(EJS.Scanner.to_text("+content+")))");break}scanner.stag=null;content="";break;case scanner.double_right:content=content+scanner.right_delimiter;break;default:content=content+token;break}}});if(content.length>0){buff.push(put_cmd+'"'+clean(content)+'")')}buff.close();this.out=buff.script+";";var to_be_evaled="/*"+name+"*/this.process = function(_CONTEXT,_VIEW) { try { with(_VIEW) { with (_CONTEXT) {"+this.out+" return ___ViewO.join('');}}}catch(e){e.lineNumber=null;throw e;}};";try{eval(to_be_evaled)}catch(e){if(typeof JSLINT!="undefined"){JSLINT(this.out);for(var i=0;i<JSLINT.errors.length;i++){var error=JSLINT.errors[i];if(error.reason!="Unnecessary semicolon."){error.line++;var e=new Error();e.lineNumber=error.line;e.message=error.reason;if(options.view){e.fileName=options.view}throw e}}}else{throw e}}}};EJS.config=function(options){EJS.cache=options.cache!=null?options.cache:EJS.cache;EJS.type=options.type!=null?options.type:EJS.type;EJS.ext=options.ext!=null?options.ext:EJS.ext;var templates_directory=EJS.templates_directory||{};EJS.templates_directory=templates_directory;EJS.get=function(path,cache){if(cache==false){return null}if(templates_directory[path]){return templates_directory[path]}return null};EJS.update=function(path,template){if(path==null){return }templates_directory[path]=template};EJS.INVALID_PATH=-1};EJS.config({cache:true,type:"<",ext:".ejs"});EJS.Helpers=function(data,extras){this._data=data;this._extras=extras;extend(this,extras)};EJS.Helpers.prototype={view:function(options,data,helpers){if(!helpers){helpers=this._extras}if(!data){data=this._data}return new EJS(options).render(data,helpers)},to_text:function(input,null_text){if(input==null||input===undefined){return null_text||""}if(input instanceof Date){return input.toDateString()}if(input.toString){return input.toString().replace(/\n/g,"<br />").replace(/''/g,"'")}return""}};EJS.newRequest=function(){var factories=[function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new XMLHttpRequest()},function(){return new ActiveXObject("Microsoft.XMLHTTP")}];for(var i=0;i<factories.length;i++){try{var request=factories[i]();if(request!=null){return request}}catch(e){continue}}};EJS.request=function(path){var request=new EJS.newRequest();request.open("GET",path,false);try{request.send(null)}catch(e){return null}if(request.status==404||request.status==2||(request.status==0&&request.responseText=="")){return null}return request.responseText};EJS.ajax_request=function(params){params.method=(params.method?params.method:"GET");var request=new EJS.newRequest();request.onreadystatechange=function(){if(request.readyState==4){if(request.status==200){params.onComplete(request)}else{params.onComplete(request)}}};request.open(params.method,params.url);request.send(null)}})();EJS.Helpers.prototype.date_tag=function(C,O,A){if(!(O instanceof Date)){O=new Date()}var B=["January","February","March","April","May","June","July","August","September","October","November","December"];var G=[],D=[],P=[];var J=O.getFullYear();var H=O.getMonth();var N=O.getDate();for(var M=J-15;M<J+15;M++){G.push({value:M,text:M})}for(var E=0;E<12;E++){D.push({value:(E),text:B[E]})}for(var I=0;I<31;I++){P.push({value:(I+1),text:(I+1)})}var L=this.select_tag(C+"[year]",J,G,{id:C+"[year]"});var F=this.select_tag(C+"[month]",H,D,{id:C+"[month]"});var K=this.select_tag(C+"[day]",N,P,{id:C+"[day]"});return L+F+K};EJS.Helpers.prototype.form_tag=function(B,A){A=A||{};A.action=B;if(A.multipart==true){A.method="post";A.enctype="multipart/form-data"}return this.start_tag_for("form",A)};EJS.Helpers.prototype.form_tag_end=function(){return this.tag_end("form")};EJS.Helpers.prototype.hidden_field_tag=function(A,C,B){return this.input_field_tag(A,C,"hidden",B)};EJS.Helpers.prototype.input_field_tag=function(A,D,C,B){B=B||{};B.id=B.id||A;B.value=D||"";B.type=C||"text";B.name=A;return this.single_tag_for("input",B)};EJS.Helpers.prototype.is_current_page=function(A){return(window.location.href==A||window.location.pathname==A?true:false)};EJS.Helpers.prototype.link_to=function(B,A,C){if(!B){var B="null"}if(!C){var C={}}if(C.confirm){C.onclick=' var ret_confirm = confirm("'+C.confirm+'"); if(!ret_confirm){ return false;} ';C.confirm=null}C.href=A;return this.start_tag_for("a",C)+B+this.tag_end("a")};EJS.Helpers.prototype.submit_link_to=function(B,A,C){if(!B){var B="null"}if(!C){var C={}}C.onclick=C.onclick||"";if(C.confirm){C.onclick=' var ret_confirm = confirm("'+C.confirm+'"); if(!ret_confirm){ return false;} ';C.confirm=null}C.value=B;C.type="submit";C.onclick=C.onclick+(A?this.url_for(A):"")+"return false;";return this.start_tag_for("input",C)};EJS.Helpers.prototype.link_to_if=function(F,B,A,D,C,E){return this.link_to_unless((F==false),B,A,D,C,E)};EJS.Helpers.prototype.link_to_unless=function(E,B,A,C,D){C=C||{};if(E){if(D&&typeof D=="function"){return D(B,A,C,D)}else{return B}}else{return this.link_to(B,A,C)}};EJS.Helpers.prototype.link_to_unless_current=function(B,A,C,D){C=C||{};return this.link_to_unless(this.is_current_page(A),B,A,C,D)};EJS.Helpers.prototype.password_field_tag=function(A,C,B){return this.input_field_tag(A,C,"password",B)};EJS.Helpers.prototype.select_tag=function(D,G,H,F){F=F||{};F.id=F.id||D;F.value=G;F.name=D;var B="";B+=this.start_tag_for("select",F);for(var E=0;E<H.length;E++){var C=H[E];var A={value:C.value};if(C.value==G){A.selected="selected"}B+=this.start_tag_for("option",A)+C.text+this.tag_end("option")}B+=this.tag_end("select");return B};EJS.Helpers.prototype.single_tag_for=function(A,B){return this.tag(A,B,"/>")};EJS.Helpers.prototype.start_tag_for=function(A,B){return this.tag(A,B)};EJS.Helpers.prototype.submit_tag=function(A,B){B=B||{};B.type=B.type||"submit";B.value=A||"Submit";return this.single_tag_for("input",B)};EJS.Helpers.prototype.tag=function(C,E,D){if(!D){var D=">"}var B=" ";for(var A in E){if(E[A]!=null){var F=E[A].toString()}else{var F=""}if(A=="Class"){A="class"}if(F.indexOf("'")!=-1){B+=A+'="'+F+'" '}else{B+=A+"='"+F+"' "}}return"<"+C+B+D};EJS.Helpers.prototype.tag_end=function(A){return"</"+A+">"};EJS.Helpers.prototype.text_area_tag=function(A,C,B){B=B||{};B.id=B.id||A;B.name=B.name||A;C=C||"";if(B.size){B.cols=B.size.split("x")[0];B.rows=B.size.split("x")[1];delete B.size}B.cols=B.cols||50;B.rows=B.rows||4;return this.start_tag_for("textarea",B)+C+this.tag_end("textarea")};EJS.Helpers.prototype.text_tag=EJS.Helpers.prototype.text_area_tag;EJS.Helpers.prototype.text_field_tag=function(A,C,B){return this.input_field_tag(A,C,"text",B)};EJS.Helpers.prototype.url_for=function(A){return'window.location="'+A+'";'};EJS.Helpers.prototype.img_tag=function(B,C,A){A=A||{};A.src=B;A.alt=C;return this.single_tag_for("img",A)}
\ No newline at end of file
// Copyright 2006 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Known Issues:
//
// * Patterns only support repeat.
// * Radial gradient are not implemented. The VML version of these look very
// different from the canvas one.
// * Clipping paths are not implemented.
// * Coordsize. The width and height attribute have higher priority than the
// width and height style values which isn't correct.
// * Painting mode isn't implemented.
// * Canvas width/height should is using content-box by default. IE in
// Quirks mode will draw the canvas using border-box. Either change your
// doctype to HTML5
// (https://www.whatwg.org/specs/web-apps/current-work/#the-doctype)
// or use Box Sizing Behavior from WebFX
// (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html)
// * Non uniform scaling does not correctly scale strokes.
// * Filling very large shapes (above 5000 points) is buggy.
// * Optimize. There is always room for speed improvements.
// Only add this code if we do not already have a canvas implementation
if (!document.createElement('canvas').getContext) {
(function() {
// alias some functions to make (compiled) code shorter
var m = Math;
var mr = m.round;
var ms = m.sin;
var mc = m.cos;
var abs = m.abs;
var sqrt = m.sqrt;
// this is used for sub pixel precision
var Z = 10;
var Z2 = Z / 2;
var IE_VERSION = +navigator.userAgent.match(/MSIE ([\d.]+)?/)[1];
/**
* This funtion is assigned to the <canvas> elements as element.getContext().
* @this {HTMLElement}
* @return {CanvasRenderingContext2D_}
*/
function getContext() {
return this.context_ ||
(this.context_ = new CanvasRenderingContext2D_(this));
}
var slice = Array.prototype.slice;
/**
* Binds a function to an object. The returned function will always use the
* passed in {@code obj} as {@code this}.
*
* Example:
*
* g = bind(f, obj, a, b)
* g(c, d) // will do f.call(obj, a, b, c, d)
*
* @param {Function} f The function to bind the object to
* @param {Object} obj The object that should act as this when the function
* is called
* @param {*} var_args Rest arguments that will be used as the initial
* arguments when the function is called
* @return {Function} A new function that has bound this
*/
function bind(f, obj, var_args) {
var a = slice.call(arguments, 2);
return function() {
return f.apply(obj, a.concat(slice.call(arguments)));
};
}
function encodeHtmlAttribute(s) {
return String(s).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
}
function addNamespace(doc, prefix, urn) {
if (!doc.namespaces[prefix]) {
doc.namespaces.add(prefix, urn, '#default#VML');
}
}
function addNamespacesAndStylesheet(doc) {
addNamespace(doc, 'g_vml_', 'urn:schemas-microsoft-com:vml');
addNamespace(doc, 'g_o_', 'urn:schemas-microsoft-com:office:office');
// Setup default CSS. Only add one style sheet per document
if (!doc.styleSheets['ex_canvas_']) {
var ss = doc.createStyleSheet();
ss.owningElement.id = 'ex_canvas_';
ss.cssText = 'canvas{display:inline-block;overflow:hidden;' +
// default size is 300x150 in Gecko and Opera
'text-align:left;width:300px;height:150px}';
}
}
// Add namespaces and stylesheet at startup.
addNamespacesAndStylesheet(document);
var G_vmlCanvasManager_ = {
init: function(opt_doc) {
var doc = opt_doc || document;
// Create a dummy element so that IE will allow canvas elements to be
// recognized.
doc.createElement('canvas');
doc.attachEvent('onreadystatechange', bind(this.init_, this, doc));
},
init_: function(doc) {
// find all canvas elements
var els = doc.getElementsByTagName('canvas');
for (var i = 0; i < els.length; i++) {
this.initElement(els[i]);
}
},
/**
* Public initializes a canvas element so that it can be used as canvas
* element from now on. This is called automatically before the page is
* loaded but if you are creating elements using createElement you need to
* make sure this is called on the element.
* @param {HTMLElement} el The canvas element to initialize.
* @return {HTMLElement} the element that was created.
*/
initElement: function(el) {
if (!el.getContext) {
el.getContext = getContext;
// Add namespaces and stylesheet to document of the element.
addNamespacesAndStylesheet(el.ownerDocument);
// Remove fallback content. There is no way to hide text nodes so we
// just remove all childNodes. We could hide all elements and remove
// text nodes but who really cares about the fallback content.
el.innerHTML = '';
// do not use inline function because that will leak memory
el.attachEvent('onpropertychange', onPropertyChange);
el.attachEvent('onresize', onResize);
var attrs = el.attributes;
if (attrs.width && attrs.width.specified) {
// TODO: use runtimeStyle and coordsize
// el.getContext().setWidth_(attrs.width.nodeValue);
el.style.width = attrs.width.nodeValue + 'px';
} else {
el.width = el.clientWidth;
}
if (attrs.height && attrs.height.specified) {
// TODO: use runtimeStyle and coordsize
// el.getContext().setHeight_(attrs.height.nodeValue);
el.style.height = attrs.height.nodeValue + 'px';
} else {
el.height = el.clientHeight;
}
//el.getContext().setCoordsize_()
}
return el;
}
};
function onPropertyChange(e) {
var el = e.srcElement;
switch (e.propertyName) {
case 'width':
el.getContext().clearRect();
el.style.width = el.attributes.width.nodeValue + 'px';
// In IE8 this does not trigger onresize.
el.firstChild.style.width = el.clientWidth + 'px';
break;
case 'height':
el.getContext().clearRect();
el.style.height = el.attributes.height.nodeValue + 'px';
el.firstChild.style.height = el.clientHeight + 'px';
break;
}
}
function onResize(e) {
var el = e.srcElement;
if (el.firstChild) {
el.firstChild.style.width = el.clientWidth + 'px';
el.firstChild.style.height = el.clientHeight + 'px';
}
}
G_vmlCanvasManager_.init();
// precompute "00" to "FF"
var decToHex = [];
for (var i = 0; i < 16; i++) {
for (var j = 0; j < 16; j++) {
decToHex[i * 16 + j] = i.toString(16) + j.toString(16);
}
}
function createMatrixIdentity() {
return [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1]
];
}
function matrixMultiply(m1, m2) {
var result = createMatrixIdentity();
for (var x = 0; x < 3; x++) {
for (var y = 0; y < 3; y++) {
var sum = 0;
for (var z = 0; z < 3; z++) {
sum += m1[x][z] * m2[z][y];
}
result[x][y] = sum;
}
}
return result;
}
function copyState(o1, o2) {
o2.fillStyle = o1.fillStyle;
o2.lineCap = o1.lineCap;
o2.lineJoin = o1.lineJoin;
o2.lineWidth = o1.lineWidth;
o2.miterLimit = o1.miterLimit;
o2.shadowBlur = o1.shadowBlur;
o2.shadowColor = o1.shadowColor;
o2.shadowOffsetX = o1.shadowOffsetX;
o2.shadowOffsetY = o1.shadowOffsetY;
o2.strokeStyle = o1.strokeStyle;
o2.globalAlpha = o1.globalAlpha;
o2.font = o1.font;
o2.textAlign = o1.textAlign;
o2.textBaseline = o1.textBaseline;
o2.arcScaleX_ = o1.arcScaleX_;
o2.arcScaleY_ = o1.arcScaleY_;
o2.lineScale_ = o1.lineScale_;
}
var colorData = {
aliceblue: '#F0F8FF',
antiquewhite: '#FAEBD7',
aquamarine: '#7FFFD4',
azure: '#F0FFFF',
beige: '#F5F5DC',
bisque: '#FFE4C4',
black: '#000000',
blanchedalmond: '#FFEBCD',
blueviolet: '#8A2BE2',
brown: '#A52A2A',
burlywood: '#DEB887',
cadetblue: '#5F9EA0',
chartreuse: '#7FFF00',
chocolate: '#D2691E',
coral: '#FF7F50',
cornflowerblue: '#6495ED',
cornsilk: '#FFF8DC',
crimson: '#DC143C',
cyan: '#00FFFF',
darkblue: '#00008B',
darkcyan: '#008B8B',
darkgoldenrod: '#B8860B',
darkgray: '#A9A9A9',
darkgreen: '#006400',
darkgrey: '#A9A9A9',
darkkhaki: '#BDB76B',
darkmagenta: '#8B008B',
darkolivegreen: '#556B2F',
darkorange: '#FF8C00',
darkorchid: '#9932CC',
darkred: '#8B0000',
darksalmon: '#E9967A',
darkseagreen: '#8FBC8F',
darkslateblue: '#483D8B',
darkslategray: '#2F4F4F',
darkslategrey: '#2F4F4F',
darkturquoise: '#00CED1',
darkviolet: '#9400D3',
deeppink: '#FF1493',
deepskyblue: '#00BFFF',
dimgray: '#696969',
dimgrey: '#696969',
dodgerblue: '#1E90FF',
firebrick: '#B22222',
floralwhite: '#FFFAF0',
forestgreen: '#228B22',
gainsboro: '#DCDCDC',
ghostwhite: '#F8F8FF',
gold: '#FFD700',
goldenrod: '#DAA520',
grey: '#808080',
greenyellow: '#ADFF2F',
honeydew: '#F0FFF0',
hotpink: '#FF69B4',
indianred: '#CD5C5C',
indigo: '#4B0082',
ivory: '#FFFFF0',
khaki: '#F0E68C',
lavender: '#E6E6FA',
lavenderblush: '#FFF0F5',
lawngreen: '#7CFC00',
lemonchiffon: '#FFFACD',
lightblue: '#ADD8E6',
lightcoral: '#F08080',
lightcyan: '#E0FFFF',
lightgoldenrodyellow: '#FAFAD2',
lightgreen: '#90EE90',
lightgrey: '#D3D3D3',
lightpink: '#FFB6C1',
lightsalmon: '#FFA07A',
lightseagreen: '#20B2AA',
lightskyblue: '#87CEFA',
lightslategray: '#778899',
lightslategrey: '#778899',
lightsteelblue: '#B0C4DE',
lightyellow: '#FFFFE0',
limegreen: '#32CD32',
linen: '#FAF0E6',
magenta: '#FF00FF',
mediumaquamarine: '#66CDAA',
mediumblue: '#0000CD',
mediumorchid: '#BA55D3',
mediumpurple: '#9370DB',
mediumseagreen: '#3CB371',
mediumslateblue: '#7B68EE',
mediumspringgreen: '#00FA9A',
mediumturquoise: '#48D1CC',
mediumvioletred: '#C71585',
midnightblue: '#191970',
mintcream: '#F5FFFA',
mistyrose: '#FFE4E1',
moccasin: '#FFE4B5',
navajowhite: '#FFDEAD',
oldlace: '#FDF5E6',
olivedrab: '#6B8E23',
orange: '#FFA500',
orangered: '#FF4500',
orchid: '#DA70D6',
palegoldenrod: '#EEE8AA',
palegreen: '#98FB98',
paleturquoise: '#AFEEEE',
palevioletred: '#DB7093',
papayawhip: '#FFEFD5',
peachpuff: '#FFDAB9',
peru: '#CD853F',
pink: '#FFC0CB',
plum: '#DDA0DD',
powderblue: '#B0E0E6',
rosybrown: '#BC8F8F',
royalblue: '#4169E1',
saddlebrown: '#8B4513',
salmon: '#FA8072',
sandybrown: '#F4A460',
seagreen: '#2E8B57',
seashell: '#FFF5EE',
sienna: '#A0522D',
skyblue: '#87CEEB',
slateblue: '#6A5ACD',
slategray: '#708090',
slategrey: '#708090',
snow: '#FFFAFA',
springgreen: '#00FF7F',
steelblue: '#4682B4',
tan: '#D2B48C',
thistle: '#D8BFD8',
tomato: '#FF6347',
turquoise: '#40E0D0',
violet: '#EE82EE',
wheat: '#F5DEB3',
whitesmoke: '#F5F5F5',
yellowgreen: '#9ACD32'
};
function getRgbHslContent(styleString) {
var start = styleString.indexOf('(', 3);
var end = styleString.indexOf(')', start + 1);
var parts = styleString.substring(start + 1, end).split(',');
// add alpha if needed
if (parts.length != 4 || styleString.charAt(3) != 'a') {
parts[3] = 1;
}
return parts;
}
function percent(s) {
return parseFloat(s) / 100;
}
function clamp(v, min, max) {
return Math.min(max, Math.max(min, v));
}
function hslToRgb(parts){
var r, g, b, h, s, l;
h = parseFloat(parts[0]) / 360 % 360;
if (h < 0)
h++;
s = clamp(percent(parts[1]), 0, 1);
l = clamp(percent(parts[2]), 0, 1);
if (s == 0) {
r = g = b = l; // achromatic
} else {
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hueToRgb(p, q, h + 1 / 3);
g = hueToRgb(p, q, h);
b = hueToRgb(p, q, h - 1 / 3);
}
return '#' + decToHex[Math.floor(r * 255)] +
decToHex[Math.floor(g * 255)] +
decToHex[Math.floor(b * 255)];
}
function hueToRgb(m1, m2, h) {
if (h < 0)
h++;
if (h > 1)
h--;
if (6 * h < 1)
return m1 + (m2 - m1) * 6 * h;
else if (2 * h < 1)
return m2;
else if (3 * h < 2)
return m1 + (m2 - m1) * (2 / 3 - h) * 6;
else
return m1;
}
var processStyleCache = {};
function processStyle(styleString) {
if (styleString in processStyleCache) {
return processStyleCache[styleString];
}
var str, alpha = 1;
styleString = String(styleString);
if (styleString.charAt(0) == '#') {
str = styleString;
} else if (/^rgb/.test(styleString)) {
var parts = getRgbHslContent(styleString);
var str = '#', n;
for (var i = 0; i < 3; i++) {
if (parts[i].indexOf('%') != -1) {
n = Math.floor(percent(parts[i]) * 255);
} else {
n = +parts[i];
}
str += decToHex[clamp(n, 0, 255)];
}
alpha = +parts[3];
} else if (/^hsl/.test(styleString)) {
var parts = getRgbHslContent(styleString);
str = hslToRgb(parts);
alpha = parts[3];
} else {
str = colorData[styleString] || styleString;
}
return processStyleCache[styleString] = {color: str, alpha: alpha};
}
var DEFAULT_STYLE = {
style: 'normal',
variant: 'normal',
weight: 'normal',
size: 10,
family: 'sans-serif'
};
// Internal text style cache
var fontStyleCache = {};
function processFontStyle(styleString) {
if (fontStyleCache[styleString]) {
return fontStyleCache[styleString];
}
var el = document.createElement('div');
var style = el.style;
try {
style.font = styleString;
} catch (ex) {
// Ignore failures to set to invalid font.
}
return fontStyleCache[styleString] = {
style: style.fontStyle || DEFAULT_STYLE.style,
variant: style.fontVariant || DEFAULT_STYLE.variant,
weight: style.fontWeight || DEFAULT_STYLE.weight,
size: style.fontSize || DEFAULT_STYLE.size,
family: style.fontFamily || DEFAULT_STYLE.family
};
}
function getComputedStyle(style, element) {
var computedStyle = {};
for (var p in style) {
computedStyle[p] = style[p];
}
// Compute the size
var canvasFontSize = parseFloat(element.currentStyle.fontSize),
fontSize = parseFloat(style.size);
if (typeof style.size == 'number') {
computedStyle.size = style.size;
} else if (style.size.indexOf('px') != -1) {
computedStyle.size = fontSize;
} else if (style.size.indexOf('em') != -1) {
computedStyle.size = canvasFontSize * fontSize;
} else if(style.size.indexOf('%') != -1) {
computedStyle.size = (canvasFontSize / 100) * fontSize;
} else if (style.size.indexOf('pt') != -1) {
computedStyle.size = fontSize / .75;
} else {
computedStyle.size = canvasFontSize;
}
// Different scaling between normal text and VML text. This was found using
// trial and error to get the same size as non VML text.
computedStyle.size *= 0.981;
return computedStyle;
}
function buildStyle(style) {
return style.style + ' ' + style.variant + ' ' + style.weight + ' ' +
style.size + 'px ' + style.family;
}
var lineCapMap = {
'butt': 'flat',
'round': 'round'
};
function processLineCap(lineCap) {
return lineCapMap[lineCap] || 'square';
}
/**
* This class implements CanvasRenderingContext2D interface as described by
* the WHATWG.
* @param {HTMLElement} canvasElement The element that the 2D context should
* be associated with
*/
function CanvasRenderingContext2D_(canvasElement) {
this.m_ = createMatrixIdentity();
this.mStack_ = [];
this.aStack_ = [];
this.currentPath_ = [];
// Canvas context properties
this.strokeStyle = '#000';
this.fillStyle = '#000';
this.lineWidth = 1;
this.lineJoin = 'miter';
this.lineCap = 'butt';
this.miterLimit = Z * 1;
this.globalAlpha = 1;
this.font = '10px sans-serif';
this.textAlign = 'left';
this.textBaseline = 'alphabetic';
this.canvas = canvasElement;
var cssText = 'width:' + canvasElement.clientWidth + 'px;height:' +
canvasElement.clientHeight + 'px;overflow:hidden;position:absolute';
var el = canvasElement.ownerDocument.createElement('div');
el.style.cssText = cssText;
canvasElement.appendChild(el);
var overlayEl = el.cloneNode(false);
// Use a non transparent background.
overlayEl.style.backgroundColor = 'red';
overlayEl.style.filter = 'alpha(opacity=0)';
canvasElement.appendChild(overlayEl);
this.element_ = el;
this.arcScaleX_ = 1;
this.arcScaleY_ = 1;
this.lineScale_ = 1;
}
var contextPrototype = CanvasRenderingContext2D_.prototype;
contextPrototype.clearRect = function() {
if (this.textMeasureEl_) {
this.textMeasureEl_.removeNode(true);
this.textMeasureEl_ = null;
}
this.element_.innerHTML = '';
};
contextPrototype.beginPath = function() {
// TODO: Branch current matrix so that save/restore has no effect
// as per safari docs.
this.currentPath_ = [];
};
contextPrototype.moveTo = function(aX, aY) {
var p = getCoords(this, aX, aY);
this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y});
this.currentX_ = p.x;
this.currentY_ = p.y;
};
contextPrototype.lineTo = function(aX, aY) {
var p = getCoords(this, aX, aY);
this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y});
this.currentX_ = p.x;
this.currentY_ = p.y;
};
contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
aCP2x, aCP2y,
aX, aY) {
var p = getCoords(this, aX, aY);
var cp1 = getCoords(this, aCP1x, aCP1y);
var cp2 = getCoords(this, aCP2x, aCP2y);
bezierCurveTo(this, cp1, cp2, p);
};
// Helper function that takes the already fixed cordinates.
function bezierCurveTo(self, cp1, cp2, p) {
self.currentPath_.push({
type: 'bezierCurveTo',
cp1x: cp1.x,
cp1y: cp1.y,
cp2x: cp2.x,
cp2y: cp2.y,
x: p.x,
y: p.y
});
self.currentX_ = p.x;
self.currentY_ = p.y;
}
contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
// the following is lifted almost directly from
// https://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes
var cp = getCoords(this, aCPx, aCPy);
var p = getCoords(this, aX, aY);
var cp1 = {
x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_),
y: this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_)
};
var cp2 = {
x: cp1.x + (p.x - this.currentX_) / 3.0,
y: cp1.y + (p.y - this.currentY_) / 3.0
};
bezierCurveTo(this, cp1, cp2, p);
};
contextPrototype.arc = function(aX, aY, aRadius,
aStartAngle, aEndAngle, aClockwise) {
aRadius *= Z;
var arcType = aClockwise ? 'at' : 'wa';
var xStart = aX + mc(aStartAngle) * aRadius - Z2;
var yStart = aY + ms(aStartAngle) * aRadius - Z2;
var xEnd = aX + mc(aEndAngle) * aRadius - Z2;
var yEnd = aY + ms(aEndAngle) * aRadius - Z2;
// IE won't render arches drawn counter clockwise if xStart == xEnd.
if (xStart == xEnd && !aClockwise) {
xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something
// that can be represented in binary
}
var p = getCoords(this, aX, aY);
var pStart = getCoords(this, xStart, yStart);
var pEnd = getCoords(this, xEnd, yEnd);
this.currentPath_.push({type: arcType,
x: p.x,
y: p.y,
radius: aRadius,
xStart: pStart.x,
yStart: pStart.y,
xEnd: pEnd.x,
yEnd: pEnd.y});
};
contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
this.moveTo(aX, aY);
this.lineTo(aX + aWidth, aY);
this.lineTo(aX + aWidth, aY + aHeight);
this.lineTo(aX, aY + aHeight);
this.closePath();
};
contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
var oldPath = this.currentPath_;
this.beginPath();
this.moveTo(aX, aY);
this.lineTo(aX + aWidth, aY);
this.lineTo(aX + aWidth, aY + aHeight);
this.lineTo(aX, aY + aHeight);
this.closePath();
this.stroke();
this.currentPath_ = oldPath;
};
contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
var oldPath = this.currentPath_;
this.beginPath();
this.moveTo(aX, aY);
this.lineTo(aX + aWidth, aY);
this.lineTo(aX + aWidth, aY + aHeight);
this.lineTo(aX, aY + aHeight);
this.closePath();
this.fill();
this.currentPath_ = oldPath;
};
contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
var gradient = new CanvasGradient_('gradient');
gradient.x0_ = aX0;
gradient.y0_ = aY0;
gradient.x1_ = aX1;
gradient.y1_ = aY1;
return gradient;
};
contextPrototype.createRadialGradient = function(aX0, aY0, aR0,
aX1, aY1, aR1) {
var gradient = new CanvasGradient_('gradientradial');
gradient.x0_ = aX0;
gradient.y0_ = aY0;
gradient.r0_ = aR0;
gradient.x1_ = aX1;
gradient.y1_ = aY1;
gradient.r1_ = aR1;
return gradient;
};
contextPrototype.drawImage = function(image, var_args) {
var dx, dy, dw, dh, sx, sy, sw, sh;
// to find the original width we overide the width and height
var oldRuntimeWidth = image.runtimeStyle.width;
var oldRuntimeHeight = image.runtimeStyle.height;
image.runtimeStyle.width = 'auto';
image.runtimeStyle.height = 'auto';
// get the original size
var w = image.width;
var h = image.height;
// and remove overides
image.runtimeStyle.width = oldRuntimeWidth;
image.runtimeStyle.height = oldRuntimeHeight;
if (arguments.length == 3) {
dx = arguments[1];
dy = arguments[2];
sx = sy = 0;
sw = dw = w;
sh = dh = h;
} else if (arguments.length == 5) {
dx = arguments[1];
dy = arguments[2];
dw = arguments[3];
dh = arguments[4];
sx = sy = 0;
sw = w;
sh = h;
} else if (arguments.length == 9) {
sx = arguments[1];
sy = arguments[2];
sw = arguments[3];
sh = arguments[4];
dx = arguments[5];
dy = arguments[6];
dw = arguments[7];
dh = arguments[8];
} else {
throw Error('Invalid number of arguments');
}
var d = getCoords(this, dx, dy);
var w2 = sw / 2;
var h2 = sh / 2;
var vmlStr = [];
var W = 10;
var H = 10;
// For some reason that I've now forgotten, using divs didn't work
vmlStr.push(' <g_vml_:group',
' coordsize="', Z * W, ',', Z * H, '"',
' coordorigin="0,0"' ,
' style="width:', W, 'px;height:', H, 'px;position:absolute;');
// If filters are necessary (rotation exists), create them
// filters are bog-slow, so only create them if abbsolutely necessary
// The following check doesn't account for skews (which don't exist
// in the canvas spec (yet) anyway.
if (this.m_[0][0] != 1 || this.m_[0][1] ||
this.m_[1][1] != 1 || this.m_[1][0]) {
var filter = [];
// Note the 12/21 reversal
filter.push('M11=', this.m_[0][0], ',',
'M12=', this.m_[1][0], ',',
'M21=', this.m_[0][1], ',',
'M22=', this.m_[1][1], ',',
'Dx=', mr(d.x / Z), ',',
'Dy=', mr(d.y / Z), '');
// Bounding box calculation (need to minimize displayed area so that
// filters don't waste time on unused pixels.
var max = d;
var c2 = getCoords(this, dx + dw, dy);
var c3 = getCoords(this, dx, dy + dh);
var c4 = getCoords(this, dx + dw, dy + dh);
max.x = m.max(max.x, c2.x, c3.x, c4.x);
max.y = m.max(max.y, c2.y, c3.y, c4.y);
vmlStr.push('padding:0 ', mr(max.x / Z), 'px ', mr(max.y / Z),
'px 0;filter:progid:DXImageTransform.Microsoft.Matrix(',
filter.join(''), ", sizingmethod='clip');");
} else {
vmlStr.push('top:', mr(d.y / Z), 'px;left:', mr(d.x / Z), 'px;');
}
vmlStr.push(' ">' ,
'<g_vml_:image src="', image.src, '"',
' style="width:', Z * dw, 'px;',
' height:', Z * dh, 'px"',
' cropleft="', sx / w, '"',
' croptop="', sy / h, '"',
' cropright="', (w - sx - sw) / w, '"',
' cropbottom="', (h - sy - sh) / h, '"',
' />',
'</g_vml_:group>');
this.element_.insertAdjacentHTML('BeforeEnd', vmlStr.join(''));
};
contextPrototype.stroke = function(aFill) {
var W = 10;
var H = 10;
// Divide the shape into chunks if it's too long because IE has a limit
// somewhere for how long a VML shape can be. This simple division does
// not work with fills, only strokes, unfortunately.
var chunkSize = 5000;
var min = {x: null, y: null};
var max = {x: null, y: null};
for (var j = 0; j < this.currentPath_.length; j += chunkSize) {
var lineStr = [];
var lineOpen = false;
lineStr.push('<g_vml_:shape',
' filled="', !!aFill, '"',
' style="position:absolute;width:', W, 'px;height:', H, 'px;"',
' coordorigin="0,0"',
' coordsize="', Z * W, ',', Z * H, '"',
' stroked="', !aFill, '"',
' path="');
var newSeq = false;
for (var i = j; i < Math.min(j + chunkSize, this.currentPath_.length); i++) {
if (i % chunkSize == 0 && i > 0) { // move into position for next chunk
lineStr.push(' m ', mr(this.currentPath_[i-1].x), ',', mr(this.currentPath_[i-1].y));
}
var p = this.currentPath_[i];
var c;
switch (p.type) {
case 'moveTo':
c = p;
lineStr.push(' m ', mr(p.x), ',', mr(p.y));
break;
case 'lineTo':
lineStr.push(' l ', mr(p.x), ',', mr(p.y));
break;
case 'close':
lineStr.push(' x ');
p = null;
break;
case 'bezierCurveTo':
lineStr.push(' c ',
mr(p.cp1x), ',', mr(p.cp1y), ',',
mr(p.cp2x), ',', mr(p.cp2y), ',',
mr(p.x), ',', mr(p.y));
break;
case 'at':
case 'wa':
lineStr.push(' ', p.type, ' ',
mr(p.x - this.arcScaleX_ * p.radius), ',',
mr(p.y - this.arcScaleY_ * p.radius), ' ',
mr(p.x + this.arcScaleX_ * p.radius), ',',
mr(p.y + this.arcScaleY_ * p.radius), ' ',
mr(p.xStart), ',', mr(p.yStart), ' ',
mr(p.xEnd), ',', mr(p.yEnd));
break;
}
// TODO: Following is broken for curves due to
// move to proper paths.
// Figure out dimensions so we can do gradient fills
// properly
if (p) {
if (min.x == null || p.x < min.x) {
min.x = p.x;
}
if (max.x == null || p.x > max.x) {
max.x = p.x;
}
if (min.y == null || p.y < min.y) {
min.y = p.y;
}
if (max.y == null || p.y > max.y) {
max.y = p.y;
}
}
}
lineStr.push(' ">');
if (!aFill) {
appendStroke(this, lineStr);
} else {
appendFill(this, lineStr, min, max);
}
lineStr.push('</g_vml_:shape>');
this.element_.insertAdjacentHTML('beforeEnd', lineStr.join(''));
}
};
function appendStroke(ctx, lineStr) {
var a = processStyle(ctx.strokeStyle);
var color = a.color;
var opacity = a.alpha * ctx.globalAlpha;
var lineWidth = ctx.lineScale_ * ctx.lineWidth;
// VML cannot correctly render a line if the width is less than 1px.
// In that case, we dilute the color to make the line look thinner.
if (lineWidth < 1) {
opacity *= lineWidth;
}
lineStr.push(
'<g_vml_:stroke',
' opacity="', opacity, '"',
' joinstyle="', ctx.lineJoin, '"',
' miterlimit="', ctx.miterLimit, '"',
' endcap="', processLineCap(ctx.lineCap), '"',
' weight="', lineWidth, 'px"',
' color="', color, '" />'
);
}
function appendFill(ctx, lineStr, min, max) {
var fillStyle = ctx.fillStyle;
var arcScaleX = ctx.arcScaleX_;
var arcScaleY = ctx.arcScaleY_;
var width = max.x - min.x;
var height = max.y - min.y;
if (fillStyle instanceof CanvasGradient_) {
// TODO: Gradients transformed with the transformation matrix.
var angle = 0;
var focus = {x: 0, y: 0};
// additional offset
var shift = 0;
// scale factor for offset
var expansion = 1;
if (fillStyle.type_ == 'gradient') {
var x0 = fillStyle.x0_ / arcScaleX;
var y0 = fillStyle.y0_ / arcScaleY;
var x1 = fillStyle.x1_ / arcScaleX;
var y1 = fillStyle.y1_ / arcScaleY;
var p0 = getCoords(ctx, x0, y0);
var p1 = getCoords(ctx, x1, y1);
var dx = p1.x - p0.x;
var dy = p1.y - p0.y;
angle = Math.atan2(dx, dy) * 180 / Math.PI;
// The angle should be a non-negative number.
if (angle < 0) {
angle += 360;
}
// Very small angles produce an unexpected result because they are
// converted to a scientific notation string.
if (angle < 1e-6) {
angle = 0;
}
} else {
var p0 = getCoords(ctx, fillStyle.x0_, fillStyle.y0_);
focus = {
x: (p0.x - min.x) / width,
y: (p0.y - min.y) / height
};
width /= arcScaleX * Z;
height /= arcScaleY * Z;
var dimension = m.max(width, height);
shift = 2 * fillStyle.r0_ / dimension;
expansion = 2 * fillStyle.r1_ / dimension - shift;
}
// We need to sort the color stops in ascending order by offset,
// otherwise IE won't interpret it correctly.
var stops = fillStyle.colors_;
stops.sort(function(cs1, cs2) {
return cs1.offset - cs2.offset;
});
var length = stops.length;
var color1 = stops[0].color;
var color2 = stops[length - 1].color;
var opacity1 = stops[0].alpha * ctx.globalAlpha;
var opacity2 = stops[length - 1].alpha * ctx.globalAlpha;
var colors = [];
for (var i = 0; i < length; i++) {
var stop = stops[i];
colors.push(stop.offset * expansion + shift + ' ' + stop.color);
}
// When colors attribute is used, the meanings of opacity and o:opacity2
// are reversed.
lineStr.push('<g_vml_:fill type="', fillStyle.type_, '"',
' method="none" focus="100%"',
' color="', color1, '"',
' color2="', color2, '"',
' colors="', colors.join(','), '"',
' opacity="', opacity2, '"',
' g_o_:opacity2="', opacity1, '"',
' angle="', angle, '"',
' focusposition="', focus.x, ',', focus.y, '" />');
} else if (fillStyle instanceof CanvasPattern_) {
if (width && height) {
var deltaLeft = -min.x;
var deltaTop = -min.y;
lineStr.push('<g_vml_:fill',
' position="',
deltaLeft / width * arcScaleX * arcScaleX, ',',
deltaTop / height * arcScaleY * arcScaleY, '"',
' type="tile"',
// TODO: Figure out the correct size to fit the scale.
//' size="', w, 'px ', h, 'px"',
' src="', fillStyle.src_, '" />');
}
} else {
var a = processStyle(ctx.fillStyle);
var color = a.color;
var opacity = a.alpha * ctx.globalAlpha;
lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity,
'" />');
}
}
contextPrototype.fill = function() {
this.stroke(true);
};
contextPrototype.closePath = function() {
this.currentPath_.push({type: 'close'});
};
function getCoords(ctx, aX, aY) {
var m = ctx.m_;
return {
x: Z * (aX * m[0][0] + aY * m[1][0] + m[2][0]) - Z2,
y: Z * (aX * m[0][1] + aY * m[1][1] + m[2][1]) - Z2
};
};
contextPrototype.save = function() {
var o = {};
copyState(this, o);
this.aStack_.push(o);
this.mStack_.push(this.m_);
this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
};
contextPrototype.restore = function() {
if (this.aStack_.length) {
copyState(this.aStack_.pop(), this);
this.m_ = this.mStack_.pop();
}
};
function matrixIsFinite(m) {
return isFinite(m[0][0]) && isFinite(m[0][1]) &&
isFinite(m[1][0]) && isFinite(m[1][1]) &&
isFinite(m[2][0]) && isFinite(m[2][1]);
}
function setM(ctx, m, updateLineScale) {
if (!matrixIsFinite(m)) {
return;
}
ctx.m_ = m;
if (updateLineScale) {
// Get the line scale.
// Determinant of this.m_ means how much the area is enlarged by the
// transformation. So its square root can be used as a scale factor
// for width.
var det = m[0][0] * m[1][1] - m[0][1] * m[1][0];
ctx.lineScale_ = sqrt(abs(det));
}
}
contextPrototype.translate = function(aX, aY) {
var m1 = [
[1, 0, 0],
[0, 1, 0],
[aX, aY, 1]
];
setM(this, matrixMultiply(m1, this.m_), false);
};
contextPrototype.rotate = function(aRot) {
var c = mc(aRot);
var s = ms(aRot);
var m1 = [
[c, s, 0],
[-s, c, 0],
[0, 0, 1]
];
setM(this, matrixMultiply(m1, this.m_), false);
};
contextPrototype.scale = function(aX, aY) {
this.arcScaleX_ *= aX;
this.arcScaleY_ *= aY;
var m1 = [
[aX, 0, 0],
[0, aY, 0],
[0, 0, 1]
];
setM(this, matrixMultiply(m1, this.m_), true);
};
contextPrototype.transform = function(m11, m12, m21, m22, dx, dy) {
var m1 = [
[m11, m12, 0],
[m21, m22, 0],
[dx, dy, 1]
];
setM(this, matrixMultiply(m1, this.m_), true);
};
contextPrototype.setTransform = function(m11, m12, m21, m22, dx, dy) {
var m = [
[m11, m12, 0],
[m21, m22, 0],
[dx, dy, 1]
];
setM(this, m, true);
};
/**
* The text drawing function.
* The maxWidth argument isn't taken in account, since no browser supports
* it yet.
*/
contextPrototype.drawText_ = function(text, x, y, maxWidth, stroke) {
var m = this.m_,
delta = 1000,
left = 0,
right = delta,
offset = {x: 0, y: 0},
lineStr = [];
var fontStyle = getComputedStyle(processFontStyle(this.font),
this.element_);
var fontStyleString = buildStyle(fontStyle);
var elementStyle = this.element_.currentStyle;
var textAlign = this.textAlign.toLowerCase();
switch (textAlign) {
case 'left':
case 'center':
case 'right':
break;
case 'end':
textAlign = elementStyle.direction == 'ltr' ? 'right' : 'left';
break;
case 'start':
textAlign = elementStyle.direction == 'rtl' ? 'right' : 'left';
break;
default:
textAlign = 'left';
}
// 1.75 is an arbitrary number, as there is no info about the text baseline
switch (this.textBaseline) {
case 'hanging':
case 'top':
offset.y = fontStyle.size / 1.75;
break;
case 'middle':
break;
default:
case null:
case 'alphabetic':
case 'ideographic':
case 'bottom':
offset.y = -fontStyle.size / 2.25;
break;
}
switch(textAlign) {
case 'right':
left = delta;
right = 0.05;
break;
case 'center':
left = right = delta / 2;
break;
}
var d = getCoords(this, x + offset.x, y + offset.y);
lineStr.push('<g_vml_:line from="', -left ,' 0" to="', right ,' 0.05" ',
' coordsize="100 100" coordorigin="0 0"',
' filled="', !stroke, '" stroked="', !!stroke,
'" style="position:absolute;width:1px;height:1px;">');
if (stroke) {
appendStroke(this, lineStr);
} else {
// TODO: Fix the min and max params.
appendFill(this, lineStr, {x: -left, y: 0},
{x: right, y: fontStyle.size});
}
var skewM = m[0][0].toFixed(3) + ',' + m[1][0].toFixed(3) + ',' +
m[0][1].toFixed(3) + ',' + m[1][1].toFixed(3) + ',0,0';
var skewOffset = mr(d.x / Z) + ',' + mr(d.y / Z);
lineStr.push('<g_vml_:skew on="t" matrix="', skewM ,'" ',
' offset="', skewOffset, '" origin="', left ,' 0" />',
'<g_vml_:path textpathok="true" />',
'<g_vml_:textpath on="true" string="',
encodeHtmlAttribute(text),
'" style="v-text-align:', textAlign,
';font:', encodeHtmlAttribute(fontStyleString),
'" /></g_vml_:line>');
this.element_.insertAdjacentHTML('beforeEnd', lineStr.join(''));
};
contextPrototype.fillText = function(text, x, y, maxWidth) {
this.drawText_(text, x, y, maxWidth, false);
};
contextPrototype.strokeText = function(text, x, y, maxWidth) {
this.drawText_(text, x, y, maxWidth, true);
};
contextPrototype.measureText = function(text) {
if (!this.textMeasureEl_) {
var s = '<span style="position:absolute;' +
'top:-20000px;left:0;padding:0;margin:0;border:none;' +
'white-space:pre;"></span>';
this.element_.insertAdjacentHTML('beforeEnd', s);
this.textMeasureEl_ = this.element_.lastChild;
}
var doc = this.element_.ownerDocument;
this.textMeasureEl_.innerHTML = '';
this.textMeasureEl_.style.font = this.font;
// Don't use innerHTML or innerText because they allow markup/whitespace.
this.textMeasureEl_.appendChild(doc.createTextNode(text));
return {width: this.textMeasureEl_.offsetWidth};
};
/******** STUBS ********/
contextPrototype.clip = function() {
// TODO: Implement
};
contextPrototype.arcTo = function() {
// TODO: Implement
};
contextPrototype.createPattern = function(image, repetition) {
return new CanvasPattern_(image, repetition);
};
// Gradient / Pattern Stubs
function CanvasGradient_(aType) {
this.type_ = aType;
this.x0_ = 0;
this.y0_ = 0;
this.r0_ = 0;
this.x1_ = 0;
this.y1_ = 0;
this.r1_ = 0;
this.colors_ = [];
}
CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
aColor = processStyle(aColor);
this.colors_.push({offset: aOffset,
color: aColor.color,
alpha: aColor.alpha});
};
function CanvasPattern_(image, repetition) {
assertImageIsValid(image);
switch (repetition) {
case 'repeat':
case null:
case '':
this.repetition_ = 'repeat';
break
case 'repeat-x':
case 'repeat-y':
case 'no-repeat':
this.repetition_ = repetition;
break;
default:
throwException('SYNTAX_ERR');
}
this.src_ = image.src;
this.width_ = image.width;
this.height_ = image.height;
}
function throwException(s) {
throw new DOMException_(s);
}
function assertImageIsValid(img) {
if (!img || img.nodeType != 1 || img.tagName != 'IMG') {
throwException('TYPE_MISMATCH_ERR');
}
if (img.readyState != 'complete') {
throwException('INVALID_STATE_ERR');
}
}
function DOMException_(s) {
this.code = this[s];
this.message = s +': DOM Exception ' + this.code;
}
var p = DOMException_.prototype = new Error;
p.INDEX_SIZE_ERR = 1;
p.DOMSTRING_SIZE_ERR = 2;
p.HIERARCHY_REQUEST_ERR = 3;
p.WRONG_DOCUMENT_ERR = 4;
p.INVALID_CHARACTER_ERR = 5;
p.NO_DATA_ALLOWED_ERR = 6;
p.NO_MODIFICATION_ALLOWED_ERR = 7;
p.NOT_FOUND_ERR = 8;
p.NOT_SUPPORTED_ERR = 9;
p.INUSE_ATTRIBUTE_ERR = 10;
p.INVALID_STATE_ERR = 11;
p.SYNTAX_ERR = 12;
p.INVALID_MODIFICATION_ERR = 13;
p.NAMESPACE_ERR = 14;
p.INVALID_ACCESS_ERR = 15;
p.VALIDATION_ERR = 16;
p.TYPE_MISMATCH_ERR = 17;
// set up externs
G_vmlCanvasManager = G_vmlCanvasManager_;
CanvasRenderingContext2D = CanvasRenderingContext2D_;
CanvasGradient = CanvasGradient_;
CanvasPattern = CanvasPattern_;
DOMException = DOMException_;
})();
} // if
if(!document.createElement("canvas").getContext){(function(){var z=Math;var K=z.round;var J=z.sin;var U=z.cos;var b=z.abs;var k=z.sqrt;var D=10;var F=D/2;function T(){return this.context_||(this.context_=new W(this))}var O=Array.prototype.slice;function G(i,j,m){var Z=O.call(arguments,2);return function(){return i.apply(j,Z.concat(O.call(arguments)))}}function AD(Z){return String(Z).replace(/&/g,"&amp;").replace(/"/g,"&quot;")}function r(i){if(!i.namespaces.g_vml_){i.namespaces.add("g_vml_","urn:schemas-microsoft-com:vml","#default#VML")}if(!i.namespaces.g_o_){i.namespaces.add("g_o_","urn:schemas-microsoft-com:office:office","#default#VML")}if(!i.styleSheets.ex_canvas_){var Z=i.createStyleSheet();Z.owningElement.id="ex_canvas_";Z.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}"}}r(document);var E={init:function(Z){if(/MSIE/.test(navigator.userAgent)&&!window.opera){var i=Z||document;i.createElement("canvas");i.attachEvent("onreadystatechange",G(this.init_,this,i))}},init_:function(m){var j=m.getElementsByTagName("canvas");for(var Z=0;Z<j.length;Z++){this.initElement(j[Z])}},initElement:function(i){if(!i.getContext){i.getContext=T;r(i.ownerDocument);i.innerHTML="";i.attachEvent("onpropertychange",S);i.attachEvent("onresize",w);var Z=i.attributes;if(Z.width&&Z.width.specified){i.style.width=Z.width.nodeValue+"px"}else{i.width=i.clientWidth}if(Z.height&&Z.height.specified){i.style.height=Z.height.nodeValue+"px"}else{i.height=i.clientHeight}}return i}};function S(i){var Z=i.srcElement;switch(i.propertyName){case"width":Z.getContext().clearRect();Z.style.width=Z.attributes.width.nodeValue+"px";Z.firstChild.style.width=Z.clientWidth+"px";break;case"height":Z.getContext().clearRect();Z.style.height=Z.attributes.height.nodeValue+"px";Z.firstChild.style.height=Z.clientHeight+"px";break}}function w(i){var Z=i.srcElement;if(Z.firstChild){Z.firstChild.style.width=Z.clientWidth+"px";Z.firstChild.style.height=Z.clientHeight+"px"}}E.init();var I=[];for(var AC=0;AC<16;AC++){for(var AB=0;AB<16;AB++){I[AC*16+AB]=AC.toString(16)+AB.toString(16)}}function V(){return[[1,0,0],[0,1,0],[0,0,1]]}function d(m,j){var i=V();for(var Z=0;Z<3;Z++){for(var AF=0;AF<3;AF++){var p=0;for(var AE=0;AE<3;AE++){p+=m[Z][AE]*j[AE][AF]}i[Z][AF]=p}}return i}function Q(i,Z){Z.fillStyle=i.fillStyle;Z.lineCap=i.lineCap;Z.lineJoin=i.lineJoin;Z.lineWidth=i.lineWidth;Z.miterLimit=i.miterLimit;Z.shadowBlur=i.shadowBlur;Z.shadowColor=i.shadowColor;Z.shadowOffsetX=i.shadowOffsetX;Z.shadowOffsetY=i.shadowOffsetY;Z.strokeStyle=i.strokeStyle;Z.globalAlpha=i.globalAlpha;Z.font=i.font;Z.textAlign=i.textAlign;Z.textBaseline=i.textBaseline;Z.arcScaleX_=i.arcScaleX_;Z.arcScaleY_=i.arcScaleY_;Z.lineScale_=i.lineScale_}var B={aliceblue:"#F0F8FF",antiquewhite:"#FAEBD7",aquamarine:"#7FFFD4",azure:"#F0FFFF",beige:"#F5F5DC",bisque:"#FFE4C4",black:"#000000",blanchedalmond:"#FFEBCD",blueviolet:"#8A2BE2",brown:"#A52A2A",burlywood:"#DEB887",cadetblue:"#5F9EA0",chartreuse:"#7FFF00",chocolate:"#D2691E",coral:"#FF7F50",cornflowerblue:"#6495ED",cornsilk:"#FFF8DC",crimson:"#DC143C",cyan:"#00FFFF",darkblue:"#00008B",darkcyan:"#008B8B",darkgoldenrod:"#B8860B",darkgray:"#A9A9A9",darkgreen:"#006400",darkgrey:"#A9A9A9",darkkhaki:"#BDB76B",darkmagenta:"#8B008B",darkolivegreen:"#556B2F",darkorange:"#FF8C00",darkorchid:"#9932CC",darkred:"#8B0000",darksalmon:"#E9967A",darkseagreen:"#8FBC8F",darkslateblue:"#483D8B",darkslategray:"#2F4F4F",darkslategrey:"#2F4F4F",darkturquoise:"#00CED1",darkviolet:"#9400D3",deeppink:"#FF1493",deepskyblue:"#00BFFF",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1E90FF",firebrick:"#B22222",floralwhite:"#FFFAF0",forestgreen:"#228B22",gainsboro:"#DCDCDC",ghostwhite:"#F8F8FF",gold:"#FFD700",goldenrod:"#DAA520",grey:"#808080",greenyellow:"#ADFF2F",honeydew:"#F0FFF0",hotpink:"#FF69B4",indianred:"#CD5C5C",indigo:"#4B0082",ivory:"#FFFFF0",khaki:"#F0E68C",lavender:"#E6E6FA",lavenderblush:"#FFF0F5",lawngreen:"#7CFC00",lemonchiffon:"#FFFACD",lightblue:"#ADD8E6",lightcoral:"#F08080",lightcyan:"#E0FFFF",lightgoldenrodyellow:"#FAFAD2",lightgreen:"#90EE90",lightgrey:"#D3D3D3",lightpink:"#FFB6C1",lightsalmon:"#FFA07A",lightseagreen:"#20B2AA",lightskyblue:"#87CEFA",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#B0C4DE",lightyellow:"#FFFFE0",limegreen:"#32CD32",linen:"#FAF0E6",magenta:"#FF00FF",mediumaquamarine:"#66CDAA",mediumblue:"#0000CD",mediumorchid:"#BA55D3",mediumpurple:"#9370DB",mediumseagreen:"#3CB371",mediumslateblue:"#7B68EE",mediumspringgreen:"#00FA9A",mediumturquoise:"#48D1CC",mediumvioletred:"#C71585",midnightblue:"#191970",mintcream:"#F5FFFA",mistyrose:"#FFE4E1",moccasin:"#FFE4B5",navajowhite:"#FFDEAD",oldlace:"#FDF5E6",olivedrab:"#6B8E23",orange:"#FFA500",orangered:"#FF4500",orchid:"#DA70D6",palegoldenrod:"#EEE8AA",palegreen:"#98FB98",paleturquoise:"#AFEEEE",palevioletred:"#DB7093",papayawhip:"#FFEFD5",peachpuff:"#FFDAB9",peru:"#CD853F",pink:"#FFC0CB",plum:"#DDA0DD",powderblue:"#B0E0E6",rosybrown:"#BC8F8F",royalblue:"#4169E1",saddlebrown:"#8B4513",salmon:"#FA8072",sandybrown:"#F4A460",seagreen:"#2E8B57",seashell:"#FFF5EE",sienna:"#A0522D",skyblue:"#87CEEB",slateblue:"#6A5ACD",slategray:"#708090",slategrey:"#708090",snow:"#FFFAFA",springgreen:"#00FF7F",steelblue:"#4682B4",tan:"#D2B48C",thistle:"#D8BFD8",tomato:"#FF6347",turquoise:"#40E0D0",violet:"#EE82EE",wheat:"#F5DEB3",whitesmoke:"#F5F5F5",yellowgreen:"#9ACD32"};function g(i){var m=i.indexOf("(",3);var Z=i.indexOf(")",m+1);var j=i.substring(m+1,Z).split(",");if(j.length==4&&i.substr(3,1)=="a"){alpha=Number(j[3])}else{j[3]=1}return j}function C(Z){return parseFloat(Z)/100}function N(i,j,Z){return Math.min(Z,Math.max(j,i))}function c(AF){var j,i,Z;h=parseFloat(AF[0])/360%360;if(h<0){h++}s=N(C(AF[1]),0,1);l=N(C(AF[2]),0,1);if(s==0){j=i=Z=l}else{var m=l<0.5?l*(1+s):l+s-l*s;var AE=2*l-m;j=A(AE,m,h+1/3);i=A(AE,m,h);Z=A(AE,m,h-1/3)}return"#"+I[Math.floor(j*255)]+I[Math.floor(i*255)]+I[Math.floor(Z*255)]}function A(i,Z,j){if(j<0){j++}if(j>1){j--}if(6*j<1){return i+(Z-i)*6*j}else{if(2*j<1){return Z}else{if(3*j<2){return i+(Z-i)*(2/3-j)*6}else{return i}}}}function Y(Z){var AE,p=1;Z=String(Z);if(Z.charAt(0)=="#"){AE=Z}else{if(/^rgb/.test(Z)){var m=g(Z);var AE="#",AF;for(var j=0;j<3;j++){if(m[j].indexOf("%")!=-1){AF=Math.floor(C(m[j])*255)}else{AF=Number(m[j])}AE+=I[N(AF,0,255)]}p=m[3]}else{if(/^hsl/.test(Z)){var m=g(Z);AE=c(m);p=m[3]}else{AE=B[Z]||Z}}}return{color:AE,alpha:p}}var L={style:"normal",variant:"normal",weight:"normal",size:10,family:"sans-serif"};var f={};function X(Z){if(f[Z]){return f[Z]}var m=document.createElement("div");var j=m.style;try{j.font=Z}catch(i){}return f[Z]={style:j.fontStyle||L.style,variant:j.fontVariant||L.variant,weight:j.fontWeight||L.weight,size:j.fontSize||L.size,family:j.fontFamily||L.family}}function P(j,i){var Z={};for(var AF in j){Z[AF]=j[AF]}var AE=parseFloat(i.currentStyle.fontSize),m=parseFloat(j.size);if(typeof j.size=="number"){Z.size=j.size}else{if(j.size.indexOf("px")!=-1){Z.size=m}else{if(j.size.indexOf("em")!=-1){Z.size=AE*m}else{if(j.size.indexOf("%")!=-1){Z.size=(AE/100)*m}else{if(j.size.indexOf("pt")!=-1){Z.size=m/0.75}else{Z.size=AE}}}}}Z.size*=0.981;return Z}function AA(Z){return Z.style+" "+Z.variant+" "+Z.weight+" "+Z.size+"px "+Z.family}function t(Z){switch(Z){case"butt":return"flat";case"round":return"round";case"square":default:return"square"}}function W(i){this.m_=V();this.mStack_=[];this.aStack_=[];this.currentPath_=[];this.strokeStyle="#000";this.fillStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=D*1;this.globalAlpha=1;this.font="10px sans-serif";this.textAlign="left";this.textBaseline="alphabetic";this.canvas=i;var Z=i.ownerDocument.createElement("div");Z.style.width=i.clientWidth+"px";Z.style.height=i.clientHeight+"px";Z.style.overflow="hidden";Z.style.position="absolute";i.appendChild(Z);this.element_=Z;this.arcScaleX_=1;this.arcScaleY_=1;this.lineScale_=1}var M=W.prototype;M.clearRect=function(){if(this.textMeasureEl_){this.textMeasureEl_.removeNode(true);this.textMeasureEl_=null}this.element_.innerHTML=""};M.beginPath=function(){this.currentPath_=[]};M.moveTo=function(i,Z){var j=this.getCoords_(i,Z);this.currentPath_.push({type:"moveTo",x:j.x,y:j.y});this.currentX_=j.x;this.currentY_=j.y};M.lineTo=function(i,Z){var j=this.getCoords_(i,Z);this.currentPath_.push({type:"lineTo",x:j.x,y:j.y});this.currentX_=j.x;this.currentY_=j.y};M.bezierCurveTo=function(j,i,AI,AH,AG,AE){var Z=this.getCoords_(AG,AE);var AF=this.getCoords_(j,i);var m=this.getCoords_(AI,AH);e(this,AF,m,Z)};function e(Z,m,j,i){Z.currentPath_.push({type:"bezierCurveTo",cp1x:m.x,cp1y:m.y,cp2x:j.x,cp2y:j.y,x:i.x,y:i.y});Z.currentX_=i.x;Z.currentY_=i.y}M.quadraticCurveTo=function(AG,j,i,Z){var AF=this.getCoords_(AG,j);var AE=this.getCoords_(i,Z);var AH={x:this.currentX_+2/3*(AF.x-this.currentX_),y:this.currentY_+2/3*(AF.y-this.currentY_)};var m={x:AH.x+(AE.x-this.currentX_)/3,y:AH.y+(AE.y-this.currentY_)/3};e(this,AH,m,AE)};M.arc=function(AJ,AH,AI,AE,i,j){AI*=D;var AN=j?"at":"wa";var AK=AJ+U(AE)*AI-F;var AM=AH+J(AE)*AI-F;var Z=AJ+U(i)*AI-F;var AL=AH+J(i)*AI-F;if(AK==Z&&!j){AK+=0.125}var m=this.getCoords_(AJ,AH);var AG=this.getCoords_(AK,AM);var AF=this.getCoords_(Z,AL);this.currentPath_.push({type:AN,x:m.x,y:m.y,radius:AI,xStart:AG.x,yStart:AG.y,xEnd:AF.x,yEnd:AF.y})};M.rect=function(j,i,Z,m){this.moveTo(j,i);this.lineTo(j+Z,i);this.lineTo(j+Z,i+m);this.lineTo(j,i+m);this.closePath()};M.strokeRect=function(j,i,Z,m){var p=this.currentPath_;this.beginPath();this.moveTo(j,i);this.lineTo(j+Z,i);this.lineTo(j+Z,i+m);this.lineTo(j,i+m);this.closePath();this.stroke();this.currentPath_=p};M.fillRect=function(j,i,Z,m){var p=this.currentPath_;this.beginPath();this.moveTo(j,i);this.lineTo(j+Z,i);this.lineTo(j+Z,i+m);this.lineTo(j,i+m);this.closePath();this.fill();this.currentPath_=p};M.createLinearGradient=function(i,m,Z,j){var p=new v("gradient");p.x0_=i;p.y0_=m;p.x1_=Z;p.y1_=j;return p};M.createRadialGradient=function(m,AE,j,i,p,Z){var AF=new v("gradientradial");AF.x0_=m;AF.y0_=AE;AF.r0_=j;AF.x1_=i;AF.y1_=p;AF.r1_=Z;return AF};M.drawImage=function(AO,j){var AH,AF,AJ,AV,AM,AK,AQ,AX;var AI=AO.runtimeStyle.width;var AN=AO.runtimeStyle.height;AO.runtimeStyle.width="auto";AO.runtimeStyle.height="auto";var AG=AO.width;var AT=AO.height;AO.runtimeStyle.width=AI;AO.runtimeStyle.height=AN;if(arguments.length==3){AH=arguments[1];AF=arguments[2];AM=AK=0;AQ=AJ=AG;AX=AV=AT}else{if(arguments.length==5){AH=arguments[1];AF=arguments[2];AJ=arguments[3];AV=arguments[4];AM=AK=0;AQ=AG;AX=AT}else{if(arguments.length==9){AM=arguments[1];AK=arguments[2];AQ=arguments[3];AX=arguments[4];AH=arguments[5];AF=arguments[6];AJ=arguments[7];AV=arguments[8]}else{throw Error("Invalid number of arguments")}}}var AW=this.getCoords_(AH,AF);var m=AQ/2;var i=AX/2;var AU=[];var Z=10;var AE=10;AU.push(" <g_vml_:group",' coordsize="',D*Z,",",D*AE,'"',' coordorigin="0,0"',' style="width:',Z,"px;height:",AE,"px;position:absolute;");if(this.m_[0][0]!=1||this.m_[0][1]||this.m_[1][1]!=1||this.m_[1][0]){var p=[];p.push("M11=",this.m_[0][0],",","M12=",this.m_[1][0],",","M21=",this.m_[0][1],",","M22=",this.m_[1][1],",","Dx=",K(AW.x/D),",","Dy=",K(AW.y/D),"");var AS=AW;var AR=this.getCoords_(AH+AJ,AF);var AP=this.getCoords_(AH,AF+AV);var AL=this.getCoords_(AH+AJ,AF+AV);AS.x=z.max(AS.x,AR.x,AP.x,AL.x);AS.y=z.max(AS.y,AR.y,AP.y,AL.y);AU.push("padding:0 ",K(AS.x/D),"px ",K(AS.y/D),"px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",p.join(""),", sizingmethod='clip');")}else{AU.push("top:",K(AW.y/D),"px;left:",K(AW.x/D),"px;")}AU.push(' ">','<g_vml_:image src="',AO.src,'"',' style="width:',D*AJ,"px;"," height:",D*AV,'px"',' cropleft="',AM/AG,'"',' croptop="',AK/AT,'"',' cropright="',(AG-AM-AQ)/AG,'"',' cropbottom="',(AT-AK-AX)/AT,'"'," />","</g_vml_:group>");this.element_.insertAdjacentHTML("BeforeEnd",AU.join(""))};M.stroke=function(AM){var m=10;var AN=10;var AE=5000;var AG={x:null,y:null};var AL={x:null,y:null};for(var AH=0;AH<this.currentPath_.length;AH+=AE){var AK=[];var AF=false;AK.push("<g_vml_:shape",' filled="',!!AM,'"',' style="position:absolute;width:',m,"px;height:",AN,'px;"',' coordorigin="0,0"',' coordsize="',D*m,",",D*AN,'"',' stroked="',!AM,'"',' path="');var AO=false;for(var AI=AH;AI<Math.min(AH+AE,this.currentPath_.length);AI++){if(AI%AE==0&&AI>0){AK.push(" m ",K(this.currentPath_[AI-1].x),",",K(this.currentPath_[AI-1].y))}var Z=this.currentPath_[AI];var AJ;switch(Z.type){case"moveTo":AJ=Z;AK.push(" m ",K(Z.x),",",K(Z.y));break;case"lineTo":AK.push(" l ",K(Z.x),",",K(Z.y));break;case"close":AK.push(" x ");Z=null;break;case"bezierCurveTo":AK.push(" c ",K(Z.cp1x),",",K(Z.cp1y),",",K(Z.cp2x),",",K(Z.cp2y),",",K(Z.x),",",K(Z.y));break;case"at":case"wa":AK.push(" ",Z.type," ",K(Z.x-this.arcScaleX_*Z.radius),",",K(Z.y-this.arcScaleY_*Z.radius)," ",K(Z.x+this.arcScaleX_*Z.radius),",",K(Z.y+this.arcScaleY_*Z.radius)," ",K(Z.xStart),",",K(Z.yStart)," ",K(Z.xEnd),",",K(Z.yEnd));break}if(Z){if(AG.x==null||Z.x<AG.x){AG.x=Z.x}if(AL.x==null||Z.x>AL.x){AL.x=Z.x}if(AG.y==null||Z.y<AG.y){AG.y=Z.y}if(AL.y==null||Z.y>AL.y){AL.y=Z.y}}}AK.push(' ">');if(!AM){R(this,AK)}else{a(this,AK,AG,AL)}AK.push("</g_vml_:shape>");this.element_.insertAdjacentHTML("beforeEnd",AK.join(""))}};function R(j,AE){var i=Y(j.strokeStyle);var m=i.color;var p=i.alpha*j.globalAlpha;var Z=j.lineScale_*j.lineWidth;if(Z<1){p*=Z}AE.push("<g_vml_:stroke",' opacity="',p,'"',' joinstyle="',j.lineJoin,'"',' miterlimit="',j.miterLimit,'"',' endcap="',t(j.lineCap),'"',' weight="',Z,'px"',' color="',m,'" />')}function a(AO,AG,Ah,AP){var AH=AO.fillStyle;var AY=AO.arcScaleX_;var AX=AO.arcScaleY_;var Z=AP.x-Ah.x;var m=AP.y-Ah.y;if(AH instanceof v){var AL=0;var Ac={x:0,y:0};var AU=0;var AK=1;if(AH.type_=="gradient"){var AJ=AH.x0_/AY;var j=AH.y0_/AX;var AI=AH.x1_/AY;var Aj=AH.y1_/AX;var Ag=AO.getCoords_(AJ,j);var Af=AO.getCoords_(AI,Aj);var AE=Af.x-Ag.x;var p=Af.y-Ag.y;AL=Math.atan2(AE,p)*180/Math.PI;if(AL<0){AL+=360}if(AL<0.000001){AL=0}}else{var Ag=AO.getCoords_(AH.x0_,AH.y0_);Ac={x:(Ag.x-Ah.x)/Z,y:(Ag.y-Ah.y)/m};Z/=AY*D;m/=AX*D;var Aa=z.max(Z,m);AU=2*AH.r0_/Aa;AK=2*AH.r1_/Aa-AU}var AS=AH.colors_;AS.sort(function(Ak,i){return Ak.offset-i.offset});var AN=AS.length;var AR=AS[0].color;var AQ=AS[AN-1].color;var AW=AS[0].alpha*AO.globalAlpha;var AV=AS[AN-1].alpha*AO.globalAlpha;var Ab=[];for(var Ae=0;Ae<AN;Ae++){var AM=AS[Ae];Ab.push(AM.offset*AK+AU+" "+AM.color)}AG.push('<g_vml_:fill type="',AH.type_,'"',' method="none" focus="100%"',' color="',AR,'"',' color2="',AQ,'"',' colors="',Ab.join(","),'"',' opacity="',AV,'"',' g_o_:opacity2="',AW,'"',' angle="',AL,'"',' focusposition="',Ac.x,",",Ac.y,'" />')}else{if(AH instanceof u){if(Z&&m){var AF=-Ah.x;var AZ=-Ah.y;AG.push("<g_vml_:fill",' position="',AF/Z*AY*AY,",",AZ/m*AX*AX,'"',' type="tile"',' src="',AH.src_,'" />')}}else{var Ai=Y(AO.fillStyle);var AT=Ai.color;var Ad=Ai.alpha*AO.globalAlpha;AG.push('<g_vml_:fill color="',AT,'" opacity="',Ad,'" />')}}}M.fill=function(){this.stroke(true)};M.closePath=function(){this.currentPath_.push({type:"close"})};M.getCoords_=function(j,i){var Z=this.m_;return{x:D*(j*Z[0][0]+i*Z[1][0]+Z[2][0])-F,y:D*(j*Z[0][1]+i*Z[1][1]+Z[2][1])-F}};M.save=function(){var Z={};Q(this,Z);this.aStack_.push(Z);this.mStack_.push(this.m_);this.m_=d(V(),this.m_)};M.restore=function(){if(this.aStack_.length){Q(this.aStack_.pop(),this);this.m_=this.mStack_.pop()}};function H(Z){return isFinite(Z[0][0])&&isFinite(Z[0][1])&&isFinite(Z[1][0])&&isFinite(Z[1][1])&&isFinite(Z[2][0])&&isFinite(Z[2][1])}function y(i,Z,j){if(!H(Z)){return }i.m_=Z;if(j){var p=Z[0][0]*Z[1][1]-Z[0][1]*Z[1][0];i.lineScale_=k(b(p))}}M.translate=function(j,i){var Z=[[1,0,0],[0,1,0],[j,i,1]];y(this,d(Z,this.m_),false)};M.rotate=function(i){var m=U(i);var j=J(i);var Z=[[m,j,0],[-j,m,0],[0,0,1]];y(this,d(Z,this.m_),false)};M.scale=function(j,i){this.arcScaleX_*=j;this.arcScaleY_*=i;var Z=[[j,0,0],[0,i,0],[0,0,1]];y(this,d(Z,this.m_),true)};M.transform=function(p,m,AF,AE,i,Z){var j=[[p,m,0],[AF,AE,0],[i,Z,1]];y(this,d(j,this.m_),true)};M.setTransform=function(AE,p,AG,AF,j,i){var Z=[[AE,p,0],[AG,AF,0],[j,i,1]];y(this,Z,true)};M.drawText_=function(AK,AI,AH,AN,AG){var AM=this.m_,AQ=1000,i=0,AP=AQ,AF={x:0,y:0},AE=[];var Z=P(X(this.font),this.element_);var j=AA(Z);var AR=this.element_.currentStyle;var p=this.textAlign.toLowerCase();switch(p){case"left":case"center":case"right":break;case"end":p=AR.direction=="ltr"?"right":"left";break;case"start":p=AR.direction=="rtl"?"right":"left";break;default:p="left"}switch(this.textBaseline){case"hanging":case"top":AF.y=Z.size/1.75;break;case"middle":break;default:case null:case"alphabetic":case"ideographic":case"bottom":AF.y=-Z.size/2.25;break}switch(p){case"right":i=AQ;AP=0.05;break;case"center":i=AP=AQ/2;break}var AO=this.getCoords_(AI+AF.x,AH+AF.y);AE.push('<g_vml_:line from="',-i,' 0" to="',AP,' 0.05" ',' coordsize="100 100" coordorigin="0 0"',' filled="',!AG,'" stroked="',!!AG,'" style="position:absolute;width:1px;height:1px;">');if(AG){R(this,AE)}else{a(this,AE,{x:-i,y:0},{x:AP,y:Z.size})}var AL=AM[0][0].toFixed(3)+","+AM[1][0].toFixed(3)+","+AM[0][1].toFixed(3)+","+AM[1][1].toFixed(3)+",0,0";var AJ=K(AO.x/D)+","+K(AO.y/D);AE.push('<g_vml_:skew on="t" matrix="',AL,'" ',' offset="',AJ,'" origin="',i,' 0" />','<g_vml_:path textpathok="true" />','<g_vml_:textpath on="true" string="',AD(AK),'" style="v-text-align:',p,";font:",AD(j),'" /></g_vml_:line>');this.element_.insertAdjacentHTML("beforeEnd",AE.join(""))};M.fillText=function(j,Z,m,i){this.drawText_(j,Z,m,i,false)};M.strokeText=function(j,Z,m,i){this.drawText_(j,Z,m,i,true)};M.measureText=function(j){if(!this.textMeasureEl_){var Z='<span style="position:absolute;top:-20000px;left:0;padding:0;margin:0;border:none;white-space:pre;"></span>';this.element_.insertAdjacentHTML("beforeEnd",Z);this.textMeasureEl_=this.element_.lastChild}var i=this.element_.ownerDocument;this.textMeasureEl_.innerHTML="";this.textMeasureEl_.style.font=this.font;this.textMeasureEl_.appendChild(i.createTextNode(j));return{width:this.textMeasureEl_.offsetWidth}};M.clip=function(){};M.arcTo=function(){};M.createPattern=function(i,Z){return new u(i,Z)};function v(Z){this.type_=Z;this.x0_=0;this.y0_=0;this.r0_=0;this.x1_=0;this.y1_=0;this.r1_=0;this.colors_=[]}v.prototype.addColorStop=function(i,Z){Z=Y(Z);this.colors_.push({offset:i,color:Z.color,alpha:Z.alpha})};function u(i,Z){q(i);switch(Z){case"repeat":case null:case"":this.repetition_="repeat";break;case"repeat-x":case"repeat-y":case"no-repeat":this.repetition_=Z;break;default:n("SYNTAX_ERR")}this.src_=i.src;this.width_=i.width;this.height_=i.height}function n(Z){throw new o(Z)}function q(Z){if(!Z||Z.nodeType!=1||Z.tagName!="IMG"){n("TYPE_MISMATCH_ERR")}if(Z.readyState!="complete"){n("INVALID_STATE_ERR")}}function o(Z){this.code=this[Z];this.message=Z+": DOM Exception "+this.code}var x=o.prototype=new Error;x.INDEX_SIZE_ERR=1;x.DOMSTRING_SIZE_ERR=2;x.HIERARCHY_REQUEST_ERR=3;x.WRONG_DOCUMENT_ERR=4;x.INVALID_CHARACTER_ERR=5;x.NO_DATA_ALLOWED_ERR=6;x.NO_MODIFICATION_ALLOWED_ERR=7;x.NOT_FOUND_ERR=8;x.NOT_SUPPORTED_ERR=9;x.INUSE_ATTRIBUTE_ERR=10;x.INVALID_STATE_ERR=11;x.SYNTAX_ERR=12;x.INVALID_MODIFICATION_ERR=13;x.NAMESPACE_ERR=14;x.INVALID_ACCESS_ERR=15;x.VALIDATION_ERR=16;x.TYPE_MISMATCH_ERR=17;G_vmlCanvasManager=E;CanvasRenderingContext2D=W;CanvasGradient=v;CanvasPattern=u;DOMException=o})()};
const UNKNOWN_REPR = '<span class="unknown">?</span>';
const FD_THRESHOLDS = [[0.95, 'red'],
[0.8, 'yellow']];
const SOCKETS_THRESHOLDS = [[1.0, 'red'],
[0.8, 'yellow']];
const PROCESS_THRESHOLDS = [[0.75, 'red'],
[0.5, 'yellow']];
const TAB_HIGHLIGHTER = "\u2192";
const WHITESPACE_HIGHLIGHTER = "\u23B5";
function fmt_string(str, unknown) {
if (unknown == undefined) {
unknown = UNKNOWN_REPR;
}
if (str == undefined) {
return unknown;
}
return fmt_escape_html("" + str);
}
function fmt_si_prefix(num0, max0, thousand, allow_fractions) {
if (num == 0) return 0;
function f(n, m, p) {
if (m > thousand) return f(n / thousand, m / thousand, p + 1);
else return [n, m, p];
}
var num_power = f(num0, max0, 0);
var num = num_power[0];
var max = num_power[1];
var power = num_power[2];
var powers = ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
return (((power != 0 || allow_fractions) && max <= 10) ? num.toFixed(1) :
num.toFixed(0)) + powers[power];
}
function fmt_boolean(b, unknown) {
if (unknown == undefined) unknown = UNKNOWN_REPR;
if (b == undefined) return unknown;
return b ? "&#9679;" : "&#9675;";
}
function fmt_date(d) {
var res = fmt_date0(d);
return res[0] + ' ' + res[1];
}
function fmt_date_mini(d) {
var res = fmt_date0(d);
return res[1] + '<sub>' + res[0] + '</sub>';
}
function fmt_date0(d) {
function f(i) {
return i < 10 ? "0" + i : i;
}
return [d.getFullYear() + "-" + f(d.getMonth() + 1) + "-" +
f(d.getDate()), f(d.getHours()) + ":" + f(d.getMinutes()) +
":" + f(d.getSeconds())];
}
function fmt_timestamp(ts) {
return fmt_date(new Date(ts));
}
function fmt_timestamp_mini(ts) {
return fmt_date_mini(new Date(ts));
}
function fmt_time(t, suffix) {
if (t == undefined || t == 0) return '';
return t + suffix;
}
function fmt_millis(millis) {
return Math.round(millis / 1000) + "s";
}
function fmt_features(obj) {
return fmt_table_short(args_to_features(obj));
}
function fmt_policy_short(obj) {
if (obj.policy != undefined && obj.policy != '') {
return '<abbr class="policy" title="Policy: ' +
fmt_escape_html(obj.policy) + '">' +
link_policy(obj.vhost, obj.policy) + '</abbr> ';
} else {
return '';
}
}
function fmt_op_policy_short(obj) {
if (obj.operator_policy != undefined && obj.operator_policy != '') {
return '<abbr class="policy" title="Operator policy: ' +
fmt_escape_html(obj.operator_policy) + '">' +
fmt_escape_html(obj.operator_policy) + '</abbr> ';
} else {
return '';
}
}
function fmt_features_short(obj) {
var res = '';
var features = args_to_features(obj);
if (obj.owner_pid_details != undefined) {
res += '<acronym title="Exclusive queue: click for owning connection">'
+ link_conn(obj.owner_pid_details.name, "Excl") + '</acronym> ';
}
for (var k in ALL_ARGS) {
if (features[k] != undefined) {
res += '<abbr title="' + k + ': ' + fmt_string(features[k]) +
'">' + ALL_ARGS[k].short + '</abbr> ';
}
}
if (features.arguments) {
res += '<abbr title="' + fmt_table_flat(features.arguments) +
'">Args</abbr> ';
}
return res;
}
function short_conn(name) {
var pat = /^(.*)->/;
var match = pat.exec(name);
return (match != null && match.length == 2) ? match[1] : name;
}
function short_chan(name) {
var pat = /^(.*)->.*( \(.*\))/;
var match = pat.exec(name);
return (match != null && match.length == 3) ? match[1] + match[2] : name;
}
function args_to_features(obj) {
var res = {};
for (var k in obj.arguments) {
if (k in KNOWN_ARGS) {
res[k] = fmt_escape_html(obj.arguments[k]);
}
else {
if (res.arguments == undefined) res.arguments = {};
res.arguments[fmt_escape_html(k)] = fmt_escape_html(obj.arguments[k]);
}
}
if (obj.exclusive) {
res['exclusive'] = true;
}
if (obj.durable) {
res['durable'] = true;
}
if (obj.auto_delete) {
res['auto-delete'] = true;
}
if (obj.internal != undefined && obj.internal) {
res['internal'] = true;
}
if (obj.messages_delayed != undefined){
res['messages delayed'] = obj.messages_delayed;
}
return res;
}
function fmt_mirrors(queue) {
var synced = queue.synchronised_slave_nodes || [];
var unsynced = queue.slave_nodes || [];
unsynced = jQuery.grep(unsynced,
function (node, i) {
return jQuery.inArray(node, synced) == -1;
});
var res = '';
if (synced.length > 0) {
res += ' <abbr title="Synchronised mirrors: ' + synced + '">+' +
synced.length + '</abbr>';
}
if (synced.length == 0 && unsynced.length > 0) {
res += ' <abbr title="There are no synchronised mirrors">+0</abbr>';
}
if (unsynced.length > 0) {
res += ' <abbr class="warning" title="Unsynchronised mirrors: ' +
unsynced + '">+' + unsynced.length + '</abbr>';
}
return res;
}
function fmt_sync_state(queue) {
var res = '<p><b>Syncing: ';
res += (queue.messages == 0) ? 100 : Math.round(100 * queue.sync_messages /
queue.messages);
res += '%</b></p>';
return res;
}
function fmt_channel_mode(ch) {
if (ch.transactional) {
return '<abbr title="Transactional">T</abbr>';
}
else if (ch.confirm) {
return '<abbr title="Confirm">C</abbr>';
}
else {
return '';
}
}
function fmt_color(r, thresholds) {
if (r == undefined) return '';
for (var i in thresholds) {
var threshold = thresholds[i][0];
var color = thresholds[i][1];
if (r >= threshold) {
return color;
}
}
return 'green';
}
function fmt_rate_num(num) {
if (num == undefined) return UNKNOWN_REPR;
else if (num < 1) return num.toFixed(2);
else if (num < 10) return num.toFixed(1);
else return fmt_num_thousands(num);
}
function fmt_num_thousands(num) {
var conv_num = parseFloat(num); // to avoid errors, if someone calls fmt_num_thousands(someNumber.toFixed(0))
return fmt_num_thousands_unfixed(conv_num.toFixed(0));
}
function fmt_num_thousands_unfixed(num) {
if (num == undefined) return UNKNOWN_REPR;
num = '' + num;
if (num.length < 4) return num;
res= fmt_num_thousands_unfixed(num.slice(0, -3)) + ',' + num.slice(-3);
return res;
}
function fmt_percent(num) {
if (num === '') {
return 'N/A';
} else {
return Math.round(num * 100) + '%';
}
}
function pick_rate(fmt, obj, name, mode) {
if (obj == undefined || obj[name] == undefined ||
obj[name + '_details'] == undefined) return '';
var details = obj[name + '_details'];
return fmt(mode == 'avg' ? details.avg_rate : details.rate);
}
function pick_abs(fmt, obj, name, mode) {
if (obj == undefined || obj[name] == undefined ||
obj[name + '_details'] == undefined) return '';
var details = obj[name + '_details'];
return fmt(mode == 'avg' ? details.avg : obj[name]);
}
function fmt_detail_rate(obj, name, mode) {
return pick_rate(fmt_rate, obj, name, mode);
}
function fmt_detail_rate_bytes(obj, name, mode) {
return pick_rate(fmt_rate_bytes, obj, name, mode);
}
// ---------------------------------------------------------------------
// These are pluggable for charts etc
function fmt_plain(num) {
return num;
}
function fmt_plain_axis(num, max) {
return fmt_si_prefix(num, max, 1000, true);
}
function fmt_rate(num) {
return fmt_rate_num(num) + '/s';
}
function fmt_rate_axis(num, max) {
return fmt_plain_axis(num, max) + '/s';
}
function fmt_bytes(bytes) {
if (bytes == undefined) return UNKNOWN_REPR;
return fmt_si_prefix(bytes, bytes, 1024, false) + 'iB';
}
function fmt_bytes_axis(num, max) {
num = parseInt(num);
return fmt_bytes(isNaN(num) ? 0 : num);
}
function fmt_rate_bytes(num) {
return fmt_bytes(num) + '/s';
}
function fmt_rate_bytes_axis(num, max) {
return fmt_bytes_axis(num, max) + '/s';
}
function fmt_ms(num) {
return fmt_rate_num(num) + 'ms';
}
// ---------------------------------------------------------------------
function fmt_maybe_vhost(name) {
return vhosts_interesting ?
' in virtual host <b>' + fmt_escape_html(name) + '</b>'
: '';
}
function fmt_exchange(name) {
return fmt_escape_html(fmt_exchange0(name));
}
function fmt_exchange0(name) {
return name == '' ? '(AMQP default)' : name;
}
function fmt_exchange_type(type) {
for (var i in exchange_types) {
if (exchange_types[i].name == type) {
return fmt_escape_html(type);
}
}
return '<div class="status-red"><abbr title="Exchange type not found. ' +
'Publishing to this exchange will fail.">' + fmt_escape_html(type) +
'</abbr></div>';
}
function fmt_exchange_url(name) {
return name == '' ? 'amq.default' : fmt_escape_html(name);
}
function fmt_download_filename(host) {
var now = new Date();
return host.replace('@', '_') + "_" + now.getFullYear() + "-" +
(now.getMonth() + 1) + "-" + now.getDate() + ".json";
}
function fmt_table_short(table) {
return '<table class="mini">' + fmt_table_body(table, ':') + '</table>';
}
function fmt_table_long(table) {
return '<table class="facts">' + fmt_table_body(table, '') +
'</table>';
}
function fmt_table_body(table, x) {
var res = '';
for (k in table) {
res += '<tr><th>' + fmt_escape_html(k) + x + '</th>' +
'<td>' + fmt_amqp_value(table[k]) + '</td>';
}
return res;
}
function fmt_amqp_value(val) {
if (val instanceof Array) {
var val2 = new Array();
for (var i = 0; i < val.length; i++) {
val2[i] = fmt_amqp_value(val[i]);
}
return val2.join("<br/>");
} else if (val instanceof Object) {
return fmt_table_short(val);
} else {
var t = typeof(val);
if (t == 'string') {
return '<abbr class="type" title="string">' +
fmt_escape_html(val) + '</abbr>';
} else {
return '<abbr class="type" title="' + t + '">' + val + '</abbr>';
}
}
}
function fmt_table_flat(table) {
var res = [];
for (k in table) {
res.push(fmt_escape_html(k) + ': ' + fmt_amqp_value_flat(table[k]));
}
return res.join(', ');
}
function fmt_amqp_value_flat(val) {
if (val instanceof Array) {
var val2 = new Array();
for (var i = 0; i < val.length; i++) {
val2[i] = fmt_amqp_value_flat(val[i]);
}
return '[' + val2.join(",") + ']';
} else if (val instanceof Object) {
return '(' + fmt_table_flat(val) + ')';
} else if (typeof(val) == 'string') {
return fmt_escape_html(val);
} else {
return val;
}
}
function fmt_uptime(u) {
var uptime = Math.floor(u / 1000);
var sec = uptime % 60;
var min = Math.floor(uptime / 60) % 60;
var hour = Math.floor(uptime / 3600) % 24;
var day = Math.floor(uptime / 86400);
if (day > 0)
return day + 'd ' + hour + 'h';
else if (hour > 0)
return hour + 'h ' + min + 'm';
else
return min + 'm ' + sec + 's';
}
function fmt_plugins_small(node) {
if (node.applications === undefined) return '';
var plugins = [];
for (var i = 0; i < node.applications.length; i++) {
var application = node.applications[i];
if (jQuery.inArray(application.name, node.enabled_plugins) != -1 ) {
plugins.push(application.name);
}
}
return '<abbr title="Enabled plugins: ' + plugins.join(", ") + '">' +
plugins.length + '</abbr>';
}
function get_plugins_list(node) {
var result = [];
for (var i = 0; i < node.applications.length; i++) {
var application = node.applications[i];
if (jQuery.inArray(application.name, node.enabled_plugins) != -1 ) {
result.push(application);
}
}
return result;
}
function fmt_rabbit_version(applications) {
for (var i in applications) {
if (applications[i].name == 'rabbit') {
return applications[i].version;
}
}
return 'unknown';
}
function fmt_strip_tags(txt) {
if(txt === null) {
return "";
}
if(txt === undefined) {
return "";
}
return ("" + txt).replace(/<(?:.|\n)*?>/gm, '');
}
function fmt_escape_html(txt) {
return fmt_escape_html0(txt).replace(/\n/g, '<br/>');
}
function fmt_escape_html_one_line(txt) {
return fmt_escape_html0(txt).replace(/\n/g, '');
}
function fmt_escape_html0(txt) {
if(txt === null) {
return "";
}
if(txt === undefined) {
return "";
}
return ("" + txt).replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\"/g, '&quot;');
}
function fmt_maybe_wrap(txt, encoding) {
if (encoding == 'string') return fmt_escape_html(txt);
var WRAP = 120;
var res = '';
while (txt != '') {
var i = txt.indexOf('\n');
if (i == -1 || i > WRAP) {
i = Math.min(WRAP, txt.length);
res += txt.substring(0, i) + '\n';
txt = txt.substring(i);
}
else {
res += txt.substring(0, i + 1);
txt = txt.substring(i + 1);
}
}
return fmt_escape_html(res);
}
function fmt_node(node_host) {
return fmt_string(node_host);
}
function fmt_object_state(obj) {
if (obj.state == undefined) return '';
var colour = 'green';
var text = obj.state;
var explanation;
if (obj.idle_since !== undefined) {
colour = 'grey';
explanation = 'Idle since ' + obj.idle_since;
text = 'idle';
}
// Only connections can be 'blocked' or 'blocking'
else if (obj.state == 'blocked') {
colour = 'red';
explanation = 'Resource alarm: connection blocked.';
}
else if (obj.state == 'blocking') {
colour = 'yellow';
explanation = 'Resource alarm: connection will block on publish.';
}
else if (obj.state == 'flow') {
colour = 'yellow';
explanation = 'Publishing rate recently throttled by server.';
}
else if (obj.state == 'terminated') {
colour = 'yellow';
var terminated_by = "";
if (obj.terminated_by) {
terminated_by = " by \"" + String(obj.terminated_by) + "\"";
}
explanation = 'The queue is being deleted' + terminated_by + ".";
}
else if (obj.state == 'down') {
colour = 'red';
explanation = 'The queue is located on a cluster node or nodes that ' +
'are down.';
}
else if (obj.state == 'crashed') {
colour = 'red';
explanation = 'The queue has crashed repeatedly and been unable to ' +
'restart.';
}
else if (obj.state == 'stopped') {
colour = 'red';
explanation = 'The queue process was stopped by the vhost supervisor.';
}
return fmt_state(colour, text, explanation);
}
function fmt_state(colour, text, explanation) {
var key;
if (explanation) {
key = '<abbr class="normal" title="' + explanation + '">' +
text + '</abbr>';
}
else {
key = text;
}
return '<div class="colour-key status-key-' + colour + '"></div>' + key;
}
function fmt_shortened_uri(uri) {
if (typeof uri == 'object') {
var res = '';
for (i in uri) {
res += fmt_shortened_uri(uri[i]) + '<br/>';
}
return res;
}
var uri = fmt_escape_html(uri);
if (uri.indexOf('?') == -1) {
return uri;
}
else {
return '<abbr title="' + uri + '">' +
uri.substr(0, uri.indexOf('?')) + '?...</abbr>';
}
}
function fmt_uri_with_credentials(uri) {
if (typeof uri == 'object') {
var res = [];
for (i in uri) {
res.push(fmt_uri_with_credentials(uri[i]));
}
return res;
}
else if (typeof uri == 'string') {
// mask password
var mask = /^([a-zA-Z0-9+-.]+):\/\/(.*):(.*)@/;
return uri.replace(mask, "$1://$2:[redacted]@");
} else {
return UNKNOWN_REPR;
}
}
function fmt_client_name(properties) {
var res = [];
if (properties.product != undefined) {
res.push(fmt_trunc(properties.product, 120));
}
if (properties.platform != undefined) {
res.push(fmt_trunc(properties.platform, 120));
}
res = res.join(" / ");
if (properties.version != undefined) {
res += '<sub>' + fmt_trunc(properties.version) + '</sub>';
}
if (properties.client_id != undefined) {
res += '<sub>' + fmt_trunc(properties.client_id, 120) + '</sub>';
}
return res;
}
function fmt_trunc(str, max_length) {
return str.length > max_length ?
('<abbr class="normal" title="' + fmt_escape_html(str) + '">' +
fmt_escape_html(str.substring(0, max_length)) + '...</abbr>') :
fmt_escape_html(str);
}
function alt_rows(i, args) {
var css = [(i % 2 == 0) ? 'alt1' : 'alt2'];
if (args != undefined && args['x-internal-purpose'] != undefined) {
css.push('internal-purpose');
}
return ' class="' + css.join(' ') + '"';
}
function esc(str) {
return encodeURIComponent(str);
}
// Replaces a sequence of characters matched by a regular expression
// group with the given character. Replaced group is combined with the stripped
// original using the provided combine function.
function replace_char_sequence_individually(input, regex, replacement, combine) {
let ms = input.match(regex);
if (ms == null) {
return input;
} else {
let n = ms[0].length;
return combine(replacement.repeat(n), input.replace(regex, ""));
}
}
// Replaces a leading sequence of characters matched by a regular expression
// group with the given character.
function replace_leading_chars(input, regex, replacement) {
return replace_char_sequence_individually(input, regex, replacement, function(patch, stripped_input) {
return patch + stripped_input;
});
}
// Replaces a trailing sequence of characters matched by a regular expression
// group with the given character.
function replace_trailing_chars(input, regex, replacement) {
return replace_char_sequence_individually(input, regex, replacement, function(patch, stripped_input) {
return stripped_input + patch;
});
}
// Highlights extra (leading and trailing) whitespace and tab characters
// with suitable Unicode characters for improved visiblity.
// Note that mid-word whitespace should not be highighted, which makes
// the implementation trickier than a simple chain of replace/2 calls.
function highlight_extra_whitespace(str) {
// Highlight leading and trailing whitespace individually but all tabs.
// This assumes that spaces are reasonable to use in the middle of a name
// but tabs are not used intentionally and must be highlighted even in the middle.
return [[replace_trailing_chars, /(\s+)$/g, WHITESPACE_HIGHLIGHTER],
[replace_leading_chars, /^(\s+)/g, WHITESPACE_HIGHLIGHTER]].reduce(function(acc, triplet) {
return triplet[0](acc, triplet[1], triplet[2]);
}, str.replace(/\t/g, TAB_HIGHLIGHTER));
}
function link_conn(name, desc) {
if (desc == undefined) {
return _link_to(short_conn(name), '#/connections/' + esc(name));
}
else {
return _link_to(desc, '#/connections/' + esc(name), false);
}
}
function link_channel(name) {
return _link_to(short_chan(name), '#/channels/' + esc(name));
}
function link_exchange(vhost, name, args) {
var url = esc(vhost) + '/' + (name == '' ? 'amq.default' : esc(name));
return _link_to(fmt_exchange0(highlight_extra_whitespace(name)), '#/exchanges/' + url, true, args);
}
function link_queue(vhost, name, args) {
return _link_to(highlight_extra_whitespace(name), '#/queues/' + esc(vhost) + '/' + esc(name), true, args);
}
function link_vhost(name) {
return _link_to(name, '#/vhosts/' + esc(name));
}
function link_user(name) {
return _link_to(name, '#/users/' + esc(name));
}
function link_node(name) {
return _link_to(fmt_node(name), '#/nodes/' + esc(name));
}
function link_policy(vhost, name) {
return _link_to(name, '#/policies/' + esc(vhost) + '/' + esc(name));
}
function _link_to(name, url, highlight, args) {
if (highlight == undefined) highlight = true;
var title = null;
if (args != undefined && args['x-internal-purpose'] != undefined) {
var purpose = args['x-internal-purpose'];
title = 'This is used internally by the ' + purpose + ' mechanism.';
}
return '<a href="' + url + '"' +
(title ? ' title="' + title + '"' : '') + '>' +
(highlight ? fmt_highlight_filter(name) : fmt_escape_html(name)) +
'</a>';
}
function fmt_highlight_filter(text) {
if (current_filter == '') return fmt_escape_html(text);
var text_to_match = current_filter.toLowerCase();
if (current_filter_regex) {
var potential_match = current_filter_regex.exec(text.toLowerCase());
if (potential_match) {
text_to_match = potential_match[0];
}
}
var ix = text.toLowerCase().indexOf(text_to_match);
var l = text_to_match.length;
if (ix == -1) {
return fmt_escape_html(text);
}
else {
return fmt_escape_html(text.substring(0, ix)) +
'<span class="filter-highlight">' +
fmt_escape_html(text.substring(ix, ix + l)) + '</span>' +
fmt_escape_html(text.substring(ix + l));
}
}
function filter_ui_pg(items, truncate, appendselect) {
var total = items.length;
if (current_filter != '') {
var items2 = [];
for (var i in items) {
var item = items[i];
var item_name = item.name.toLowerCase();
if ((current_filter_regex_on &&
current_filter_regex &&
current_filter_regex.test(item_name)) ||
item_name.indexOf(current_filter.toLowerCase()) != -1) {
items2.push(item);
}
}
items.length = items2.length;
for (var i in items2) items[i] = items2[i];
}
var res = '<div class="filter"><table' +
(current_filter == '' ? '' : ' class="filter-active"') +
'><tr><th>Filter:</th>' +
'<td><input id="filter" type="text" value="' +
fmt_escape_html(current_filter) + '"/>' +
'<input type="checkbox" name="filter-regex-mode" id="filter-regex-mode"' +
(current_filter_regex_on ? ' checked' : '') +
'/><label for="filter-regex-mode">Regex</label> <span class="help" id="filter-regex"></span>' +
'</td></tr></table>';
function items_desc(l) {
return l == 1 ? (l + ' item') : (l + ' items');
}
var selected = current_filter == '' ? (items_desc(items.length)) :
(items.length + ' of ' + items_desc(total) + ' selected');
selected += appendselect;
res += '<p id="filter-truncate"><span class="updatable">' + selected +
'</span>' + truncate + '</p>';
res += '</div>';
return res;
}
function filter_ui(items) {
var current_truncate = parseInt(get_pref('truncate'));
var truncate_input = '<input type="text" id="truncate" value="' +
current_truncate + '">';
var selected = '';
if (items.length > current_truncate) {
selected += '<span id="filter-warning-show"> ' +
'(only showing first</span> ';
items.length = current_truncate;
}
else {
selected += ', page size up to ';
}
return filter_ui_pg(items, truncate_input, selected);
}
function paginate_header_ui(pages, context){
var res = '<h2 class="updatable">';
res += ' All ' + context +' (' + pages.total_count + ((pages.filtered_count != pages.total_count) ? ', filtered down to ' + pages.filtered_count : '') + ')';
res += '</h2>';
return res;
}
function paginate_ui(pages, context){
var res = paginate_header_ui(pages, context);
res += '<div class="hider">';
res += '<h3>Pagination</h3>';
res += '<div class="filter">';
res += '<table class="updatable">';
res += '<tr>';
res += '<th><label for="'+ context +'-page">Page </label> <select id="'+ context +'-page" class="pagination_class pagination_class_select" >';
var page = fmt_page_number_request(context, pages.page);
if (pages.page_count > 0 && page > pages.page_count){
page = pages.page_count;
update_pages(context, page);
return;
};
for (var i = 1; i <= pages.page_count; i++) { ;
if (i == page) {;
res += ' <option selected="selected" value="'+ i + '">' + i + '</option>';
} else { ;
res += '<option value="' + i + '"> ' + i + '</option>';
} };
res += '</select> </th>';
res += '<th><label for="' + context +'-pageof">of </label> ' + pages.page_count +'</th>';
res += '<th><span><label for="'+ context +'-name"> - Filter: </label> <input id="'+ context +'-name" data-page-start="1" class="pagination_class pagination_class_input" type="text"';
res += 'value = ' + fmt_filter_name_request(context, "") + '>';
res += '</input></th></span>';
res += '<th> <input type="checkbox" data-page-start="1" class="pagination_class pagination_class_checkbox" id="'+ context +'-filter-regex-mode"' ;
res += fmt_regex_request(context, "") + '></input> <label for="filter-regex-mode">Regex</label> <span class="help" id="filter-regex"></span></th>' ;
res +=' </table>' ;
res += '<p id="filter-truncate"><span class="updatable">';
res += '<span><label for="'+ context +'-pagesize"> Displaying ' + pages.item_count + ' item'+ ((pages.item_count > 1) ? 's' : '' ) + ' , page size up to: </label> ';
res += ' <input id="'+ context +'-pagesize" data-page-start="1" class="pagination_class shortinput pagination_class_input" type="text" ';
res += 'value = "' + fmt_page_size_request(context, pages.page_size) +'"';
res += 'onkeypress = "return isNumberKey(event)"> </input></span></p>';
res += '</tr>';
res += '</div>';
res += '</div>';
return res;
}
function maybe_truncate(items) {
var maximum = 500;
var str = '';
if (items.length > maximum) {
str = '<p class="warning">Only ' + maximum + ' of ' +
items.length + ' items are shown.</p>';
items.length = maximum;
}
return str;
}
function fmt_sort(display, sort) {
var prefix = '';
if (current_sort == sort) {
prefix = '<span class="arrow">' +
(current_sort_reverse ? '&#9660; ' : '&#9650; ') +
'</span>';
}
return '<a class="sort" sort="' + sort + '">' + prefix + display + '</a>';
}
function group_count(mode, group, bools) {
var count = 0;
for (var i = 0; i < bools.length; i++) {
if (bools[i]) count++;
}
var options = COLUMNS[mode][group];
for (var i = 0; i < options.length; i++) {
var column = options[i][0];
if (show_column(mode, column)) count++;
}
return count;
}
function group_heading(mode, group, bools) {
var count = group_count(mode, group, bools);
if (count == 0) {
return '';
}
else {
return '<th colspan="' + count + '">' + group + '</th>';
}
}
function fmt_permissions(obj, permissions, lookup, show, warning) {
var res = [];
for (var i in permissions) {
var permission = permissions[i];
if (permission[lookup] == obj.name) {
res.push(fmt_escape_html(permission[show]));
}
}
return res.length == 0 ? warning : res.join(', ');
}
var radio_id = 0;
function fmt_radio(name, text, value, current) {
radio_id++;
return '<label class="radio" for="radio-' + radio_id + '">' +
'<input type="radio" id="radio-' + radio_id + '" name="' + name +
'" value="' + value + '"' +
((value == current) ? ' checked="checked"' : '') +
'>' + text + '</label>';
}
function fmt_checkbox(name, text, current) {
return '<label class="checkbox" for="checkbox-' + name + '">' +
'<input type="checkbox" id="checkbox-' + name + '" name="' + name +
'"' + (current ? ' checked="checked"' : '') + '>' + text + '</label>';
}
function properties_size(obj) {
var count = 0;
for (k in obj) {
if (obj.hasOwnProperty(k)) count++;
}
return count;
}
function stored_value_or_default(template, defaultValue){
var stored_value = get_pref(template);
var result = (((stored_value == null)
|| (stored_value == undefined)
|| (stored_value == '')) ? defaultValue :
stored_value);
return ((result == undefined) ? defaultValue : result);
}
function fmt_page_number_request(template, defaultPage){
if ((defaultPage == undefined) || (defaultPage <= 0)) {
defaultPage = 1;
}
return stored_value_or_default(template + '_current_page_number', defaultPage);
}
function fmt_page_size_request(template, defaultPageSize){
if ((defaultPageSize == undefined) || (defaultPageSize < 0)) {
defaultPageSize = 100;
}
var result = stored_value_or_default(template + '_current_page_size', defaultPageSize);
if (result > 500) {
// hard limit
result = 500;
}
return result;
}
function fmt_filter_name_request(template, defaultName){
return fmt_escape_html(stored_value_or_default(template + '_current_filter_name', defaultName));
}
function fmt_regex_request(template, defaultName){
return fmt_escape_html(stored_value_or_default(template + '_current_regex', defaultName));
}
function fmt_vhost_state(vhost){
var cluster_state = vhost.cluster_state;
var down_nodes = [];
var ok_count = 0;
var non_ok_count = 0;
for(var node in cluster_state){
var node_state = cluster_state[node];
if(cluster_state[node] == "stopped" || cluster_state[node] == "nodedown"){
non_ok_count++;
down_nodes.push(node);
} else if(cluster_state[node] == "running"){
ok_count++;
}
}
if(non_ok_count == 0 ){
return fmt_state('green', 'running', '');
} else if(non_ok_count > 0 && ok_count == 0){
return fmt_state('red', 'stopped', 'Vhost supervisor is not running.');
} else if(non_ok_count > 0 && ok_count > 0){
return fmt_state('yellow', 'partial', 'Vhost supervisor is stopped on some cluster nodes: ' + down_nodes.join(', '));
}
}
function isNumberKey(evt){
var charCode = (evt.which) ? evt.which : event.keyCode;
if (charCode > 31 && (charCode < 48 || charCode > 57))
return false;
return true;
}
///////////////////////
// //
// Genuine constants //
// //
///////////////////////
// Just used below
function map(list) {
var res = {};
for (i in list) {
res[list[i]] = '';
}
return res;
}
// Extension arguments that we know about and present specially in the UI.
var KNOWN_ARGS = {'alternate-exchange': {'short': 'AE', 'type': 'string'},
'x-message-ttl': {'short': 'TTL', 'type': 'int'},
'x-expires': {'short': 'Exp', 'type': 'int'},
'x-max-length': {'short': 'Lim', 'type': 'int'},
'x-max-length-bytes': {'short': 'Lim B', 'type': 'int'},
'x-overflow': {'short': 'Ovfl', 'type': 'string'},
'x-dead-letter-exchange': {'short': 'DLX', 'type': 'string'},
'x-dead-letter-routing-key': {'short': 'DLK', 'type': 'string'},
'x-queue-master-locator': {'short': 'ML', 'type': 'string'},
'x-max-priority': {'short': 'Pri', 'type': 'int'}};
// Things that are like arguments that we format the same way in listings.
var IMPLICIT_ARGS = {'durable': {'short': 'D', 'type': 'boolean'},
'auto-delete': {'short': 'AD', 'type': 'boolean'},
'internal': {'short': 'I', 'type': 'boolean'},
'exclusive': {'short': 'Excl', 'type': 'boolean'},
'messages delayed':{'short': 'DM', 'type': 'int'}};
// Both the above
var ALL_ARGS = {};
for (var k in IMPLICIT_ARGS) ALL_ARGS[k] = IMPLICIT_ARGS[k];
for (var k in KNOWN_ARGS) ALL_ARGS[k] = KNOWN_ARGS[k];
var NAVIGATION = {'Overview': ['#/', "management"],
'Connections': ['#/connections', "management"],
'Channels': ['#/channels', "management"],
'Exchanges': ['#/exchanges', "management"],
'Queues': ['#/queues', "management"],
'Admin':
[{'Users': ['#/users', "administrator"],
'Virtual Hosts': ['#/vhosts', "administrator"],
'Feature Flags': ['#/feature-flags', "administrator"],
'Policies': ['#/policies', "management"],
'Limits': ['#/limits', "management"],
'Cluster': ['#/cluster-name', "administrator"]},
"management"]
};
var CHART_RANGES = {'global': [], 'basic': []};
var ALL_CHART_RANGES = {};
var COLUMNS =
{'exchanges' :
{'Overview': [['type', 'Type', true],
['features', 'Features (with policy)', true],
['features_no_policy', 'Features (no policy)', false],
['policy', 'Policy', false]],
'Message rates': [['rate-in', 'rate in', true],
['rate-out', 'rate out', true]]},
'queues' :
{'Overview': [['features', 'Features (with policy)', true],
['features_no_policy', 'Features (no policy)', false],
['policy', 'Policy', false],
['consumers', 'Consumer count', false],
['consumer_utilisation', 'Consumer utilisation', false],
['state', 'State', true]],
'Messages': [['msgs-ready', 'Ready', true],
['msgs-unacked', 'Unacknowledged', true],
['msgs-ram', 'In memory', false],
['msgs-persistent', 'Persistent', false],
['msgs-total', 'Total', true]],
'Message bytes': [['msg-bytes-ready', 'Ready', false],
['msg-bytes-unacked', 'Unacknowledged', false],
['msg-bytes-ram', 'In memory', false],
['msg-bytes-persistent', 'Persistent', false],
['msg-bytes-total', 'Total', false]],
'Message rates': [['rate-incoming', 'incoming', true],
['rate-deliver', 'deliver / get', true],
['rate-redeliver', 'redelivered', false],
['rate-ack', 'ack', true]]},
'channels' :
{'Overview': [['user', 'User name', true],
['mode', 'Mode', true],
['state', 'State', true]],
'Details': [['msgs-unconfirmed', 'Unconfirmed', true],
['prefetch', 'Prefetch', true],
['msgs-unacked', 'Unacked', true]],
'Transactions': [['msgs-uncommitted', 'Msgs uncommitted', false],
['acks-uncommitted', 'Acks uncommitted', false]],
'Message rates': [['rate-publish', 'publish', true],
['rate-confirm', 'confirm', true],
['rate-return', 'return (mandatory)', false],
['rate-deliver', 'deliver / get', true],
['rate-redeliver', 'redelivered', false],
['rate-ack', 'ack', true]]},
'connections':
{'Overview': [['user', 'User name', true],
['state', 'State', true]],
'Details': [['ssl', 'SSL / TLS', true],
['ssl_info', 'SSL Details', false],
['protocol', 'Protocol', true],
['channels', 'Channels', true],
['channel_max', 'Channel max', false],
['frame_max', 'Frame max', false],
['auth_mechanism', 'Auth mechanism', false],
['client', 'Client', false]],
'Network': [['from_client', 'From client', true],
['to_client', 'To client', true],
['heartbeat', 'Heartbeat', false],
['connected_at', 'Connected at', false]]},
'vhosts':
{'Overview': [['cluster-state', 'Cluster state', false]],
'Messages': [['msgs-ready', 'Ready', true],
['msgs-unacked', 'Unacknowledged', true],
['msgs-total', 'Total', true]],
'Network': [['from_client', 'From client', true],
['to_client', 'To client', true]],
'Message rates': [['rate-publish', 'publish', true],
['rate-deliver', 'deliver / get', true]]},
'overview':
{'Statistics': [['file_descriptors', 'File descriptors', true],
['socket_descriptors', 'Socket descriptors', true],
['erlang_processes', 'Erlang processes', true],
['memory', 'Memory', true],
['disk_space', 'Disk space', true]],
'General': [['uptime', 'Uptime', true],
['info', 'Info', true],
['reset_stats', 'Reset stats', true]]}};
// All help ? popups
var HELP = {
'exchange-auto-delete':
'If yes, the exchange will delete itself after at least one queue or exchange has been bound to this one, and then all queues or exchanges have been unbound.',
'exchange-internal':
'If yes, clients cannot publish to this exchange directly. It can only be used with exchange to exchange bindings.',
'exchange-alternate':
'If messages to this exchange cannot otherwise be routed, send them to the alternate exchange named here.<br/>(Sets the "<a target="_blank" href="https://rabbitmq.com/ae.html">alternate-exchange</a>" argument.)',
'queue-message-ttl':
'How long a message published to a queue can live before it is discarded (milliseconds).<br/>(Sets the "<a target="_blank" href="https://rabbitmq.com/ttl.html#per-queue-message-ttl">x-message-ttl</a>" argument.)',
'queue-expires':
'How long a queue can be unused for before it is automatically deleted (milliseconds).<br/>(Sets the "<a target="_blank" href="https://rabbitmq.com/ttl.html#queue-ttl">x-expires</a>" argument.)',
'queue-max-length':
'How many (ready) messages a queue can contain before it starts to drop them from its head.<br/>(Sets the "<a target="_blank" href="https://rabbitmq.com/maxlength.html">x-max-length</a>" argument.)',
'queue-max-length-bytes':
'Total body size for ready messages a queue can contain before it starts to drop them from its head.<br/>(Sets the "<a target="_blank" href="https://rabbitmq.com/maxlength.html">x-max-length-bytes</a>" argument.)',
'queue-auto-delete':
'If yes, the queue will delete itself after at least one consumer has connected, and then all consumers have disconnected.',
'queue-dead-letter-exchange':
'Optional name of an exchange to which messages will be republished if they are rejected or expire.<br/>(Sets the "<a target="_blank" href="https://rabbitmq.com/dlx.html">x-dead-letter-exchange</a>" argument.)',
'queue-dead-letter-routing-key':
'Optional replacement routing key to use when a message is dead-lettered. If this is not set, the message\'s original routing key will be used.<br/>(Sets the "<a target="_blank" href="https://rabbitmq.com/dlx.html">x-dead-letter-routing-key</a>" argument.)',
'queue-max-priority':
'Maximum number of priority levels for the queue to support; if not set, the queue will not support message priorities.<br/>(Sets the "<a target="_blank" href="https://rabbitmq.com/priority.html">x-max-priority</a>" argument.)',
'queue-lazy':
'Set the queue into lazy mode, keeping as many messages as possible on disk to reduce RAM usage; if not set, the queue will keep an in-memory cache to deliver messages as fast as possible.<br/>(Sets the "<a target="_blank" href="https://www.rabbitmq.com/lazy-queues.html">x-queue-mode</a>" argument.)',
'queue-overflow':
'Sets the <a target="_blank" href="https://www.rabbitmq.com/maxlength.html#overflow-behaviour">queue overflow behaviour</a>. This determines what happens to messages when the maximum length of a queue is reached. Valid values are <code>drop-head</code> or <code>reject-publish</code>.',
'queue-master-locator':
'Set the queue into master location mode, determining the rule by which the queue master is located when declared on a cluster of nodes.<br/>(Sets the "<a target="_blank" href="https://www.rabbitmq.com/ha.html">x-queue-master-locator</a>" argument.)',
'queue-messages':
'<p>Message counts.</p><p>Note that "in memory" and "persistent" are not mutually exclusive; persistent messages can be in memory as well as on disc, and transient messages can be paged out if memory is tight. Non-durable queues will consider all messages to be transient.</p>',
'queue-message-body-bytes':
'<p>The sum total of the sizes of the message bodies in this queue. This only counts message bodies; it does not include message properties (including headers) or metadata used by the queue.</p><p>Note that "in memory" and "persistent" are not mutually exclusive; persistent messages can be in memory as well as on disc, and transient messages can be paged out if memory is tight. Non-durable queues will consider all messages to be transient.</p><p>If a message is routed to multiple queues on publication, its body will be stored only once (in memory and on disk) and shared between queues. The value shown here does not take account of this effect.</p>',
'queue-process-memory':
'Total memory used by this queue process. This does not include in-memory message bodies (which may be shared between queues and will appear in the global "binaries" memory) but does include everything else.',
'queue-consumer-utilisation':
'Fraction of the time that the queue is able to immediately deliver messages to consumers. If this number is less than 100% you may be able to deliver messages faster if: \
<ul> \
<li>There were more consumers or</li> \
<li>The consumers were faster or</li> \
<li>The consumers had a higher prefetch count</li> \
</ul>',
'internal-users-only':
'Only users within the internal RabbitMQ database are shown here. Other users (e.g. those authenticated over LDAP) will not appear.',
'export-definitions':
'The definitions consist of users, virtual hosts, permissions, parameters, exchanges, queues, policies and bindings. They do not include the contents of queues. Exclusive queues will not be exported.',
'export-definitions-vhost':
'The definitions exported for a single virtual host consist of exchanges, queues, bindings and policies.',
'import-definitions':
'The definitions that are imported will be merged with the current definitions. If an error occurs during import, any changes made will not be rolled back.',
'import-definitions-vhost':
'For a single virtual host, only exchanges, queues, bindings and policies are imported.',
'exchange-rates-incoming':
'The incoming rate is the rate at which messages are published directly to this exchange.',
'exchange-rates-outgoing':
'The outgoing rate is the rate at which messages enter queues, having been published directly to this exchange.',
'channel-mode':
'Channel guarantee mode. Can be one of the following, or neither:<br/> \
<dl> \
<dt><abbr title="Confirm">C</abbr> &ndash; <a target="_blank" href="https://www.rabbitmq.com/confirms.html">confirm</a></dt> \
<dd>Channel will send streaming publish confirmations.</dd> \
<dt><abbr title="Transactional">T</abbr> &ndash; <a target="_blank" href="https://www.rabbitmq.com/amqp-0-9-1-reference.html#class.tx">transactional</a></dt> \
<dd>Channel is transactional.</dd> \
</dl>',
'channel-prefetch':
'Channel prefetch counts. \
<p> \
Each channel can have two prefetch counts: A per-consumer count, which \
will limit each new consumer created on the channel, and a global \
count, which is shared between all consumers on the channel.\
</p> \
<p> \
This column shows one, the other, or both limits if they are set. \
</p>',
'file-descriptors':
'<p>File descriptor count and limit, as reported by the operating \
system. The count includes network sockets and file handles.</p> \
<p>To optimize disk access RabbitMQ uses as many free descriptors as are \
available, so the count may safely approach the limit. \
However, if most of the file descriptors are used by sockets then \
persister performance will be negatively impacted.</p> \
<p>To change the limit on Unix / Linux, use "ulimit -n". To change \
the limit on Windows, set the ERL_MAX_PORTS environment variable</p> \
<p>To report used file handles on Windows, handle.exe from \
sysinternals must be installed in your path. You can download it \
<a target="_blank" href="https://technet.microsoft.com/en-us/sysinternals/bb896655">here</a>.</p>',
'socket-descriptors':
'The network sockets count and limit managed by RabbitMQ.<br/> \
When the limit is exhausted RabbitMQ will stop accepting new \
network connections.',
'memory-alarm':
'<p>The <a target="_blank" href="https://www.rabbitmq.com/memory.html#memsup">memory \
alarm</a> for this node has gone off. It will block \
incoming network traffic until the memory usage drops below \
the watermark.</p>\
<p>Note that the pale line in this case indicates the high watermark \
in relation to how much memory is used in total. </p>',
'disk-free-alarm':
'The <a target="_blank" href="https://www.rabbitmq.com/memory.html#diskfreesup">disk \
free space alarm</a> for this node has gone off. It will block \
incoming network traffic until the amount of free space exceeds \
the limit.',
'message-get-requeue':
'<p>Clicking "Get Message(s)" will consume messages from the queue. \
If requeue is set the message will be put back into the queue in place, \
but "redelivered" will be set on the message.</p> \
<p>If requeue is not set messages will be removed from the queue.</p> \
<p>Furthermore, message payloads will be truncated to 50000 bytes.</p>',
'message-publish-headers':
'Headers can have any name. Only long string headers can be set here.',
'message-publish-properties':
'<p>You can set other message properties here (delivery mode and headers \
are pulled out as the most common cases).</p>\
<p>Invalid properties will be ignored. Valid properties are:</p>\
<ul>\
<li>content_type</li>\
<li>content_encoding</li>\
<li>priority</li>\
<li>correlation_id</li>\
<li>reply_to</li>\
<li>expiration</li>\
<li>message_id</li>\
<li>timestamp</li>\
<li>type</li>\
<li>user_id</li>\
<li>app_id</li>\
<li>cluster_id</li>\
</ul>',
'string-base64':
'<p>AMQP message payloads can contain any binary content. They can \
therefore be difficult to display in a browser. The options here \
have the following meanings:</p> \
<dl> \
<dt>Auto string / base64</dt> \
<dd>If the message payload can be interpreted as a string in UTF-8 \
encoding, do so. Otherwise return the payload encoded as \
base64.</dd> \
<dt>base64</dt> \
<dd>Return the payload encoded as base64 unconditionally.</dd> \
</dl>',
'user-tags':
'Comma-separated list of tags to apply to the user. Currently \
<a target="_blank" href="https://www.rabbitmq.com/management.html#permissions">supported \
by the management plugin</a>: \
<dl> \
<dt>management</dt> \
<dd> \
User can access the management plugin \
</dd> \
<dt>policymaker</dt> \
<dd> \
User can access the management plugin and manage policies and \
parameters for the vhosts they have access to. \
</dd> \
<dt>monitoring</dt> \
<dd> \
User can access the management plugin and see all connections and \
channels as well as node-related information. \
</dd> \
<dt>administrator</dt> \
<dd> \
User can do everything monitoring can do, manage users, \
vhosts and permissions, close other user\'s connections, and manage \
policies and parameters for all vhosts. \
</dd> \
</dl> \
<p> \
Note that you can set any tag here; the links for the above four \
tags are just for convenience. \
</p>',
'queued-messages':
'<dl> \
<dt>Ready</dt>\
<dd>Number of messages that are available to be delivered now.</dd>\
<dt>Unacknowledged</dt>\
<dd>Number of messages for which the server is waiting for acknowledgement.</dd>\
<dt>Total</dt>\
<dd>The total of these two numbers.</dd>\
</dl>',
'message-rates':
'Only rates for which some activity is taking place will be shown.\
<dl>\
<dt>Publish</dt>\
<dd>Rate at which messages are entering the server.</dd>\
<dt>Publisher confirm</dt>\
<dd>Rate at which the server is confirming publishes.</dd>\
<dt>Deliver (manual ack)</dt>\
<dd>Rate at which messages are delivered to consumers that use manual acknowledgements.</dd>\
<dt>Deliver (auto ack)</dt>\
<dd>Rate at which messages are delivered to consumers that use automatic acknowledgements.</dd>\
<dt>Consumer ack</dt>\
<dd>Rate at which messages are being acknowledged by consumers.</dd>\
<dt>Redelivered</dt>\
<dd>Rate at which messages with the \'redelivered\' flag set are being delivered. Note that these messages will <b>also</b> be counted in one of the delivery rates above.</dd>\
<dt>Get (manual ack)</dt>\
<dd>Rate at which messages requiring acknowledgement are being delivered in response to basic.get.</dd>\
<dt>Get (auto ack)</dt>\
<dd>Rate at which messages not requiring acknowledgement are being delivered in response to basic.get.</dd>\
<dt>Return</dt>\
<dd>Rate at which basic.return is sent to publishers for unroutable messages published with the \'mandatory\' flag set.</dd>\
<dt>Disk read</dt>\
<dd>Rate at which queues read messages from disk.</dd>\
<dt>Disk write</dt>\
<dd>Rate at which queues write messages to disk.</dd>\
</dl>\
<p>\
Note that the last two items originate in queues rather than \
channels; they may therefore be slightly out of sync with other \
statistics.\
</p>',
'disk-monitoring-no-watermark' : 'There is no <a target="_blank" href="https://www.rabbitmq.com/memory.html#diskfreesup">disk space low watermark</a> set. RabbitMQ will not take any action to avoid running out of disk space.',
'resource-counts' : 'Shows total number of objects for all virtual hosts the current user has access to.',
'memory-use' : '<p>Note that the memory details shown here are only updated on request - they could be too expensive to calculate every few seconds on a busy server.</p><p><a target="_blank" href="https://www.rabbitmq.com/memory-use.html">Read more</a> on memory use.</p>',
'memory-calculation-strategy-breakdown' : '<p>The setting <code>vm_memory_calculation_strategy</code> defines which of the below memory values is used to check if the memory usage reaches the watermark or paging to disk is required.</p><p><a target="_blank" href="https://www.rabbitmq.com/memory-use.html">Read more</a> on memory use.</p>',
'memory-calculation-strategy' : '<p>This value can be calculated using different strategies the <code>vm_memory_calculation_strategy</code> config setting.</p><p><a target="_blank" href="https://www.rabbitmq.com/memory-use.html">Read more</a> on memory use.</p>',
'binary-use' : '<p>Binary accounting is not exact; binaries are shared between processes (and thus the same binary might be counted in more than one section), and the VM does not allow us to track binaries that are not associated with processes (so some binary use might not appear at all).</p>',
'policy-ha-mode' : 'One of <code>all</code> (mirror to all nodes in the cluster), <code>exactly</code> (mirror to a set number of nodes) or <code>nodes</code> (mirror to an explicit list of nodes). If you choose one of the latter two, you must also set <code>ha-params</code>.',
'policy-ha-params' : 'Absent if <code>ha-mode</code> is <code>all</code>, a number\
if <code>ha-mode</code> is <code>exactly</code>, or a list\
of strings if <code>ha-mode</code> is <code>nodes</code>.',
'policy-ha-sync-mode' : 'One of <code>manual</code> or <code>automatic</code>. <a target="_blank" href="https://www.rabbitmq.com/ha.html#unsynchronised-mirrors">Learn more</a>',
'policy-ha-promote-on-shutdown' : 'One of <code>when-synced</code> or <code>always</code>. <a target="_blank" href="https://www.rabbitmq.com/ha.html#unsynchronised-mirrors">Learn more</a>',
'policy-ha-promote-on-failure' : 'One of <code>when-synced</code> or <code>always</code>. <a target="_blank" href="https://www.rabbitmq.com/ha.html#unsynchronised-mirrors">Learn more</a>',
'policy-federation-upstream-set' :
'A string; only if the federation plugin is enabled. Chooses the name of a set of upstreams to use with federation, or "all" to use all upstreams. Incompatible with <code>federation-upstream</code>.',
'policy-federation-upstream' :
'A string; only if the federation plugin is enabled. Chooses a specific upstream set to use for federation. Incompatible with <code>federation-upstream-set</code>.',
'handle-exe' : 'In order to monitor the number of file descriptors in use on Windows, RabbitMQ needs the <a href="https://technet.microsoft.com/en-us/sysinternals/bb896655" target="_blank">handle.exe command line tool from Microsoft</a>. Download it and place it in the path (e.g. in C:\Windows).',
'filter-regex' :
'Whether to enable regular expression matching. Both string literals \
and regular expressions are matched in a case-insensitive manner.<br/></br/> \
(<a href="https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions" target="_blank">Regular expression reference</a>)',
'plugins' :
'Note that only plugins which are both explicitly enabled and running are shown here.',
'io-operations':
'Rate of I/O operations. Only operations performed by the message \
persister are shown here (e.g. metadata changes in Mnesia or writes \
to the log files are not shown).\
<dl>\
<dt>Read</dt>\
<dd>Rate at which data is read from the disk.</dd>\
<dt>Write</dt>\
<dd>Rate at which data is written to the disk.</dd>\
<dt>Seek</dt>\
<dd>Rate at which the broker switches position while reading or \
writing to disk.</dd>\
<dt>Sync</dt>\
<dd>Rate at which the broker invokes <code>fsync()</code> to ensure \
data is flushed to disk.</dd>\
<dt>Reopen</dt>\
<dd>Rate at which the broker recycles file handles in order to support \
more queues than it has file handles. If this operation is occurring \
frequently you may get a performance boost from increasing the number \
of file handles available.</dd>\
</dl>',
'mnesia-transactions':
'Rate at which Mnesia transactions are initiated on this node (this node \
will also take part in Mnesia transactions initiated on other nodes).\
<dl>\
<dt>RAM only</dt>\
<dd>Rate at which RAM-only transactions take place (e.g. creation / \
deletion of transient queues).</dd>\
<dt>Disk</dt>\
<dd>Rate at which disk (and RAM) transactions take place (.e.g \
creation / deletion of durable queues).</dd>\
</dl>',
'persister-operations-msg':
'Rate at which per-message persister operations take place on this node. See \
<a href="https://www.rabbitmq.com/persistence-conf.html" target="_blank">here</a> \
for more information on the persister. \
<dl>\
<dt>QI Journal</dt>\
<dd>Rate at which message information (publishes, deliveries and \
acknowledgements) is written to queue index journals.</dd>\
<dt>Store Read</dt>\
<dd>Rate at which messages are read from the message store.</dd>\
<dt>Store Write</dt>\
<dd>Rate at which messages are written to the message store.</dd>\
</dl>',
'persister-operations-bulk':
'Rate at which whole-file persister operations take place on this node. See \
<a href="https://www.rabbitmq.com/persistence-conf.html" target="_blank">here</a> \
for more information on the persister. \
<dl>\
<dt>QI Read</dt>\
<dd>Rate at which queue index segment files are read.</dd>\
<dt>QI Write</dt>\
<dd>Rate at which queue index segment files are written. </dd>\
</dl>',
'gc-operations':
'Rate at which garbage collection operations take place on this node.',
'gc-bytes':
'Rate at which memory is reclaimed by the garbage collector on this node.',
'context-switches-operations':
'Rate at which runtime context switching takes place on this node.',
'process-reductions':
'Rate at which reductions take place on this process.',
'connection-operations':
' <dl>\
<dt>Created</dt>\
<dd>Rate at which connections are created.</dd>\
<dt>Closed</dt>\
<dd>Rate at which connections are closed.</dd>\
</dl> ',
'channel-operations':
' <dl>\
<dt>Created</dt>\
<dd>Rate at which channels are created.</dd>\
<dt>Closed</dt>\
<dd>Rate at which channels are closed.</dd>\
</dl> ',
'queue-operations':
' <dl>\
<dt>Declared</dt>\
<dd>Rate at which queues are declared by clients.</dd>\
<dt>Created</dt>\
<dd>Rate at which queues are created. Declaring a queue that already exists counts for a "Declared" event, but not for a "Created" event. </dd>\
<dt>Deleted</dt>\
<dd>Rate at which queues are deleted.</dd>\
</dl> '
};
///////////////////////////////////////////////////////////////////////////
// //
// Mostly constant, typically get set once at startup (or rarely anyway) //
// //
///////////////////////////////////////////////////////////////////////////
// All these are to do with hiding UI elements if
var rates_mode; // ...there are no fine stats
var user_administrator; // ...user is not an admin
var is_user_policymaker; // ...user is not a policymaker
var user_monitor; // ...user cannot monitor
var nodes_interesting; // ...we are not in a cluster
var vhosts_interesting; // ...there is only one vhost
var rabbit_versions_interesting; // ...all cluster nodes run the same version
// Extensions write to this, the dispatcher maker reads it
var dispatcher_modules = [];
// We need to know when all extension script files have loaded
var extension_count;
// The dispatcher needs access to the Sammy app
var app;
// Used for the new exchange form, and to display broken exchange types
var exchange_types;
// Used for access control
var user_tags;
var user;
// Set up the above vars
function setup_global_vars() {
var overview = JSON.parse(sync_get('/overview'));
rates_mode = overview.rates_mode;
user_tags = expand_user_tags(user.tags.split(","));
user_administrator = jQuery.inArray("administrator", user_tags) != -1;
is_user_policymaker = jQuery.inArray("policymaker", user_tags) != -1;
user_monitor = jQuery.inArray("monitoring", user_tags) != -1;
exchange_types = overview.exchange_types.map(function(xt) { return xt.name; });
cluster_name = fmt_escape_html(overview.cluster_name);
$('#logout').before(
'<li>Cluster ' + (user_administrator ? '<a href="#/cluster-name">' + cluster_name + '</a>' : cluster_name) + '</li>'
);
user_name = fmt_escape_html(user.name);
$('#header #logout').prepend(
'User ' + (user_administrator ? '<a href="#/users/' + user_name + '">' + user_name + '</a>' : user_name)
);
$('#versions').html(
'<abbr title="Available exchange types: ' + exchange_types.join(", ") + '">' + fmt_escape_html(overview.rabbitmq_version) + '</abbr>' +
'<abbr title="' + fmt_escape_html(overview.erlang_full_version) + '">Erlang ' + fmt_escape_html(overview.erlang_version) + '</abbr>'
);
nodes_interesting = false;
rabbit_versions_interesting = false;
if (user_monitor) {
var nodes = JSON.parse(sync_get('/nodes'));
if (nodes.length > 1) {
nodes_interesting = true;
var v = '';
for (var i = 0; i < nodes.length; i++) {
var v1 = fmt_rabbit_version(nodes[i].applications);
if (v1 != 'unknown') {
if (v != '' && v != v1) rabbit_versions_interesting = true;
v = v1;
}
}
}
}
vhosts_interesting = JSON.parse(sync_get('/vhosts')).length > 1;
current_vhost = get_pref('vhost');
exchange_types = overview.exchange_types;
setup_chart_ranges(overview.sample_retention_policies);
}
function setup_chart_ranges(srp) {
var range_types = ['global', 'basic'];
var default_ranges = {
60: ['60|5', 'Last minute'],
600: ['600|5', 'Last ten minutes'],
3600: ['3600|60', 'Last hour'],
28800: ['28800|600', 'Last eight hours'],
86400: ['86400|1800', 'Last day']
};
for (var range in default_ranges) {
var data = default_ranges[range];
var range = data[0];
var desc = data[1];
ALL_CHART_RANGES[range] = desc;
}
for (var i = 0; i < range_types.length; ++i) {
var range_type = range_types[i];
if (srp.hasOwnProperty(range_type)) {
var srp_range_types = srp[range_type];
var last_minute_added = false;
for (var j = 0; j < srp_range_types.length; ++j) {
var srp_range = srp_range_types[j];
if (default_ranges.hasOwnProperty(srp_range)) {
if (srp_range === 60) {
last_minute_added = true;
}
var v = default_ranges[srp_range];
CHART_RANGES[range_type].push(v);
}
}
if (!last_minute_added) {
var last_minute = default_ranges[60];
CHART_RANGES[range_type].unshift(last_minute);
}
}
}
}
function expand_user_tags(tags) {
var new_tags = [];
for (var i = 0; i < tags.length; i++) {
var tag = tags[i];
new_tags.push(tag);
switch (tag) { // Note deliberate fall-through
case "administrator": new_tags.push("monitoring");
new_tags.push("policymaker");
case "monitoring": new_tags.push("management");
break;
case "policymaker": new_tags.push("management");
default: break;
}
}
return new_tags;
}
////////////////////////////////////////////////////
// //
// Change frequently (typically every "new page") //
// //
////////////////////////////////////////////////////
// Which top level template we're showing
var current_template;
// Which JSON requests do we need to populate it
var current_reqs;
// And which of those have yet to return (so we can cancel them when
// changing current_template).
var outstanding_reqs = [];
// Which tab is highlighted
var current_highlight;
// Which vhost are we looking at
var current_vhost = '';
// What is our current sort order
var current_sort;
var current_sort_reverse = false;
var current_filter = '';
var current_filter_regex_on = false;
var current_filter_regex;
var current_truncate;
// The timer object for auto-updates, and how often it goes off
var timer;
var timer_interval;
// When did we last connect successfully (for the "could not connect" error)
var last_successful_connect;
// Every 200 updates without user interaction we do a full refresh, to
// work around memory leaks in browser DOM implementations.
// TODO: maybe we don't need this any more?
var update_counter = 0;
// Holds chart data in between writing the div in an ejs and rendering
// the chart.
var chart_data = {};
// whenever a UI requests a page that doesn't exist
// because things were deleted between refreshes
var last_page_out_of_range_error = 0;
/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */
!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=v.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:y}),"function"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,S="sizzle"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),D=function(e,t){return e===t&&(l=!0),0},j={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",F=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",B=new RegExp(M+"+","g"),$=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="<a id='"+S+"'></a><select id='"+S+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!=C&&T(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&j.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+"").replace(re,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(D),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1<t.indexOf(i):"$="===r?i&&t.slice(-i.length)===i:"~="===r?-1<(" "+t.replace(B," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(h,e,t,g,v){var y="nth"!==h.slice(0,3),m="last"!==h.slice(-4),x="of-type"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?"nextSibling":"previousSibling",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l="only"===h&&!u&&"nextSibling"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[k,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[k,d]),a===e))break;return(d-=v)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return a[S]?a(o):1<a.length?(t=[e,e,"",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace($,"$1"));return s[S]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||"")||se.error("unsupported lang: "+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&"parentNode"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[k,p];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[S]||(e[S]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===k&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,v,y,e){return v&&!v[S]&&(v=Ce(v)),y&&!y[S]&&(y=Ce(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?y||(e?d:l||v)?[]:t:f;if(g&&g(f,p,n,r),v){i=Te(p,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(y||d){if(y){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);y(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=y?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),y?y(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[" "],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace($,"$1"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace($," ")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,x,r,i=[],o=[],a=A[e+" "];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[S]?i.push(a):o.push(a);(a=A(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l="0",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG("*",i),h=k+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==C||(T(o),n=!E);while(s=v[a++])if(s(o,t||C,n)){r.push(o);break}i&&(k=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(k=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},d.sortStable=S.split("").sort(D).join("")===S,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement("fieldset"))}),ce(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||fe("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||fe("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute("disabled")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);S.find=d,S.expr=d.selectors,S.expr[":"]=S.expr.pseudos,S.uniqueSort=S.unique=d.uniqueSort,S.text=d.getText,S.isXMLDoc=d.isXML,S.contains=d.contains,S.escapeSelector=d.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},T=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},k=S.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1<i.call(n,e)!==r}):S.filter(n,e,r)}S.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?S.find.matchesSelector(r,e)?[r]:[]:S.find.matches(e,S.grep(t,function(e){return 1===e.nodeType}))},S.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(S(e).filter(function(){for(t=0;t<r;t++)if(S.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)S.find(e,i[t],n);return 1<r?S.uniqueSort(n):n},filter:function(e){return this.pushStack(D(this,e||[],!1))},not:function(e){return this.pushStack(D(this,e||[],!0))},is:function(e){return!!D(this,"string"==typeof e&&k.test(e)?S(e):e||[],!1).length}});var j,q=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(S.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&S(e);if(!k.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&S.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?S.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?i.call(S(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(S.uniqueSort(S.merge(this.get(),S(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),S.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,"parentNode")},parentsUntil:function(e,t,n){return h(e,"parentNode",n)},next:function(e){return O(e,"nextSibling")},prev:function(e){return O(e,"previousSibling")},nextAll:function(e){return h(e,"nextSibling")},prevAll:function(e){return h(e,"previousSibling")},nextUntil:function(e,t,n){return h(e,"nextSibling",n)},prevUntil:function(e,t,n){return h(e,"previousSibling",n)},siblings:function(e){return T((e.parentNode||{}).firstChild,e)},children:function(e){return T(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(A(e,"template")&&(e=e.content||e),S.merge([],e.childNodes))}},function(r,i){S.fn[r]=function(e,t){var n=S.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=S.filter(t,n)),1<this.length&&(H[r]||S.uniqueSort(n),L.test(r)&&n.reverse()),this.pushStack(n)}});var P=/[^\x20\t\r\n\f]+/g;function R(e){return e}function M(e){throw e}function I(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}S.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},S.each(e.match(P)||[],function(e,t){n[t]=!0}),n):S.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:"")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){S.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return S.each(arguments,function(e,t){var n;while(-1<(n=S.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<S.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t="",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},S.extend({Deferred:function(e){var o=[["notify","progress",S.Callbacks("memory"),S.Callbacks("memory"),2],["resolve","done",S.Callbacks("once memory"),S.Callbacks("once memory"),0,"resolved"],["reject","fail",S.Callbacks("once memory"),S.Callbacks("once memory"),1,"rejected"]],i="pending",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},"catch":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return S.Deferred(function(r){S.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+"With"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError("Thenable self-resolution");t=e&&("object"==typeof e||"function"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,R,s),l(u,o,M,s)):(u++,t.call(e,l(u,o,R,s),l(u,o,M,s),l(u,o,R,o.notifyWith))):(a!==R&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){S.Deferred.exceptionHook&&S.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==M&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(S.Deferred.getStackHook&&(t.stackTrace=S.Deferred.getStackHook()),C.setTimeout(t))}}return S.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:R,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:R)),o[2][3].add(l(0,e,m(n)?n:M))}).promise()},promise:function(e){return null!=e?S.extend(e,a):a}},s={};return S.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+"With"](this===s?void 0:this,arguments),this},s[t[0]+"With"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=S.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(I(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)I(i[t],a(t),o.reject);return o.promise()}});var W=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;S.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&W.test(e.name)&&C.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},S.readyException=function(e){C.setTimeout(function(){throw e})};var F=S.Deferred();function B(){E.removeEventListener("DOMContentLoaded",B),C.removeEventListener("load",B),S.ready()}S.fn.ready=function(e){return F.then(e)["catch"](function(e){S.readyException(e)}),this},S.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--S.readyWait:S.isReady)||(S.isReady=!0)!==e&&0<--S.readyWait||F.resolveWith(E,[S])}}),S.ready.then=F.then,"complete"===E.readyState||"loading"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(S.ready):(E.addEventListener("DOMContentLoaded",B),C.addEventListener("load",B));var $=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===w(n))for(s in i=!0,n)$(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(S(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},_=/^-ms-/,z=/-([a-z])/g;function U(e,t){return t.toUpperCase()}function X(e){return e.replace(_,"ms-").replace(z,U)}var V=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function G(){this.expando=S.expando+G.uid++}G.uid=1,G.prototype={cache:function(e){var t=e[this.expando];return t||(t={},V(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[X(t)]=n;else for(r in t)i[X(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][X(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(X):(t=X(t))in r?[t]:t.match(P)||[]).length;while(n--)delete r[t[n]]}(void 0===t||S.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!S.isEmptyObject(t)}};var Y=new G,Q=new G,J=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,K=/[A-Z]/g;function Z(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(K,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:J.test(i)?JSON.parse(i):i)}catch(e){}Q.set(e,t,n)}else n=void 0;return n}S.extend({hasData:function(e){return Q.hasData(e)||Y.hasData(e)},data:function(e,t,n){return Q.access(e,t,n)},removeData:function(e,t){Q.remove(e,t)},_data:function(e,t,n){return Y.access(e,t,n)},_removeData:function(e,t){Y.remove(e,t)}}),S.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=Q.get(o),1===o.nodeType&&!Y.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=X(r.slice(5)),Z(o,r,i[r]));Y.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){Q.set(this,n)}):$(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=Q.get(o,n))?t:void 0!==(t=Z(o,n))?t:void 0;this.each(function(){Q.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){Q.remove(this,e)})}}),S.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Y.get(e,t),n&&(!r||Array.isArray(n)?r=Y.access(e,t,S.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=S.queue(e,t),r=n.length,i=n.shift(),o=S._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){S.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Y.get(e,n)||Y.access(e,n,{empty:S.Callbacks("once memory").add(function(){Y.remove(e,[t+"queue",n])})})}}),S.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?S.queue(this[0],t):void 0===n?this:this.each(function(){var e=S.queue(this,t,n);S._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&S.dequeue(this,t)})},dequeue:function(e){return this.each(function(){S.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=S.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=Y.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var ee=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,te=new RegExp("^(?:([+-])=|)("+ee+")([a-z%]*)$","i"),ne=["Top","Right","Bottom","Left"],re=E.documentElement,ie=function(e){return S.contains(e.ownerDocument,e)},oe={composed:!0};re.getRootNode&&(ie=function(e){return S.contains(e.ownerDocument,e)||e.getRootNode(oe)===e.ownerDocument});var ae=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&ie(e)&&"none"===S.css(e,"display")};function se(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return S.css(e,t,"")},u=s(),l=n&&n[3]||(S.cssNumber[t]?"":"px"),c=e.nodeType&&(S.cssNumber[t]||"px"!==l&&+u)&&te.exec(S.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)S.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,S.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ue={};function le(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?("none"===n&&(l[c]=Y.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&ae(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ue[s])||(o=a.body.appendChild(a.createElement(s)),u=S.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),ue[s]=u)))):"none"!==n&&(l[c]="none",Y.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}S.fn.extend({show:function(){return le(this,!0)},hide:function(){return le(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?S(this).show():S(this).hide()})}});var ce,fe,pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="<textarea>x</textarea>",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="<option></option>",y.option=!!ce.lastChild;var ge={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,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n<r;n++)Y.set(e[n],"globalEval",!t||Y.get(t[n],"globalEval"))}ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td,y.option||(ge.optgroup=ge.option=[1,"<select multiple='multiple'>","</select>"]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===w(o))S.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement("div")),s=(de.exec(o)||["",""])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+S.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;S.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&-1<S.inArray(o,r))i&&i.push(o);else if(l=ie(o),a=ve(f.appendChild(o),"script"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}var be=/^key/,we=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Te=/^([^.]*)(?:\.(.+)|)/;function Ce(){return!0}function Ee(){return!1}function Se(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function ke(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)ke(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Ee;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return S().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=S.guid++)),e.each(function(){S.event.add(this,t,i,r,n)})}function Ae(e,i,o){o?(Y.set(e,i,!1),S.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Y.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(S.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Y.set(this,i,r),t=o(this,i),this[i](),r!==(n=Y.get(this,i))||t?Y.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Y.set(this,i,{value:S.event.trigger(S.extend(r[0],S.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Y.get(e,i)&&S.event.add(e,i,Ce)}S.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.get(t);if(V(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&S.find.matchesSelector(re,i),n.guid||(n.guid=S.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof S&&S.event.triggered!==e.type?S.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(P)||[""]).length;while(l--)d=g=(s=Te.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=S.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=S.event.special[d]||{},c=S.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&S.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),S.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.hasData(e)&&Y.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(P)||[""]).length;while(l--)if(d=g=(s=Te.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=S.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||S.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)S.event.remove(e,d+t[l],n,r,!0);S.isEmptyObject(u)&&Y.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=S.event.fix(e),l=(Y.get(this,"events")||Object.create(null))[u.type]||[],c=S.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=S.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((S.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?-1<S(i,this).index(l):S.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(S.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[S.expando]?e:new S.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click",Ce),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,"input")&&Y.get(t,"click")||A(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},S.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},S.Event=function(e,t){if(!(this instanceof S.Event))return new S.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?Ce:Ee,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&S.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[S.expando]=!0},S.Event.prototype={constructor:S.Event,isDefaultPrevented:Ee,isPropagationStopped:Ee,isImmediatePropagationStopped:Ee,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=Ce,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=Ce,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=Ce,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},S.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&be.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&we.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},S.event.addProp),S.each({focus:"focusin",blur:"focusout"},function(e,t){S.event.special[e]={setup:function(){return Ae(this,e,Se),!1},trigger:function(){return Ae(this,e),!0},delegateType:t}}),S.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){S.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||S.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),S.fn.extend({on:function(e,t,n,r){return ke(this,e,t,n,r)},one:function(e,t,n,r){return ke(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,S(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=Ee),this.each(function(){S.event.remove(this,e,n,t)})}});var Ne=/<script|<style|<link/i,De=/checked\s*(?:[^=]|=\s*.checked.)/i,je=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n<r;n++)S.event.add(t,i,s[i][n]);Q.hasData(e)&&(o=Q.access(e),a=S.extend({},o),Q.set(t,a))}}function Pe(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&"string"==typeof d&&!y.checkClone&&De.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),Pe(t,r,i,o)});if(f&&(t=(e=xe(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=S.map(ve(e,"script"),Le)).length;c<f;c++)u=e,c!==p&&(u=S.clone(u,!0,!0),s&&S.merge(a,ve(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,S.map(a,He),c=0;c<s;c++)u=a[c],he.test(u.type||"")&&!Y.access(u,"globalEval")&&S.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?S._evalUrl&&!u.noModule&&S._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")},l):b(u.textContent.replace(je,""),u,l))}return n}function Re(e,t,n){for(var r,i=t?S.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||S.cleanData(ve(r)),r.parentNode&&(n&&ie(r)&&ye(ve(r,"script")),r.parentNode.removeChild(r));return e}S.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ie(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||S.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ve(e),a=a||ve(c),r=0,i=o.length;r<i;r++)Oe(o[r],a[r]);else Oe(e,c);return 0<(a=ve(c,"script")).length&&ye(a,!f&&ve(e,"script")),c},cleanData:function(e){for(var t,n,r,i=S.event.special,o=0;void 0!==(n=e[o]);o++)if(V(n)){if(t=n[Y.expando]){if(t.events)for(r in t.events)i[r]?S.event.remove(n,r):S.removeEvent(n,r,t.handle);n[Y.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),S.fn.extend({detach:function(e){return Re(this,e,!0)},remove:function(e){return Re(this,e)},text:function(e){return $(this,function(e){return void 0===e?S.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Pe(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||qe(this,e).appendChild(e)})},prepend:function(){return Pe(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=qe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(S.cleanData(ve(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return S.clone(this,e,t)})},html:function(e){return $(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ne.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=S.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(S.cleanData(ve(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return Pe(this,arguments,function(e){var t=this.parentNode;S.inArray(this,n)<0&&(S.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),S.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){S.fn[e]=function(e){for(var t,n=[],r=S(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),S(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var Me=new RegExp("^("+ee+")(?!px)[a-z%]+$","i"),Ie=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},We=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},Fe=new RegExp(ne.join("|"),"i");function Be(e,t,n){var r,i,o,a,s=e.style;return(n=n||Ie(e))&&(""!==(a=n.getPropertyValue(t)||n[t])||ie(e)||(a=S.style(e,t)),!y.pixelBoxStyles()&&Me.test(a)&&Fe.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function $e(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",l.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",re.appendChild(u).appendChild(l);var e=C.getComputedStyle(l);n="1%"!==e.top,s=12===t(e.marginLeft),l.style.right="60%",o=36===t(e.right),r=36===t(e.width),l.style.position="absolute",i=12===t(l.offsetWidth/3),re.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=E.createElement("div"),l=E.createElement("div");l.style&&(l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",y.clearCloneStyle="content-box"===l.style.backgroundClip,S.extend(y,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=E.createElement("table"),t=E.createElement("tr"),n=E.createElement("div"),e.style.cssText="position:absolute;left:-11111px",t.style.height="1px",n.style.height="9px",re.appendChild(e).appendChild(t).appendChild(n),r=C.getComputedStyle(t),a=3<parseInt(r.height),re.removeChild(e)),a}}))}();var _e=["Webkit","Moz","ms"],ze=E.createElement("div").style,Ue={};function Xe(e){var t=S.cssProps[e]||Ue[e];return t||(e in ze?e:Ue[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=_e.length;while(n--)if((e=_e[n]+t)in ze)return e}(e)||e)}var Ve=/^(none|table(?!-c[ea]).+)/,Ge=/^--/,Ye={position:"absolute",visibility:"hidden",display:"block"},Qe={letterSpacing:"0",fontWeight:"400"};function Je(e,t,n){var r=te.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function Ke(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=S.css(e,n+ne[a],!0,i)),r?("content"===n&&(u-=S.css(e,"padding"+ne[a],!0,i)),"margin"!==n&&(u-=S.css(e,"border"+ne[a]+"Width",!0,i))):(u+=S.css(e,"padding"+ne[a],!0,i),"padding"!==n?u+=S.css(e,"border"+ne[a]+"Width",!0,i):s+=S.css(e,"border"+ne[a]+"Width",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function Ze(e,t,n){var r=Ie(e),i=(!y.boxSizingReliable()||n)&&"border-box"===S.css(e,"boxSizing",!1,r),o=i,a=Be(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(Me.test(a)){if(!n)return a;a="auto"}return(!y.boxSizingReliable()&&i||!y.reliableTrDimensions()&&A(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===S.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===S.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Ke(e,t,n||(i?"border":"content"),o,r,a)+"px"}function et(e,t,n,r,i){return new et.prototype.init(e,t,n,r,i)}S.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Be(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=X(t),u=Ge.test(t),l=e.style;if(u||(t=Xe(s)),a=S.cssHooks[t]||S.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=te.exec(n))&&i[1]&&(n=se(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(S.cssNumber[s]?"":"px")),y.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=X(t);return Ge.test(t)||(t=Xe(s)),(a=S.cssHooks[t]||S.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Be(e,t,r)),"normal"===i&&t in Qe&&(i=Qe[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),S.each(["height","width"],function(e,u){S.cssHooks[u]={get:function(e,t,n){if(t)return!Ve.test(S.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?Ze(e,u,n):We(e,Ye,function(){return Ze(e,u,n)})},set:function(e,t,n){var r,i=Ie(e),o=!y.scrollboxSize()&&"absolute"===i.position,a=(o||n)&&"border-box"===S.css(e,"boxSizing",!1,i),s=n?Ke(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e["offset"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-Ke(e,u,"border",!1,i)-.5)),s&&(r=te.exec(t))&&"px"!==(r[3]||"px")&&(e.style[u]=t,t=S.css(e,u)),Je(0,t,s)}}}),S.cssHooks.marginLeft=$e(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Be(e,"marginLeft"))||e.getBoundingClientRect().left-We(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),S.each({margin:"",padding:"",border:"Width"},function(i,o){S.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r="string"==typeof e?e.split(" "):[e];t<4;t++)n[i+ne[t]+o]=r[t]||r[t-2]||r[0];return n}},"margin"!==i&&(S.cssHooks[i+o].set=Je)}),S.fn.extend({css:function(e,t){return $(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Ie(e),i=t.length;a<i;a++)o[t[a]]=S.css(e,t[a],!1,r);return o}return void 0!==n?S.style(e,t,n):S.css(e,t)},e,t,1<arguments.length)}}),((S.Tween=et).prototype={constructor:et,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||S.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(S.cssNumber[n]?"":"px")},cur:function(){var e=et.propHooks[this.prop];return e&&e.get?e.get(this):et.propHooks._default.get(this)},run:function(e){var t,n=et.propHooks[this.prop];return this.options.duration?this.pos=t=S.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):et.propHooks._default.set(this),this}}).init.prototype=et.prototype,(et.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=S.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){S.fx.step[e.prop]?S.fx.step[e.prop](e):1!==e.elem.nodeType||!S.cssHooks[e.prop]&&null==e.elem.style[Xe(e.prop)]?e.elem[e.prop]=e.now:S.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=et.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},S.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},S.fx=et.prototype.init,S.fx.step={};var tt,nt,rt,it,ot=/^(?:toggle|show|hide)$/,at=/queueHooks$/;function st(){nt&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(st):C.setTimeout(st,S.fx.interval),S.fx.tick())}function ut(){return C.setTimeout(function(){tt=void 0}),tt=Date.now()}function lt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=ne[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function ct(e,t,n){for(var r,i=(ft.tweeners[t]||[]).concat(ft.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function ft(o,e,t){var n,a,r=0,i=ft.prefilters.length,s=S.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=tt||ut(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:S.extend({},e),opts:S.extend(!0,{specialEasing:{},easing:S.easing._default},t),originalProperties:e,originalOptions:t,startTime:tt||ut(),duration:t.duration,tweens:[],createTween:function(e,t){var n=S.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=X(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=S.cssHooks[r])&&"expand"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=ft.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(S._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return S.map(c,ct,l),m(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),S.fx.timer(S.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}S.Animation=S.extend(ft,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return se(n.elem,e,te.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=["*"]):e=e.match(P);for(var n,r=0,i=e.length;r<i;r++)n=e[r],ft.tweeners[n]=ft.tweeners[n]||[],ft.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&ae(e),v=Y.get(e,"fxshow");for(r in n.queue||(null==(a=S._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,S.queue(e,"fx").length||a.empty.fire()})})),t)if(i=t[r],ot.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||S.style(e,r)}if((u=!S.isEmptyObject(t))||!S.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=Y.get(e,"display")),"none"===(c=S.css(e,"display"))&&(l?c=l:(le([e],!0),l=e.style.display||l,c=S.css(e,"display"),le([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===S.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?"hidden"in v&&(g=v.hidden):v=Y.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&le([e],!0),p.done(function(){for(r in g||le([e]),Y.remove(e,"fxshow"),d)S.style(e,r,d[r])})),u=ct(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?ft.prefilters.unshift(e):ft.prefilters.push(e)}}),S.speed=function(e,t,n){var r=e&&"object"==typeof e?S.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return S.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in S.fx.speeds?r.duration=S.fx.speeds[r.duration]:r.duration=S.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){m(r.old)&&r.old.call(this),r.queue&&S.dequeue(this,r.queue)},r},S.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ae).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=S.isEmptyObject(t),o=S.speed(e,n,r),a=function(){var e=ft(this,S.extend({},t),o);(i||Y.get(this,"finish"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return"string"!=typeof i&&(o=e,e=i,i=void 0),e&&this.queue(i||"fx",[]),this.each(function(){var e=!0,t=null!=i&&i+"queueHooks",n=S.timers,r=Y.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&at.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||S.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||"fx"),this.each(function(){var e,t=Y.get(this),n=t[a+"queue"],r=t[a+"queueHooks"],i=S.timers,o=n?n.length:0;for(t.finish=!0,S.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),S.each(["toggle","show","hide"],function(e,r){var i=S.fn[r];S.fn[r]=function(e,t,n){return null==e||"boolean"==typeof e?i.apply(this,arguments):this.animate(lt(r,!0),e,t,n)}}),S.each({slideDown:lt("show"),slideUp:lt("hide"),slideToggle:lt("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,r){S.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),S.timers=[],S.fx.tick=function(){var e,t=0,n=S.timers;for(tt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||S.fx.stop(),tt=void 0},S.fx.timer=function(e){S.timers.push(e),S.fx.start()},S.fx.interval=13,S.fx.start=function(){nt||(nt=!0,st())},S.fx.stop=function(){nt=null},S.fx.speeds={slow:600,fast:200,_default:400},S.fn.delay=function(r,e){return r=S.fx&&S.fx.speeds[r]||r,e=e||"fx",this.queue(e,function(e,t){var n=C.setTimeout(e,r);t.stop=function(){C.clearTimeout(n)}})},rt=E.createElement("input"),it=E.createElement("select").appendChild(E.createElement("option")),rt.type="checkbox",y.checkOn=""!==rt.value,y.optSelected=it.selected,(rt=E.createElement("input")).value="t",rt.type="radio",y.radioValue="t"===rt.value;var pt,dt=S.expr.attrHandle;S.fn.extend({attr:function(e,t){return $(this,S.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){S.removeAttr(this,e)})}}),S.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?S.prop(e,t,n):(1===o&&S.isXMLDoc(e)||(i=S.attrHooks[t.toLowerCase()]||(S.expr.match.bool.test(t)?pt:void 0)),void 0!==n?null===n?void S.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=S.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&"radio"===t&&A(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(P);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),pt={set:function(e,t,n){return!1===t?S.removeAttr(e,n):e.setAttribute(n,n),n}},S.each(S.expr.match.bool.source.match(/\w+/g),function(e,t){var a=dt[t]||S.find.attr;dt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=dt[o],dt[o]=r,r=null!=a(e,t,n)?o:null,dt[o]=i),r}});var ht=/^(?:input|select|textarea|button)$/i,gt=/^(?:a|area)$/i;function vt(e){return(e.match(P)||[]).join(" ")}function yt(e){return e.getAttribute&&e.getAttribute("class")||""}function mt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(P)||[]}S.fn.extend({prop:function(e,t){return $(this,S.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[S.propFix[e]||e]})}}),S.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&S.isXMLDoc(e)||(t=S.propFix[t]||t,i=S.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=S.find.attr(e,"tabindex");return t?parseInt(t,10):ht.test(e.nodeName)||gt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),y.optSelected||(S.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),S.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){S.propFix[this.toLowerCase()]=this}),S.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).addClass(t.call(this,e,yt(this)))});if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).removeClass(t.call(this,e,yt(this)))});if(!arguments.length)return this.attr("class","");if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])while(-1<r.indexOf(" "+o+" "))r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(i,t){var o=typeof i,a="string"===o||Array.isArray(i);return"boolean"==typeof t&&a?t?this.addClass(i):this.removeClass(i):m(i)?this.each(function(e){S(this).toggleClass(i.call(this,e,yt(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=S(this),r=mt(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&"boolean"!==o||((e=yt(this))&&Y.set(this,"__className__",e),this.setAttribute&&this.setAttribute("class",e||!1===i?"":Y.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&-1<(" "+vt(yt(n))+" ").indexOf(t))return!0;return!1}});var xt=/\r/g;S.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=m(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,S(this).val()):n)?t="":"number"==typeof t?t+="":Array.isArray(t)&&(t=S.map(t,function(e){return null==e?"":e+""})),(r=S.valHooks[this.type]||S.valHooks[this.nodeName.toLowerCase()])&&"set"in r&&void 0!==r.set(this,t,"value")||(this.value=t))})):t?(r=S.valHooks[t.type]||S.valHooks[t.nodeName.toLowerCase()])&&"get"in r&&void 0!==(e=r.get(t,"value"))?e:"string"==typeof(e=t.value)?e.replace(xt,""):null==e?"":e:void 0}}),S.extend({valHooks:{option:{get:function(e){var t=S.find.attr(e,"value");return null!=t?t:vt(S.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!A(n.parentNode,"optgroup"))){if(t=S(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=S.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<S.inArray(S.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),S.each(["radio","checkbox"],function(){S.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<S.inArray(S(e).val(),t)}},y.checkOn||(S.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),y.focusin="onfocusin"in C;var bt=/^(?:focusinfocus|focusoutblur)$/,wt=function(e){e.stopPropagation()};S.extend(S.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||E],d=v.call(e,"type")?e.type:e,h=v.call(e,"namespace")?e.namespace.split("."):[];if(o=f=a=n=n||E,3!==n.nodeType&&8!==n.nodeType&&!bt.test(d+S.event.triggered)&&(-1<d.indexOf(".")&&(d=(h=d.split(".")).shift(),h.sort()),u=d.indexOf(":")<0&&"on"+d,(e=e[S.expando]?e:new S.Event(d,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:S.makeArray(t,[e]),c=S.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,bt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||E)&&p.push(a.defaultView||a.parentWindow||C)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(Y.get(o,"events")||Object.create(null))[e.type]&&Y.get(o,"handle"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&V(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!V(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),S.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,wt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,wt),S.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=S.extend(new S.Event,n,{type:e,isSimulated:!0});S.event.trigger(r,null,t)}}),S.fn.extend({trigger:function(e,t){return this.each(function(){S.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return S.event.trigger(e,t,n,!0)}}),y.focusin||S.each({focus:"focusin",blur:"focusout"},function(n,r){var i=function(e){S.event.simulate(r,e.target,S.event.fix(e))};S.event.special[r]={setup:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r);t||e.addEventListener(n,i,!0),Y.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r)-1;t?Y.access(e,r,t):(e.removeEventListener(n,i,!0),Y.remove(e,r))}}});var Tt=C.location,Ct={guid:Date.now()},Et=/\?/;S.parseXML=function(e){var t;if(!e||"string"!=typeof e)return null;try{t=(new C.DOMParser).parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||S.error("Invalid XML: "+e),t};var St=/\[\]$/,kt=/\r?\n/g,At=/^(?:submit|button|image|reset|file)$/i,Nt=/^(?:input|select|textarea|keygen)/i;function Dt(n,e,r,i){var t;if(Array.isArray(e))S.each(e,function(e,t){r||St.test(n)?i(n,t):Dt(n+"["+("object"==typeof t&&null!=t?e:"")+"]",t,r,i)});else if(r||"object"!==w(e))i(n,e);else for(t in e)Dt(n+"["+t+"]",e[t],r,i)}S.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!S.isPlainObject(e))S.each(e,function(){i(this.name,this.value)});else for(n in e)Dt(n,e[n],t,i);return r.join("&")},S.fn.extend({serialize:function(){return S.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=S.prop(this,"elements");return e?S.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!S(this).is(":disabled")&&Nt.test(this.nodeName)&&!At.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=S(this).val();return null==n?null:Array.isArray(n)?S.map(n,function(e){return{name:t.name,value:e.replace(kt,"\r\n")}}):{name:t.name,value:n.replace(kt,"\r\n")}}).get()}});var jt=/%20/g,qt=/#.*$/,Lt=/([?&])_=[^&]*/,Ht=/^(.*?):[ \t]*([^\r\n]*)$/gm,Ot=/^(?:GET|HEAD)$/,Pt=/^\/\//,Rt={},Mt={},It="*/".concat("*"),Wt=E.createElement("a");function Ft(o){return function(e,t){"string"!=typeof e&&(t=e,e="*");var n,r=0,i=e.toLowerCase().match(P)||[];if(m(t))while(n=i[r++])"+"===n[0]?(n=n.slice(1)||"*",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function Bt(t,i,o,a){var s={},u=t===Mt;function l(e){var r;return s[e]=!0,S.each(t[e]||[],function(e,t){var n=t(i,o,a);return"string"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s["*"]&&l("*")}function $t(e,t){var n,r,i=S.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&S.extend(!0,e,r),e}Wt.href=Tt.href,S.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Tt.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Tt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":It,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":S.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?$t($t(e,S.ajaxSettings),t):$t(S.ajaxSettings,e)},ajaxPrefilter:Ft(Rt),ajaxTransport:Ft(Mt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=S.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?S(y):S.event,x=S.Deferred(),b=S.Callbacks("once memory"),w=v.statusCode||{},a={},s={},u="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=Ht.exec(p))n[t[1].toLowerCase()+" "]=(n[t[1].toLowerCase()+" "]||[]).concat(t[2])}t=n[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||Tt.href)+"").replace(Pt,Tt.protocol+"//"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||"*").toLowerCase().match(P)||[""],null==v.crossDomain){r=E.createElement("a");try{r.href=v.url,r.href=r.href,v.crossDomain=Wt.protocol+"//"+Wt.host!=r.protocol+"//"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&"string"!=typeof v.data&&(v.data=S.param(v.data,v.traditional)),Bt(Rt,v,t,T),h)return T;for(i in(g=S.event&&v.global)&&0==S.active++&&S.event.trigger("ajaxStart"),v.type=v.type.toUpperCase(),v.hasContent=!Ot.test(v.type),f=v.url.replace(qt,""),v.hasContent?v.data&&v.processData&&0===(v.contentType||"").indexOf("application/x-www-form-urlencoded")&&(v.data=v.data.replace(jt,"+")):(o=v.url.slice(f.length),v.data&&(v.processData||"string"==typeof v.data)&&(f+=(Et.test(f)?"&":"?")+v.data,delete v.data),!1===v.cache&&(f=f.replace(Lt,"$1"),o=(Et.test(f)?"&":"?")+"_="+Ct.guid+++o),v.url=f+o),v.ifModified&&(S.lastModified[f]&&T.setRequestHeader("If-Modified-Since",S.lastModified[f]),S.etag[f]&&T.setRequestHeader("If-None-Match",S.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader("Content-Type",v.contentType),T.setRequestHeader("Accept",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+("*"!==v.dataTypes[0]?", "+It+"; q=0.01":""):v.accepts["*"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u="abort",b.add(v.complete),T.done(v.success),T.fail(v.error),c=Bt(Mt,v,t,T)){if(T.readyState=1,g&&m.trigger("ajaxSend",[T,v]),h)return T;v.async&&0<v.timeout&&(d=C.setTimeout(function(){T.abort("timeout")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,"No Transport");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&C.clearTimeout(d),c=void 0,p=r||"",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(v,T,n)),!i&&-1<S.inArray("script",v.dataTypes)&&(v.converters["text script"]=function(){}),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader("Last-Modified"))&&(S.lastModified[f]=u),(u=T.getResponseHeader("etag"))&&(S.etag[f]=u)),204===e||"HEAD"===v.type?l="nocontent":304===e?l="notmodified":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l="error",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+"",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?"ajaxSuccess":"ajaxError",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger("ajaxComplete",[T,v]),--S.active||S.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return S.get(e,t,n,"json")},getScript:function(e,t){return S.get(e,void 0,t,"script")}}),S.each(["get","post"],function(e,i){S[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),S.ajax(S.extend({url:e,type:i,dataType:r,data:t,success:n},S.isPlainObject(e)&&e))}}),S.ajaxPrefilter(function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")}),S._evalUrl=function(e,t,n){return S.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){S.globalEval(e,t,n)}})},S.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=S(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return m(n)?this.each(function(e){S(this).wrapInner(n.call(this,e))}):this.each(function(){var e=S(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){S(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not("body").each(function(){S(this).replaceWith(this.childNodes)}),this}}),S.expr.pseudos.hidden=function(e){return!S.expr.pseudos.visible(e)},S.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},S.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var _t={0:200,1223:204},zt=S.ajaxSettings.xhr();y.cors=!!zt&&"withCredentials"in zt,y.ajax=zt=!!zt,S.ajaxTransport(function(i){var o,a;if(y.cors||zt&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,"abort"===e?r.abort():"error"===e?"number"!=typeof r.status?t(0,"error"):t(r.status,r.statusText):t(_t[r.status]||r.status,r.statusText,"text"!==(r.responseType||"text")||"string"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o("error"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&C.setTimeout(function(){o&&a()})},o=o("abort");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),S.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),S.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return S.globalEval(e),e}}}),S.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),S.ajaxTransport("script",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=S("<script>").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="<form></form><form></form>",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1<s&&(r=vt(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),0<a.length&&S.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?S("<div>").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var Gt=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;S.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),m(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||S.guid++,i},S.holdReady=function(e){e?S.readyWait++:S.ready(!0)},S.isArray=Array.isArray,S.parseJSON=JSON.parse,S.nodeName=A,S.isFunction=m,S.isWindow=x,S.camelCase=X,S.type=w,S.now=Date.now,S.isNumeric=function(e){var t=S.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},S.trim=function(e){return null==e?"":(e+"").replace(Gt,"")},"function"==typeof define&&define.amd&&define("jquery",[],function(){return S});var Yt=C.jQuery,Qt=C.$;return S.noConflict=function(e){return C.$===S&&(C.$=Qt),e&&C.jQuery===S&&(C.jQuery=Yt),S},"undefined"==typeof e&&(C.jQuery=C.$=S),S});
/* Javascript plotting library for jQuery, version 0.8.1.
Copyright (c) 2007-2013 IOLA and Ole Laursen.
Licensed under the MIT license.
*/// first an inline dependency, jquery.colorhelpers.js, we inline it here
// for convenience
/* Plugin for jQuery for working with colors.
*
* Version 1.1.
*
* Inspiration from jQuery color animation plugin by John Resig.
*
* Released under the MIT license by Ole Laursen, October 2009.
*
* Examples:
*
* $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString()
* var c = $.color.extract($("#mydiv"), 'background-color');
* console.log(c.r, c.g, c.b, c.a);
* $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)"
*
* Note that .scale() and .add() return the same modified object
* instead of making a new one.
*
* V. 1.1: Fix error handling so e.g. parsing an empty string does
* produce a color rather than just crashing.
*/(function(e){e.color={},e.color.make=function(t,n,r,i){var s={};return s.r=t||0,s.g=n||0,s.b=r||0,s.a=i!=null?i:1,s.add=function(e,t){for(var n=0;n<e.length;++n)s[e.charAt(n)]+=t;return s.normalize()},s.scale=function(e,t){for(var n=0;n<e.length;++n)s[e.charAt(n)]*=t;return s.normalize()},s.toString=function(){return s.a>=1?"rgb("+[s.r,s.g,s.b].join(",")+")":"rgba("+[s.r,s.g,s.b,s.a].join(",")+")"},s.normalize=function(){function e(e,t,n){return t<e?e:t>n?n:t}return s.r=e(0,parseInt(s.r),255),s.g=e(0,parseInt(s.g),255),s.b=e(0,parseInt(s.b),255),s.a=e(0,s.a,1),s},s.clone=function(){return e.color.make(s.r,s.b,s.g,s.a)},s.normalize()},e.color.extract=function(t,n){var r;do{r=t.css(n).toLowerCase();if(r!=""&&r!="transparent")break;t=t.parent()}while(!e.nodeName(t.get(0),"body"));return r=="rgba(0, 0, 0, 0)"&&(r="transparent"),e.color.parse(r)},e.color.parse=function(n){var r,i=e.color.make;if(r=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(n))return i(parseInt(r[1],10),parseInt(r[2],10),parseInt(r[3],10));if(r=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(n))return i(parseInt(r[1],10),parseInt(r[2],10),parseInt(r[3],10),parseFloat(r[4]));if(r=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(n))return i(parseFloat(r[1])*2.55,parseFloat(r[2])*2.55,parseFloat(r[3])*2.55);if(r=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(n))return i(parseFloat(r[1])*2.55,parseFloat(r[2])*2.55,parseFloat(r[3])*2.55,parseFloat(r[4]));if(r=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(n))return i(parseInt(r[1],16),parseInt(r[2],16),parseInt(r[3],16));if(r=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(n))return i(parseInt(r[1]+r[1],16),parseInt(r[2]+r[2],16),parseInt(r[3]+r[3],16));var s=e.trim(n).toLowerCase();return s=="transparent"?i(255,255,255,0):(r=t[s]||[0,0,0],i(r[0],r[1],r[2]))};var t={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery),function(e){function n(t,n){var r=n.children("."+t)[0];if(r==null){r=document.createElement("canvas"),r.className=t,e(r).css({direction:"ltr",position:"absolute",left:0,top:0}).appendTo(n);if(!r.getContext){if(!window.G_vmlCanvasManager)throw new Error("Canvas is not available. If you're using IE with a fall-back such as Excanvas, then there's either a mistake in your conditional include, or the page has no DOCTYPE and is rendering in Quirks Mode.");r=window.G_vmlCanvasManager.initElement(r)}}this.element=r;var i=this.context=r.getContext("2d"),s=window.devicePixelRatio||1,o=i.webkitBackingStorePixelRatio||i.mozBackingStorePixelRatio||i.msBackingStorePixelRatio||i.oBackingStorePixelRatio||i.backingStorePixelRatio||1;this.pixelRatio=s/o,this.resize(n.width(),n.height()),this.textContainer=null,this.text={},this._textCache={}}function r(t,r,s,o){function E(e,t){t=[w].concat(t);for(var n=0;n<e.length;++n)e[n].apply(this,t)}function S(){var t={Canvas:n};for(var r=0;r<o.length;++r){var i=o[r];i.init(w,t),i.options&&e.extend(!0,a,i.options)}}function x(n){e.extend(!0,a,n),n&&n.colors&&(a.colors=n.colors),a.xaxis.color==null&&(a.xaxis.color=e.color.parse(a.grid.color).scale("a",.22).toString()),a.yaxis.color==null&&(a.yaxis.color=e.color.parse(a.grid.color).scale("a",.22).toString()),a.xaxis.tickColor==null&&(a.xaxis.tickColor=a.grid.tickColor||a.xaxis.color),a.yaxis.tickColor==null&&(a.yaxis.tickColor=a.grid.tickColor||a.yaxis.color),a.grid.borderColor==null&&(a.grid.borderColor=a.grid.color),a.grid.tickColor==null&&(a.grid.tickColor=e.color.parse(a.grid.color).scale("a",.22).toString());var r,i,s,o={style:t.css("font-style"),size:Math.round(.8*(+t.css("font-size").replace("px","")||13)),variant:t.css("font-variant"),weight:t.css("font-weight"),family:t.css("font-family")};o.lineHeight=o.size*1.15,s=a.xaxes.length||1;for(r=0;r<s;++r)i=a.xaxes[r],i&&!i.tickColor&&(i.tickColor=i.color),i=e.extend(!0,{},a.xaxis,i),a.xaxes[r]=i,i.font&&(i.font=e.extend({},o,i.font),i.font.color||(i.font.color=i.color));s=a.yaxes.length||1;for(r=0;r<s;++r)i=a.yaxes[r],i&&!i.tickColor&&(i.tickColor=i.color),i=e.extend(!0,{},a.yaxis,i),a.yaxes[r]=i,i.font&&(i.font=e.extend({},o,i.font),i.font.color||(i.font.color=i.color));a.xaxis.noTicks&&a.xaxis.ticks==null&&(a.xaxis.ticks=a.xaxis.noTicks),a.yaxis.noTicks&&a.yaxis.ticks==null&&(a.yaxis.ticks=a.yaxis.noTicks),a.x2axis&&(a.xaxes[1]=e.extend(!0,{},a.xaxis,a.x2axis),a.xaxes[1].position="top"),a.y2axis&&(a.yaxes[1]=e.extend(!0,{},a.yaxis,a.y2axis),a.yaxes[1].position="right"),a.grid.coloredAreas&&(a.grid.markings=a.grid.coloredAreas),a.grid.coloredAreasColor&&(a.grid.markingsColor=a.grid.coloredAreasColor),a.lines&&e.extend(!0,a.series.lines,a.lines),a.points&&e.extend(!0,a.series.points,a.points),a.bars&&e.extend(!0,a.series.bars,a.bars),a.shadowSize!=null&&(a.series.shadowSize=a.shadowSize),a.highlightColor!=null&&(a.series.highlightColor=a.highlightColor);for(r=0;r<a.xaxes.length;++r)O(d,r+1).options=a.xaxes[r];for(r=0;r<a.yaxes.length;++r)O(v,r+1).options=a.yaxes[r];for(var u in b)a.hooks[u]&&a.hooks[u].length&&(b[u]=b[u].concat(a.hooks[u]));E(b.processOptions,[a])}function T(e){u=N(e),M(),_()}function N(t){var n=[];for(var r=0;r<t.length;++r){var i=e.extend(!0,{},a.series);t[r].data!=null?(i.data=t[r].data,delete t[r].data,e.extend(!0,i,t[r]),t[r].data=i.data):i.data=t[r],n.push(i)}return n}function C(e,t){var n=e[t+"axis"];return typeof n=="object"&&(n=n.n),typeof n!="number"&&(n=1),n}function k(){return e.grep(d.concat(v),function(e){return e})}function L(e){var t={},n,r;for(n=0;n<d.length;++n)r=d[n],r&&r.used&&(t["x"+r.n]=r.c2p(e.left));for(n=0;n<v.length;++n)r=v[n],r&&r.used&&(t["y"+r.n]=r.c2p(e.top));return t.x1!==undefined&&(t.x=t.x1),t.y1!==undefined&&(t.y=t.y1),t}function A(e){var t={},n,r,i;for(n=0;n<d.length;++n){r=d[n];if(r&&r.used){i="x"+r.n,e[i]==null&&r.n==1&&(i="x");if(e[i]!=null){t.left=r.p2c(e[i]);break}}}for(n=0;n<v.length;++n){r=v[n];if(r&&r.used){i="y"+r.n,e[i]==null&&r.n==1&&(i="y");if(e[i]!=null){t.top=r.p2c(e[i]);break}}}return t}function O(t,n){return t[n-1]||(t[n-1]={n:n,direction:t==d?"x":"y",options:e.extend(!0,{},t==d?a.xaxis:a.yaxis)}),t[n-1]}function M(){var t=u.length,n=-1,r;for(r=0;r<u.length;++r){var i=u[r].color;i!=null&&(t--,typeof i=="number"&&i>n&&(n=i))}t<=n&&(t=n+1);var s,o=[],f=a.colors,l=f.length,c=0;for(r=0;r<t;r++)s=e.color.parse(f[r%l]||"#666"),r%l==0&&r&&(c>=0?c<.5?c=-c-.2:c=0:c=-c),o[r]=s.scale("rgb",1+c);var h=0,p;for(r=0;r<u.length;++r){p=u[r],p.color==null?(p.color=o[h].toString(),++h):typeof p.color=="number"&&(p.color=o[p.color].toString());if(p.lines.show==null){var m,g=!0;for(m in p)if(p[m]&&p[m].show){g=!1;break}g&&(p.lines.show=!0)}p.lines.zero==null&&(p.lines.zero=!!p.lines.fill),p.xaxis=O(d,C(p,"x")),p.yaxis=O(v,C(p,"y"))}}function _(){function x(e,t,n){t<e.datamin&&t!=-r&&(e.datamin=t),n>e.datamax&&n!=r&&(e.datamax=n)}var t=Number.POSITIVE_INFINITY,n=Number.NEGATIVE_INFINITY,r=Number.MAX_VALUE,i,s,o,a,f,l,c,h,p,d,v,m,g,y,w,S;e.each(k(),function(e,r){r.datamin=t,r.datamax=n,r.used=!1});for(i=0;i<u.length;++i)l=u[i],l.datapoints={points:[]},E(b.processRawData,[l,l.data,l.datapoints]);for(i=0;i<u.length;++i){l=u[i],w=l.data,S=l.datapoints.format;if(!S){S=[],S.push({x:!0,number:!0,required:!0}),S.push({y:!0,number:!0,required:!0});if(l.bars.show||l.lines.show&&l.lines.fill){var T=!!(l.bars.show&&l.bars.zero||l.lines.show&&l.lines.zero);S.push({y:!0,number:!0,required:!1,defaultValue:0,autoscale:T}),l.bars.horizontal&&(delete S[S.length-1].y,S[S.length-1].x=!0)}l.datapoints.format=S}if(l.datapoints.pointsize!=null)continue;l.datapoints.pointsize=S.length,h=l.datapoints.pointsize,c=l.datapoints.points;var N=l.lines.show&&l.lines.steps;l.xaxis.used=l.yaxis.used=!0;for(s=o=0;s<w.length;++s,o+=h){y=w[s];var C=y==null;if(!C)for(a=0;a<h;++a)m=y[a],g=S[a],g&&(g.number&&m!=null&&(m=+m,isNaN(m)?m=null:m==Infinity?m=r:m==-Infinity&&(m=-r)),m==null&&(g.required&&(C=!0),g.defaultValue!=null&&(m=g.defaultValue))),c[o+a]=m;if(C)for(a=0;a<h;++a)m=c[o+a],m!=null&&(g=S[a],g.autoscale&&(g.x&&x(l.xaxis,m,m),g.y&&x(l.yaxis,m,m))),c[o+a]=null;else if(N&&o>0&&c[o-h]!=null&&c[o-h]!=c[o]&&c[o-h+1]!=c[o+1]){for(a=0;a<h;++a)c[o+h+a]=c[o+a];c[o+1]=c[o-h+1],o+=h}}}for(i=0;i<u.length;++i)l=u[i],E(b.processDatapoints,[l,l.datapoints]);for(i=0;i<u.length;++i){l=u[i],c=l.datapoints.points,h=l.datapoints.pointsize,S=l.datapoints.format;var L=t,A=t,O=n,M=n;for(s=0;s<c.length;s+=h){if(c[s]==null)continue;for(a=0;a<h;++a){m=c[s+a],g=S[a];if(!g||g.autoscale===!1||m==r||m==-r)continue;g.x&&(m<L&&(L=m),m>O&&(O=m)),g.y&&(m<A&&(A=m),m>M&&(M=m))}}if(l.bars.show){var _;switch(l.bars.align){case"left":_=0;break;case"right":_=-l.bars.barWidth;break;case"center":_=-l.bars.barWidth/2;break;default:throw new Error("Invalid bar alignment: "+l.bars.align)}l.bars.horizontal?(A+=_,M+=_+l.bars.barWidth):(L+=_,O+=_+l.bars.barWidth)}x(l.xaxis,L,O),x(l.yaxis,A,M)}e.each(k(),function(e,r){r.datamin==t&&(r.datamin=null),r.datamax==n&&(r.datamax=null)})}function D(){t.css("padding",0).children(":not(.flot-base,.flot-overlay)").remove(),t.css("position")=="static"&&t.css("position","relative"),f=new n("flot-base",t),l=new n("flot-overlay",t),h=f.context,p=l.context,c=e(l.element).unbind();var r=t.data("plot");r&&(r.shutdown(),l.clear()),t.data("plot",w)}function P(){a.grid.hoverable&&(c.mousemove(at),c.bind("mouseleave",ft)),a.grid.clickable&&c.click(lt),E(b.bindEvents,[c])}function H(){ot&&clearTimeout(ot),c.unbind("mousemove",at),c.unbind("mouseleave",ft),c.unbind("click",lt),E(b.shutdown,[c])}function B(e){function t(e){return e}var n,r,i=e.options.transform||t,s=e.options.inverseTransform;e.direction=="x"?(n=e.scale=g/Math.abs(i(e.max)-i(e.min)),r=Math.min(i(e.max),i(e.min))):(n=e.scale=y/Math.abs(i(e.max)-i(e.min)),n=-n,r=Math.max(i(e.max),i(e.min))),i==t?e.p2c=function(e){return(e-r)*n}:e.p2c=function(e){return(i(e)-r)*n},s?e.c2p=function(e){return s(r+e/n)}:e.c2p=function(e){return r+e/n}}function j(e){var t=e.options,n=e.ticks||[],r=t.labelWidth||0,i=t.labelHeight||0,s=r||e.direction=="x"?Math.floor(f.width/(n.length||1)):null;legacyStyles=e.direction+"Axis "+e.direction+e.n+"Axis",layer="flot-"+e.direction+"-axis flot-"+e.direction+e.n+"-axis "+legacyStyles,font=t.font||"flot-tick-label tickLabel";for(var o=0;o<n.length;++o){var u=n[o];if(!u.label)continue;var a=f.getTextInfo(layer,u.label,font,null,s);r=Math.max(r,a.width),i=Math.max(i,a.height)}e.labelWidth=t.labelWidth||r,e.labelHeight=t.labelHeight||i}function F(t){var n=t.labelWidth,r=t.labelHeight,i=t.options.position,s=t.options.tickLength,o=a.grid.axisMargin,u=a.grid.labelMargin,l=t.direction=="x"?d:v,c,h,p=e.grep(l,function(e){return e&&e.options.position==i&&e.reserveSpace});e.inArray(t,p)==p.length-1&&(o=0);if(s==null){var g=e.grep(l,function(e){return e&&e.reserveSpace});h=e.inArray(t,g)==0,h?s="full":s=5}isNaN(+s)||(u+=+s),t.direction=="x"?(r+=u,i=="bottom"?(m.bottom+=r+o,t.box={top:f.height-m.bottom,height:r}):(t.box={top:m.top+o,height:r},m.top+=r+o)):(n+=u,i=="left"?(t.box={left:m.left+o,width:n},m.left+=n+o):(m.right+=n+o,t.box={left:f.width-m.right,width:n})),t.position=i,t.tickLength=s,t.box.padding=u,t.innermost=h}function I(e){e.direction=="x"?(e.box.left=m.left-e.labelWidth/2,e.box.width=f.width-m.left-m.right+e.labelWidth):(e.box.top=m.top-e.labelHeight/2,e.box.height=f.height-m.bottom-m.top+e.labelHeight)}function q(){var t=a.grid.minBorderMargin,n={x:0,y:0},r,i;if(t==null){t=0;for(r=0;r<u.length;++r)t=Math.max(t,2*(u[r].points.radius+u[r].points.lineWidth/2))}n.x=n.y=Math.ceil(t),e.each(k(),function(e,t){var r=t.direction;t.reserveSpace&&(n[r]=Math.ceil(Math.max(n[r],(r=="x"?t.labelWidth:t.labelHeight)/2)))}),m.left=Math.max(n.x,m.left),m.right=Math.max(n.x,m.right),m.top=Math.max(n.y,m.top),m.bottom=Math.max(n.y,m.bottom)}function R(){var t,n=k(),r=a.grid.show;for(var i in m){var s=a.grid.margin||0;m[i]=typeof s=="number"?s:s[i]||0}E(b.processOffset,[m]);for(var i in m)typeof a.grid.borderWidth=="object"?m[i]+=r?a.grid.borderWidth[i]:0:m[i]+=r?a.grid.borderWidth:0;e.each(n,function(e,t){t.show=t.options.show,t.show==null&&(t.show=t.used),t.reserveSpace=t.show||t.options.reserveSpace,U(t)});if(r){var o=e.grep(n,function(e){return e.reserveSpace});e.each(o,function(e,t){z(t),W(t),X(t,t.ticks),j(t)});for(t=o.length-1;t>=0;--t)F(o[t]);q(),e.each(o,function(e,t){I(t)})}g=f.width-m.left-m.right,y=f.height-m.bottom-m.top,e.each(n,function(e,t){B(t)}),r&&G(),it()}function U(e){var t=e.options,n=+(t.min!=null?t.min:e.datamin),r=+(t.max!=null?t.max:e.datamax),i=r-n;if(i==0){var s=r==0?1:.01;t.min==null&&(n-=s);if(t.max==null||t.min!=null)r+=s}else{var o=t.autoscaleMargin;o!=null&&(t.min==null&&(n-=i*o,n<0&&e.datamin!=null&&e.datamin>=0&&(n=0)),t.max==null&&(r+=i*o,r>0&&e.datamax!=null&&e.datamax<=0&&(r=0)))}e.min=n,e.max=r}function z(t){var n=t.options,r;typeof n.ticks=="number"&&n.ticks>0?r=n.ticks:r=.3*Math.sqrt(t.direction=="x"?f.width:f.height);var s=(t.max-t.min)/r,o=-Math.floor(Math.log(s)/Math.LN10),u=n.tickDecimals;u!=null&&o>u&&(o=u);var a=Math.pow(10,-o),l=s/a,c;l<1.5?c=1:l<3?(c=2,l>2.25&&(u==null||o+1<=u)&&(c=2.5,++o)):l<7.5?c=5:c=10,c*=a,n.minTickSize!=null&&c<n.minTickSize&&(c=n.minTickSize),t.delta=s,t.tickDecimals=Math.max(0,u!=null?u:o),t.tickSize=n.tickSize||c;if(n.mode=="time"&&!t.tickGenerator)throw new Error("Time mode requires the flot.time plugin.");t.tickGenerator||(t.tickGenerator=function(e){var t=[],n=i(e.min,e.tickSize),r=0,s=Number.NaN,o;do o=s,s=n+r*e.tickSize,t.push(s),++r;while(s<e.max&&s!=o);return t},t.tickFormatter=function(e,t){var n=t.tickDecimals?Math.pow(10,t.tickDecimals):1,r=""+Math.round(e*n)/n;if(t.tickDecimals!=null){var i=r.indexOf("."),s=i==-1?0:r.length-i-1;if(s<t.tickDecimals)return(s?r:r+".")+(""+n).substr(1,t.tickDecimals-s)}return r}),e.isFunction(n.tickFormatter)&&(t.tickFormatter=function(e,t){return""+n.tickFormatter(e,t)});if(n.alignTicksWithAxis!=null){var h=(t.direction=="x"?d:v)[n.alignTicksWithAxis-1];if(h&&h.used&&h!=t){var p=t.tickGenerator(t);p.length>0&&(n.min==null&&(t.min=Math.min(t.min,p[0])),n.max==null&&p.length>1&&(t.max=Math.max(t.max,p[p.length-1]))),t.tickGenerator=function(e){var t=[],n,r;for(r=0;r<h.ticks.length;++r)n=(h.ticks[r].v-h.min)/(h.max-h.min),n=e.min+n*(e.max-e.min),t.push(n);return t};if(!t.mode&&n.tickDecimals==null){var m=Math.max(0,-Math.floor(Math.log(t.delta)/Math.LN10)+1),g=t.tickGenerator(t);g.length>1&&/\..*0$/.test((g[1]-g[0]).toFixed(m))||(t.tickDecimals=m)}}}}function W(t){var n=t.options.ticks,r=[];n==null||typeof n=="number"&&n>0?r=t.tickGenerator(t):n&&(e.isFunction(n)?r=n(t):r=n);var i,s;t.ticks=[];for(i=0;i<r.length;++i){var o=null,u=r[i];typeof u=="object"?(s=+u[0],u.length>1&&(o=u[1])):s=+u,o==null&&(o=t.tickFormatter(s,t)),isNaN(s)||t.ticks.push({v:s,label:o})}}function X(e,t){e.options.autoscaleMargin&&t.length>0&&(e.options.min==null&&(e.min=Math.min(e.min,t[0].v)),e.options.max==null&&t.length>1&&(e.max=Math.max(e.max,t[t.length-1].v)))}function V(){f.clear(),E(b.drawBackground,[h]);var e=a.grid;e.show&&e.backgroundColor&&K(),e.show&&!e.aboveData&&Q();for(var t=0;t<u.length;++t)E(b.drawSeries,[h,u[t]]),Y(u[t]);E(b.draw,[h]),e.show&&e.aboveData&&Q(),f.render(),ht()}function J(e,t){var n,r,i,s,o=k();for(var u=0;u<o.length;++u){n=o[u];if(n.direction==t){s=t+n.n+"axis",!e[s]&&n.n==1&&(s=t+"axis");if(e[s]){r=e[s].from,i=e[s].to;break}}}e[s]||(n=t=="x"?d[0]:v[0],r=e[t+"1"],i=e[t+"2"]);if(r!=null&&i!=null&&r>i){var a=r;r=i,i=a}return{from:r,to:i,axis:n}}function K(){h.save(),h.translate(m.left,m.top),h.fillStyle=bt(a.grid.backgroundColor,y,0,"rgba(255, 255, 255, 0)"),h.fillRect(0,0,g,y),h.restore()}function Q(){var t,n,r,i;h.save(),h.translate(m.left,m.top);var s=a.grid.markings;if(s){e.isFunction(s)&&(n=w.getAxes(),n.xmin=n.xaxis.min,n.xmax=n.xaxis.max,n.ymin=n.yaxis.min,n.ymax=n.yaxis.max,s=s(n));for(t=0;t<s.length;++t){var o=s[t],u=J(o,"x"),f=J(o,"y");u.from==null&&(u.from=u.axis.min),u.to==null&&(u.to=u.axis.max),f.from==null&&(f.from=f.axis.min),f.to==null&&(f.to=f.axis.max);if(u.to<u.axis.min||u.from>u.axis.max||f.to<f.axis.min||f.from>f.axis.max)continue;u.from=Math.max(u.from,u.axis.min),u.to=Math.min(u.to,u.axis.max),f.from=Math.max(f.from,f.axis.min),f.to=Math.min(f.to,f.axis.max);if(u.from==u.to&&f.from==f.to)continue;u.from=u.axis.p2c(u.from),u.to=u.axis.p2c(u.to),f.from=f.axis.p2c(f.from),f.to=f.axis.p2c(f.to),u.from==u.to||f.from==f.to?(h.beginPath(),h.strokeStyle=o.color||a.grid.markingsColor,h.lineWidth=o.lineWidth||a.grid.markingsLineWidth,h.moveTo(u.from,f.from),h.lineTo(u.to,f.to),h.stroke()):(h.fillStyle=o.color||a.grid.markingsColor,h.fillRect(u.from,f.to,u.to-u.from,f.from-f.to))}}n=k(),r=a.grid.borderWidth;for(var l=0;l<n.length;++l){var c=n[l],p=c.box,d=c.tickLength,v,b,E,S;if(!c.show||c.ticks.length==0)continue;h.lineWidth=1,c.direction=="x"?(v=0,d=="full"?b=c.position=="top"?0:y:b=p.top-m.top+(c.position=="top"?p.height:0)):(b=0,d=="full"?v=c.position=="left"?0:g:v=p.left-m.left+(c.position=="left"?p.width:0)),c.innermost||(h.strokeStyle=c.options.color,h.beginPath(),E=S=0,c.direction=="x"?E=g+1:S=y+1,h.lineWidth==1&&(c.direction=="x"?b=Math.floor(b)+.5:v=Math.floor(v)+.5),h.moveTo(v,b),h.lineTo(v+E,b+S),h.stroke()),h.strokeStyle=c.options.tickColor,h.beginPath();for(t=0;t<c.ticks.length;++t){var x=c.ticks[t].v;E=S=0;if(isNaN(x)||x<c.min||x>c.max||d=="full"&&(typeof r=="object"&&r[c.position]>0||r>0)&&(x==c.min||x==c.max))continue;c.direction=="x"?(v=c.p2c(x),S=d=="full"?-y:d,c.position=="top"&&(S=-S)):(b=c.p2c(x),E=d=="full"?-g:d,c.position=="left"&&(E=-E)),h.lineWidth==1&&(c.direction=="x"?v=Math.floor(v)+.5:b=Math.floor(b)+.5),h.moveTo(v,b),h.lineTo(v+E,b+S)}h.stroke()}r&&(i=a.grid.borderColor,typeof r=="object"||typeof i=="object"?(typeof r!="object"&&(r={top:r,right:r,bottom:r,left:r}),typeof i!="object"&&(i={top:i,right:i,bottom:i,left:i}),r.top>0&&(h.strokeStyle=i.top,h.lineWidth=r.top,h.beginPath(),h.moveTo(0-r.left,0-r.top/2),h.lineTo(g,0-r.top/2),h.stroke()),r.right>0&&(h.strokeStyle=i.right,h.lineWidth=r.right,h.beginPath(),h.moveTo(g+r.right/2,0-r.top),h.lineTo(g+r.right/2,y),h.stroke()),r.bottom>0&&(h.strokeStyle=i.bottom,h.lineWidth=r.bottom,h.beginPath(),h.moveTo(g+r.right,y+r.bottom/2),h.lineTo(0,y+r.bottom/2),h.stroke()),r.left>0&&(h.strokeStyle=i.left,h.lineWidth=r.left,h.beginPath(),h.moveTo(0-r.left/2,y+r.bottom),h.lineTo(0-r.left/2,0),h.stroke())):(h.lineWidth=r,h.strokeStyle=a.grid.borderColor,h.strokeRect(-r/2,-r/2,g+r,y+r))),h.restore()}function G(){e.each(k(),function(e,t){if(!t.show||t.ticks.length==0)return;var n=t.box,r=t.direction+"Axis "+t.direction+t.n+"Axis",i="flot-"+t.direction+"-axis flot-"+t.direction+t.n+"-axis "+r,s=t.options.font||"flot-tick-label tickLabel",o,u,a,l,c;f.removeText(i);for(var h=0;h<t.ticks.length;++h){o=t.ticks[h];if(!o.label||o.v<t.min||o.v>t.max)continue;t.direction=="x"?(l="center",u=m.left+t.p2c(o.v),t.position=="bottom"?a=n.top+n.padding:(a=n.top+n.height-n.padding,c="bottom")):(c="middle",a=m.top+t.p2c(o.v),t.position=="left"?(u=n.left+n.width-n.padding,l="right"):u=n.left+n.padding),f.addText(i,u,a,o.label,s,null,null,l,c)}})}function Y(e){e.lines.show&&Z(e),e.bars.show&&nt(e),e.points.show&&et(e)}function Z(e){function t(e,t,n,r,i){var s=e.points,o=e.pointsize,u=null,a=null;h.beginPath();for(var f=o;f<s.length;f+=o){var l=s[f-o],c=s[f-o+1],p=s[f],d=s[f+1];if(l==null||p==null)continue;if(c<=d&&c<i.min){if(d<i.min)continue;l=(i.min-c)/(d-c)*(p-l)+l,c=i.min}else if(d<=c&&d<i.min){if(c<i.min)continue;p=(i.min-c)/(d-c)*(p-l)+l,d=i.min}if(c>=d&&c>i.max){if(d>i.max)continue;l=(i.max-c)/(d-c)*(p-l)+l,c=i.max}else if(d>=c&&d>i.max){if(c>i.max)continue;p=(i.max-c)/(d-c)*(p-l)+l,d=i.max}if(l<=p&&l<r.min){if(p<r.min)continue;c=(r.min-l)/(p-l)*(d-c)+c,l=r.min}else if(p<=l&&p<r.min){if(l<r.min)continue;d=(r.min-l)/(p-l)*(d-c)+c,p=r.min}if(l>=p&&l>r.max){if(p>r.max)continue;c=(r.max-l)/(p-l)*(d-c)+c,l=r.max}else if(p>=l&&p>r.max){if(l>r.max)continue;d=(r.max-l)/(p-l)*(d-c)+c,p=r.max}(l!=u||c!=a)&&h.moveTo(r.p2c(l)+t,i.p2c(c)+n),u=p,a=d,h.lineTo(r.p2c(p)+t,i.p2c(d)+n)}h.stroke()}function n(e,t,n){var r=e.points,i=e.pointsize,s=Math.min(Math.max(0,n.min),n.max),o=0,u,a=!1,f=1,l=0,c=0;for(;;){if(i>0&&o>r.length+i)break;o+=i;var p=r[o-i],d=r[o-i+f],v=r[o],m=r[o+f];if(a){if(i>0&&p!=null&&v==null){c=o,i=-i,f=2;continue}if(i<0&&o==l+i){h.fill(),a=!1,i=-i,f=1,o=l=c+i;continue}}if(p==null||v==null)continue;if(p<=v&&p<t.min){if(v<t.min)continue;d=(t.min-p)/(v-p)*(m-d)+d,p=t.min}else if(v<=p&&v<t.min){if(p<t.min)continue;m=(t.min-p)/(v-p)*(m-d)+d,v=t.min}if(p>=v&&p>t.max){if(v>t.max)continue;d=(t.max-p)/(v-p)*(m-d)+d,p=t.max}else if(v>=p&&v>t.max){if(p>t.max)continue;m=(t.max-p)/(v-p)*(m-d)+d,v=t.max}a||(h.beginPath(),h.moveTo(t.p2c(p),n.p2c(s)),a=!0);if(d>=n.max&&m>=n.max){h.lineTo(t.p2c(p),n.p2c(n.max)),h.lineTo(t.p2c(v),n.p2c(n.max));continue}if(d<=n.min&&m<=n.min){h.lineTo(t.p2c(p),n.p2c(n.min)),h.lineTo(t.p2c(v),n.p2c(n.min));continue}var g=p,y=v;d<=m&&d<n.min&&m>=n.min?(p=(n.min-d)/(m-d)*(v-p)+p,d=n.min):m<=d&&m<n.min&&d>=n.min&&(v=(n.min-d)/(m-d)*(v-p)+p,m=n.min),d>=m&&d>n.max&&m<=n.max?(p=(n.max-d)/(m-d)*(v-p)+p,d=n.max):m>=d&&m>n.max&&d<=n.max&&(v=(n.max-d)/(m-d)*(v-p)+p,m=n.max),p!=g&&h.lineTo(t.p2c(g),n.p2c(d)),h.lineTo(t.p2c(p),n.p2c(d)),h.lineTo(t.p2c(v),n.p2c(m)),v!=y&&(h.lineTo(t.p2c(v),n.p2c(m)),h.lineTo(t.p2c(y),n.p2c(m)))}}h.save(),h.translate(m.left,m.top),h.lineJoin="round";var r=e.lines.lineWidth,i=e.shadowSize;if(r>0&&i>0){h.lineWidth=i,h.strokeStyle="rgba(0,0,0,0.1)";var s=Math.PI/18;t(e.datapoints,Math.sin(s)*(r/2+i/2),Math.cos(s)*(r/2+i/2),e.xaxis,e.yaxis),h.lineWidth=i/2,t(e.datapoints,Math.sin(s)*(r/2+i/4),Math.cos(s)*(r/2+i/4),e.xaxis,e.yaxis)}h.lineWidth=r,h.strokeStyle=e.color;var o=rt(e.lines,e.color,0,y);o&&(h.fillStyle=o,n(e.datapoints,e.xaxis,e.yaxis)),r>0&&t(e.datapoints,0,0,e.xaxis,e.yaxis),h.restore()}function et(e){function t(e,t,n,r,i,s,o,u){var a=e.points,f=e.pointsize;for(var l=0;l<a.length;l+=f){var c=a[l],p=a[l+1];if(c==null||c<s.min||c>s.max||p<o.min||p>o.max)continue;h.beginPath(),c=s.p2c(c),p=o.p2c(p)+r,u=="circle"?h.arc(c,p,t,0,i?Math.PI:Math.PI*2,!1):u(h,c,p,t,i),h.closePath(),n&&(h.fillStyle=n,h.fill()),h.stroke()}}h.save(),h.translate(m.left,m.top);var n=e.points.lineWidth,r=e.shadowSize,i=e.points.radius,s=e.points.symbol;n==0&&(n=1e-4);if(n>0&&r>0){var o=r/2;h.lineWidth=o,h.strokeStyle="rgba(0,0,0,0.1)",t(e.datapoints,i,null,o+o/2,!0,e.xaxis,e.yaxis,s),h.strokeStyle="rgba(0,0,0,0.2)",t(e.datapoints,i,null,o/2,!0,e.xaxis,e.yaxis,s)}h.lineWidth=n,h.strokeStyle=e.color,t(e.datapoints,i,rt(e.points,e.color),0,!1,e.xaxis,e.yaxis,s),h.restore()}function tt(e,t,n,r,i,s,o,u,a,f,l,c){var h,p,d,v,m,g,y,b,w;l?(b=g=y=!0,m=!1,h=n,p=e,v=t+r,d=t+i,p<h&&(w=p,p=h,h=w,m=!0,g=!1)):(m=g=y=!0,b=!1,h=e+r,p=e+i,d=n,v=t,v<d&&(w=v,v=d,d=w,b=!0,y=!1));if(p<u.min||h>u.max||v<a.min||d>a.max)return;h<u.min&&(h=u.min,m=!1),p>u.max&&(p=u.max,g=!1),d<a.min&&(d=a.min,b=!1),v>a.max&&(v=a.max,y=!1),h=u.p2c(h),d=a.p2c(d),p=u.p2c(p),v=a.p2c(v),o&&(f.beginPath(),f.moveTo(h,d),f.lineTo(h,v),f.lineTo(p,v),f.lineTo(p,d),f.fillStyle=o(d,v),f.fill()),c>0&&(m||g||y||b)&&(f.beginPath(),f.moveTo(h,d+s),m?f.lineTo(h,v+s):f.moveTo(h,v+s),y?f.lineTo(p,v+s):f.moveTo(p,v+s),g?f.lineTo(p,d+s):f.moveTo(p,d+s),b?f.lineTo(h,d+s):f.moveTo(h,d+s),f.stroke())}function nt(e){function t(t,n,r,i,s,o,u){var a=t.points,f=t.pointsize;for(var l=0;l<a.length;l+=f){if(a[l]==null)continue;tt(a[l],a[l+1],a[l+2],n,r,i,s,o,u,h,e.bars.horizontal,e.bars.lineWidth)}}h.save(),h.translate(m.left,m.top),h.lineWidth=e.bars.lineWidth,h.strokeStyle=e.color;var n;switch(e.bars.align){case"left":n=0;break;case"right":n=-e.bars.barWidth;break;case"center":n=-e.bars.barWidth/2;break;default:throw new Error("Invalid bar alignment: "+e.bars.align)}var r=e.bars.fill?function(t,n){return rt(e.bars,e.color,t,n)}:null;t(e.datapoints,n,n+e.bars.barWidth,0,r,e.xaxis,e.yaxis),h.restore()}function rt(t,n,r,i){var s=t.fill;if(!s)return null;if(t.fillColor)return bt(t.fillColor,r,i,n);var o=e.color.parse(n);return o.a=typeof s=="number"?s:.4,o.normalize(),o.toString()}function it(){t.find(".legend").remove();if(!a.legend.show)return;var n=[],r=[],i=!1,s=a.legend.labelFormatter,o,f;for(var l=0;l<u.length;++l)o=u[l],o.label&&(f=s?s(o.label,o):o.label,f&&r.push({label:f,color:o.color}));if(a.legend.sorted)if(e.isFunction(a.legend.sorted))r.sort(a.legend.sorted);else if(a.legend.sorted=="reverse")r.reverse();else{var c=a.legend.sorted!="descending";r.sort(function(e,t){return e.label==t.label?0:e.label<t.label!=c?1:-1})}for(var l=0;l<r.length;++l){var h=r[l];l%a.legend.noColumns==0&&(i&&n.push("</tr>"),n.push("<tr>"),i=!0),n.push('<td class="legendColorBox"><div style="border:1px solid '+a.legend.labelBoxBorderColor+';padding:1px"><div style="width:4px;height:0;border:5px solid '+h.color+';overflow:hidden"></div></div></td>'+'<td class="legendLabel">'+h.label+"</td>")}i&&n.push("</tr>");if(n.length==0)return;var p='<table style="font-size:smaller;color:'+a.grid.color+'">'+n.join("")+"</table>";if(a.legend.container!=null)e(a.legend.container).html(p);else{var d="",v=a.legend.position,g=a.legend.margin;g[0]==null&&(g=[g,g]),v.charAt(0)=="n"?d+="top:"+(g[1]+m.top)+"px;":v.charAt(0)=="s"&&(d+="bottom:"+(g[1]+m.bottom)+"px;"),v.charAt(1)=="e"?d+="right:"+(g[0]+m.right)+"px;":v.charAt(1)=="w"&&(d+="left:"+(g[0]+m.left)+"px;");var y=e('<div class="legend">'+p.replace('style="','style="position:absolute;'+d+";")+"</div>").appendTo(t);if(a.legend.backgroundOpacity!=0){var b=a.legend.backgroundColor;b==null&&(b=a.grid.backgroundColor,b&&typeof b=="string"?b=e.color.parse(b):b=e.color.extract(y,"background-color"),b.a=1,b=b.toString());var w=y.children();e('<div style="position:absolute;width:'+w.width()+"px;height:"+w.height()+"px;"+d+"background-color:"+b+';"> </div>').prependTo(y).css("opacity",a.legend.backgroundOpacity)}}}function ut(e,t,n){var r=a.grid.mouseActiveRadius,i=r*r+1,s=null,o=!1,f,l,c;for(f=u.length-1;f>=0;--f){if(!n(u[f]))continue;var h=u[f],p=h.xaxis,d=h.yaxis,v=h.datapoints.points,m=p.c2p(e),g=d.c2p(t),y=r/p.scale,b=r/d.scale;c=h.datapoints.pointsize,p.options.inverseTransform&&(y=Number.MAX_VALUE),d.options.inverseTransform&&(b=Number.MAX_VALUE);if(h.lines.show||h.points.show)for(l=0;l<v.length;l+=c){var w=v[l],E=v[l+1];if(w==null)continue;if(w-m>y||w-m<-y||E-g>b||E-g<-b)continue;var S=Math.abs(p.p2c(w)-e),x=Math.abs(d.p2c(E)-t),T=S*S+x*x;T<i&&(i=T,s=[f,l/c])}if(h.bars.show&&!s){var N=h.bars.align=="left"?0:-h.bars.barWidth/2,C=N+h.bars.barWidth;for(l=0;l<v.length;l+=c){var w=v[l],E=v[l+1],k=v[l+2];if(w==null)continue;if(u[f].bars.horizontal?m<=Math.max(k,w)&&m>=Math.min(k,w)&&g>=E+N&&g<=E+C:m>=w+N&&m<=w+C&&g>=Math.min(k,E)&&g<=Math.max(k,E))s=[f,l/c]}}}return s?(f=s[0],l=s[1],c=u[f].datapoints.pointsize,{datapoint:u[f].datapoints.points.slice(l*c,(l+1)*c),dataIndex:l,series:u[f],seriesIndex:f}):null}function at(e){a.grid.hoverable&&ct("plothover",e,function(e){return e["hoverable"]!=0})}function ft(e){a.grid.hoverable&&ct("plothover",e,function(e){return!1})}function lt(e){ct("plotclick",e,function(e){return e["clickable"]!=0})}function ct(e,n,r){var i=c.offset(),s=n.pageX-i.left-m.left,o=n.pageY-i.top-m.top,u=L({left:s,top:o});u.pageX=n.pageX,u.pageY=n.pageY;var f=ut(s,o,r);f&&(f.pageX=parseInt(f.series.xaxis.p2c(f.datapoint[0])+i.left+m.left,10),f.pageY=parseInt(f.series.yaxis.p2c(f.datapoint[1])+i.top+m.top,10));if(a.grid.autoHighlight){for(var l=0;l<st.length;++l){var h=st[l];h.auto==e&&(!f||h.series!=f.series||h.point[0]!=f.datapoint[0]||h.point[1]!=f.datapoint[1])&&vt(h.series,h.point)}f&&dt(f.series,f.datapoint,e)}t.trigger(e,[u,f])}function ht(){var e=a.interaction.redrawOverlayInterval;if(e==-1){pt();return}ot||(ot=setTimeout(pt,e))}function pt(){ot=null,p.save(),l.clear(),p.translate(m.left,m.top);var e,t;for(e=0;e<st.length;++e)t=st[e],t.series.bars.show?yt(t.series,t.point):gt(t.series,t.point);p.restore(),E(b.drawOverlay,[p])}function dt(e,t,n){typeof e=="number"&&(e=u[e]);if(typeof t=="number"){var r=e.datapoints.pointsize;t=e.datapoints.points.slice(r*t,r*(t+1))}var i=mt(e,t);i==-1?(st.push({series:e,point:t,auto:n}),ht()):n||(st[i].auto=!1)}function vt(e,t){if(e==null&&t==null){st=[],ht();return}typeof e=="number"&&(e=u[e]);if(typeof t=="number"){var n=e.datapoints.pointsize;t=e.datapoints.points.slice(n*t,n*(t+1))}var r=mt(e,t);r!=-1&&(st.splice(r,1),ht())}function mt(e,t){for(var n=0;n<st.length;++n){var r=st[n];if(r.series==e&&r.point[0]==t[0]&&r.point[1]==t[1])return n}return-1}function gt(t,n){var r=n[0],i=n[1],s=t.xaxis,o=t.yaxis,u=typeof t.highlightColor=="string"?t.highlightColor:e.color.parse(t.color).scale("a",.5).toString();if(r<s.min||r>s.max||i<o.min||i>o.max)return;var a=t.points.radius+t.points.lineWidth/2;p.lineWidth=a,p.strokeStyle=u;var f=1.5*a;r=s.p2c(r),i=o.p2c(i),p.beginPath(),t.points.symbol=="circle"?p.arc(r,i,f,0,2*Math.PI,!1):t.points.symbol(p,r,i,f,!1),p.closePath(),p.stroke()}function yt(t,n){var r=typeof t.highlightColor=="string"?t.highlightColor:e.color.parse(t.color).scale("a",.5).toString(),i=r,s=t.bars.align=="left"?0:-t.bars.barWidth/2;p.lineWidth=t.bars.lineWidth,p.strokeStyle=r,tt(n[0],n[1],n[2]||0,s,s+t.bars.barWidth,0,function(){return i},t.xaxis,t.yaxis,p,t.bars.horizontal,t.bars.lineWidth)}function bt(t,n,r,i){if(typeof t=="string")return t;var s=h.createLinearGradient(0,r,0,n);for(var o=0,u=t.colors.length;o<u;++o){var a=t.colors[o];if(typeof a!="string"){var f=e.color.parse(i);a.brightness!=null&&(f=f.scale("rgb",a.brightness)),a.opacity!=null&&(f.a*=a.opacity),a=f.toString()}s.addColorStop(o/(u-1),a)}return s}var u=[],a={colors:["#edc240","#afd8f8","#cb4b4b","#4da74d","#9440ed"],legend:{show:!0,noColumns:1,labelFormatter:null,labelBoxBorderColor:"#ccc",container:null,position:"ne",margin:5,backgroundColor:null,backgroundOpacity:.85,sorted:null},xaxis:{show:null,position:"bottom",mode:null,font:null,color:null,tickColor:null,transform:null,inverseTransform:null,min:null,max:null,autoscaleMargin:null,ticks:null,tickFormatter:null,labelWidth:null,labelHeight:null,reserveSpace:null,tickLength:null,alignTicksWithAxis:null,tickDecimals:null,tickSize:null,minTickSize:null},yaxis:{autoscaleMargin:.02,position:"left"},xaxes:[],yaxes:[],series:{points:{show:!1,radius:3,lineWidth:2,fill:!0,fillColor:"#ffffff",symbol:"circle"},lines:{lineWidth:2,fill:!1,fillColor:null,steps:!1},bars:{show:!1,lineWidth:2,barWidth:1,fill:!0,fillColor:null,align:"left",horizontal:!1,zero:!0},shadowSize:3,highlightColor:null},grid:{show:!0,aboveData:!1,color:"#545454",backgroundColor:null,borderColor:null,tickColor:null,margin:0,labelMargin:5,axisMargin:8,borderWidth:2,minBorderMargin:null,markings:null,markingsColor:"#f4f4f4",markingsLineWidth:2,clickable:!1,hoverable:!1,autoHighlight:!0,mouseActiveRadius:10},interaction:{redrawOverlayInterval:1e3/60},hooks:{}},f=null,l=null,c=null,h=null,p=null,d=[],v=[],m={left:0,right:0,top:0,bottom
:0},g=0,y=0,b={processOptions:[],processRawData:[],processDatapoints:[],processOffset:[],drawBackground:[],drawSeries:[],draw:[],bindEvents:[],drawOverlay:[],shutdown:[]},w=this;w.setData=T,w.setupGrid=R,w.draw=V,w.getPlaceholder=function(){return t},w.getCanvas=function(){return f.element},w.getPlotOffset=function(){return m},w.width=function(){return g},w.height=function(){return y},w.offset=function(){var e=c.offset();return e.left+=m.left,e.top+=m.top,e},w.getData=function(){return u},w.getAxes=function(){var t={},n;return e.each(d.concat(v),function(e,n){n&&(t[n.direction+(n.n!=1?n.n:"")+"axis"]=n)}),t},w.getXAxes=function(){return d},w.getYAxes=function(){return v},w.c2p=L,w.p2c=A,w.getOptions=function(){return a},w.highlight=dt,w.unhighlight=vt,w.triggerRedrawOverlay=ht,w.pointOffset=function(e){return{left:parseInt(d[C(e,"x")-1].p2c(+e.x)+m.left,10),top:parseInt(v[C(e,"y")-1].p2c(+e.y)+m.top,10)}},w.shutdown=H,w.resize=function(){var e=t.width(),n=t.height();f.resize(e,n),l.resize(e,n)},w.hooks=b,S(w),x(s),D(),T(r),R(),V(),P();var st=[],ot=null}function i(e,t){return t*Math.floor(e/t)}var t=Object.prototype.hasOwnProperty;n.prototype.resize=function(e,t){if(e<=0||t<=0)throw new Error("Invalid dimensions for plot, width = "+e+", height = "+t);var n=this.element,r=this.context,i=this.pixelRatio;this.width!=e&&(n.width=e*i,n.style.width=e+"px",this.width=e),this.height!=t&&(n.height=t*i,n.style.height=t+"px",this.height=t),r.restore(),r.save(),r.scale(i,i)},n.prototype.clear=function(){this.context.clearRect(0,0,this.width,this.height)},n.prototype.render=function(){var e=this._textCache;for(var n in e)if(t.call(e,n)){var r=this.getTextLayer(n),i=e[n];r.hide();for(var s in i)if(t.call(i,s)){var o=i[s];for(var u in o)if(t.call(o,u)){var a=o[u].positions;for(var f=0,l;l=a[f];f++)l.active?l.rendered||(r.append(l.element),l.rendered=!0):(a.splice(f--,1),l.rendered&&l.element.detach());a.length==0&&delete o[u]}}r.show()}},n.prototype.getTextLayer=function(t){var n=this.text[t];return n==null&&(this.textContainer==null&&(this.textContainer=e("<div class='flot-text'></div>").css({position:"absolute",top:0,left:0,bottom:0,right:0,"font-size":"smaller",color:"#545454"}).insertAfter(this.element)),n=this.text[t]=e("<div></div>").addClass(t).css({position:"absolute",top:0,left:0,bottom:0,right:0}).appendTo(this.textContainer)),n},n.prototype.getTextInfo=function(t,n,r,i,s){var o,u,a,f;n=""+n,typeof r=="object"?o=r.style+" "+r.variant+" "+r.weight+" "+r.size+"px/"+r.lineHeight+"px "+r.family:o=r,u=this._textCache[t],u==null&&(u=this._textCache[t]={}),a=u[o],a==null&&(a=u[o]={}),f=a[n];if(f==null){var l=e("<div></div>").html(n).css({position:"absolute","max-width":s,top:-9999}).appendTo(this.getTextLayer(t));typeof r=="object"?l.css({font:o,color:r.color}):typeof r=="string"&&l.addClass(r),f=a[n]={width:l.outerWidth(!0),height:l.outerHeight(!0),element:l,positions:[]},l.detach()}return f},n.prototype.addText=function(e,t,n,r,i,s,o,u,a){var f=this.getTextInfo(e,r,i,s,o),l=f.positions;u=="center"?t-=f.width/2:u=="right"&&(t-=f.width),a=="middle"?n-=f.height/2:a=="bottom"&&(n-=f.height);for(var c=0,h;h=l[c];c++)if(h.x==t&&h.y==n){h.active=!0;return}h={active:!0,rendered:!1,element:l.length?f.element.clone():f.element,x:t,y:n},l.push(h),h.element.css({top:Math.round(n),left:Math.round(t),"text-align":u})},n.prototype.removeText=function(e,n,r,i,s,o){if(i==null){var u=this._textCache[e];if(u!=null)for(var a in u)if(t.call(u,a)){var f=u[a];for(var l in f)if(t.call(f,l)){var c=f[l].positions;for(var h=0,p;p=c[h];h++)p.active=!1}}}else{var c=this.getTextInfo(e,i,s,o).positions;for(var h=0,p;p=c[h];h++)p.x==n&&p.y==r&&(p.active=!1)}},e.plot=function(t,n,i){var s=new r(e(t),n,i,e.plot.plugins);return s},e.plot.version="0.8.1",e.plot.plugins=[],e.fn.plot=function(t,n){return this.each(function(){e.plot(this,t,n)})}}(jQuery);
\ No newline at end of file
/* Pretty handling of time axes.
Copyright (c) 2007-2013 IOLA and Ole Laursen.
Licensed under the MIT license.
Set axis.mode to "time" to enable. See the section "Time series data" in
API.txt for details.
*/
(function($) {
var options = {
xaxis: {
timezone: null, // "browser" for local to the client or timezone for timezone-js
timeformat: null, // format string to use
twelveHourClock: false, // 12 or 24 time in time mode
monthNames: null // list of names of months
}
};
// round to nearby lower multiple of base
function floorInBase(n, base) {
return base * Math.floor(n / base);
}
// Returns a string with the date d formatted according to fmt.
// A subset of the Open Group's strftime format is supported.
function formatDate(d, fmt, monthNames, dayNames) {
if (typeof d.strftime == "function") {
return d.strftime(fmt);
}
var leftPad = function(n, pad) {
n = "" + n;
pad = "" + (pad == null ? "0" : pad);
return n.length == 1 ? pad + n : n;
};
var r = [];
var escape = false;
var hours = d.getHours();
var isAM = hours < 12;
if (monthNames == null) {
monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
}
if (dayNames == null) {
dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
}
var hours12;
if (hours > 12) {
hours12 = hours - 12;
} else if (hours == 0) {
hours12 = 12;
} else {
hours12 = hours;
}
for (var i = 0; i < fmt.length; ++i) {
var c = fmt.charAt(i);
if (escape) {
switch (c) {
case 'a': c = "" + dayNames[d.getDay()]; break;
case 'b': c = "" + monthNames[d.getMonth()]; break;
case 'd': c = leftPad(d.getDate()); break;
case 'e': c = leftPad(d.getDate(), " "); break;
case 'h': // For back-compat with 0.7; remove in 1.0
case 'H': c = leftPad(hours); break;
case 'I': c = leftPad(hours12); break;
case 'l': c = leftPad(hours12, " "); break;
case 'm': c = leftPad(d.getMonth() + 1); break;
case 'M': c = leftPad(d.getMinutes()); break;
// quarters not in Open Group's strftime specification
case 'q':
c = "" + (Math.floor(d.getMonth() / 3) + 1); break;
case 'S': c = leftPad(d.getSeconds()); break;
case 'y': c = leftPad(d.getFullYear() % 100); break;
case 'Y': c = "" + d.getFullYear(); break;
case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break;
case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break;
case 'w': c = "" + d.getDay(); break;
}
r.push(c);
escape = false;
} else {
if (c == "%") {
escape = true;
} else {
r.push(c);
}
}
}
return r.join("");
}
// To have a consistent view of time-based data independent of which time
// zone the client happens to be in we need a date-like object independent
// of time zones. This is done through a wrapper that only calls the UTC
// versions of the accessor methods.
function makeUtcWrapper(d) {
function addProxyMethod(sourceObj, sourceMethod, targetObj, targetMethod) {
sourceObj[sourceMethod] = function() {
return targetObj[targetMethod].apply(targetObj, arguments);
};
};
var utc = {
date: d
};
// support strftime, if found
if (d.strftime != undefined) {
addProxyMethod(utc, "strftime", d, "strftime");
}
addProxyMethod(utc, "getTime", d, "getTime");
addProxyMethod(utc, "setTime", d, "setTime");
var props = ["Date", "Day", "FullYear", "Hours", "Milliseconds", "Minutes", "Month", "Seconds"];
for (var p = 0; p < props.length; p++) {
addProxyMethod(utc, "get" + props[p], d, "getUTC" + props[p]);
addProxyMethod(utc, "set" + props[p], d, "setUTC" + props[p]);
}
return utc;
};
// select time zone strategy. This returns a date-like object tied to the
// desired timezone
function dateGenerator(ts, opts) {
if (opts.timezone == "browser") {
return new Date(ts);
} else if (!opts.timezone || opts.timezone == "utc") {
return makeUtcWrapper(new Date(ts));
} else if (typeof timezoneJS != "undefined" && typeof timezoneJS.Date != "undefined") {
var d = new timezoneJS.Date();
// timezone-js is fickle, so be sure to set the time zone before
// setting the time.
d.setTimezone(opts.timezone);
d.setTime(ts);
return d;
} else {
return makeUtcWrapper(new Date(ts));
}
}
// map of app. size of time units in milliseconds
var timeUnitSize = {
"second": 1000,
"minute": 60 * 1000,
"hour": 60 * 60 * 1000,
"day": 24 * 60 * 60 * 1000,
"month": 30 * 24 * 60 * 60 * 1000,
"quarter": 3 * 30 * 24 * 60 * 60 * 1000,
"year": 365.2425 * 24 * 60 * 60 * 1000
};
// the allowed tick sizes, after 1 year we use
// an integer algorithm
var baseSpec = [
[1, "second"], [2, "second"], [5, "second"], [10, "second"],
[30, "second"],
[1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"],
[30, "minute"],
[1, "hour"], [2, "hour"], [4, "hour"],
[8, "hour"], [12, "hour"],
[1, "day"], [2, "day"], [3, "day"],
[0.25, "month"], [0.5, "month"], [1, "month"],
[2, "month"]
];
// we don't know which variant(s) we'll need yet, but generating both is
// cheap
var specMonths = baseSpec.concat([[3, "month"], [6, "month"],
[1, "year"]]);
var specQuarters = baseSpec.concat([[1, "quarter"], [2, "quarter"],
[1, "year"]]);
function init(plot) {
plot.hooks.processOptions.push(function (plot, options) {
$.each(plot.getAxes(), function(axisName, axis) {
var opts = axis.options;
if (opts.mode == "time") {
axis.tickGenerator = function(axis) {
var ticks = [];
var d = dateGenerator(axis.min, opts);
var minSize = 0;
// make quarter use a possibility if quarters are
// mentioned in either of these options
var spec = (opts.tickSize && opts.tickSize[1] ===
"quarter") ||
(opts.minTickSize && opts.minTickSize[1] ===
"quarter") ? specQuarters : specMonths;
if (opts.minTickSize != null) {
if (typeof opts.tickSize == "number") {
minSize = opts.tickSize;
} else {
minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]];
}
}
for (var i = 0; i < spec.length - 1; ++i) {
if (axis.delta < (spec[i][0] * timeUnitSize[spec[i][1]]
+ spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2
&& spec[i][0] * timeUnitSize[spec[i][1]] >= minSize) {
break;
}
}
var size = spec[i][0];
var unit = spec[i][1];
// special-case the possibility of several years
if (unit == "year") {
// if given a minTickSize in years, just use it,
// ensuring that it's an integer
if (opts.minTickSize != null && opts.minTickSize[1] == "year") {
size = Math.floor(opts.minTickSize[0]);
} else {
var magn = Math.pow(10, Math.floor(Math.log(axis.delta / timeUnitSize.year) / Math.LN10));
var norm = (axis.delta / timeUnitSize.year) / magn;
if (norm < 1.5) {
size = 1;
} else if (norm < 3) {
size = 2;
} else if (norm < 7.5) {
size = 5;
} else {
size = 10;
}
size *= magn;
}
// minimum size for years is 1
if (size < 1) {
size = 1;
}
}
axis.tickSize = opts.tickSize || [size, unit];
var tickSize = axis.tickSize[0];
unit = axis.tickSize[1];
var step = tickSize * timeUnitSize[unit];
if (unit == "second") {
d.setSeconds(floorInBase(d.getSeconds(), tickSize));
} else if (unit == "minute") {
d.setMinutes(floorInBase(d.getMinutes(), tickSize));
} else if (unit == "hour") {
d.setHours(floorInBase(d.getHours(), tickSize));
} else if (unit == "month") {
d.setMonth(floorInBase(d.getMonth(), tickSize));
} else if (unit == "quarter") {
d.setMonth(3 * floorInBase(d.getMonth() / 3,
tickSize));
} else if (unit == "year") {
d.setFullYear(floorInBase(d.getFullYear(), tickSize));
}
// reset smaller components
d.setMilliseconds(0);
if (step >= timeUnitSize.minute) {
d.setSeconds(0);
}
if (step >= timeUnitSize.hour) {
d.setMinutes(0);
}
if (step >= timeUnitSize.day) {
d.setHours(0);
}
if (step >= timeUnitSize.day * 4) {
d.setDate(1);
}
if (step >= timeUnitSize.month * 2) {
d.setMonth(floorInBase(d.getMonth(), 3));
}
if (step >= timeUnitSize.quarter * 2) {
d.setMonth(floorInBase(d.getMonth(), 6));
}
if (step >= timeUnitSize.year) {
d.setMonth(0);
}
var carry = 0;
var v = Number.NaN;
var prev;
do {
prev = v;
v = d.getTime();
ticks.push(v);
if (unit == "month" || unit == "quarter") {
if (tickSize < 1) {
// a bit complicated - we'll divide the
// month/quarter up but we need to take
// care of fractions so we don't end up in
// the middle of a day
d.setDate(1);
var start = d.getTime();
d.setMonth(d.getMonth() +
(unit == "quarter" ? 3 : 1));
var end = d.getTime();
d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize);
carry = d.getHours();
d.setHours(0);
} else {
d.setMonth(d.getMonth() +
tickSize * (unit == "quarter" ? 3 : 1));
}
} else if (unit == "year") {
d.setFullYear(d.getFullYear() + tickSize);
} else {
d.setTime(v + step);
}
} while (v < axis.max && v != prev);
return ticks;
};
axis.tickFormatter = function (v, axis) {
var d = dateGenerator(v, axis.options);
// first check global format
if (opts.timeformat != null) {
return formatDate(d, opts.timeformat, opts.monthNames, opts.dayNames);
}
// possibly use quarters if quarters are mentioned in
// any of these places
var useQuarters = (axis.options.tickSize &&
axis.options.tickSize[1] == "quarter") ||
(axis.options.minTickSize &&
axis.options.minTickSize[1] == "quarter");
var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]];
var span = axis.max - axis.min;
var suffix = (opts.twelveHourClock) ? " %p" : "";
var hourCode = (opts.twelveHourClock) ? "%I" : "%H";
var fmt;
if (t < timeUnitSize.minute) {
fmt = hourCode + ":%M:%S" + suffix;
} else if (t < timeUnitSize.day) {
if (span < 2 * timeUnitSize.day) {
fmt = hourCode + ":%M" + suffix;
} else {
fmt = "%b %d " + hourCode + ":%M" + suffix;
}
} else if (t < timeUnitSize.month) {
fmt = "%b %d";
} else if ((useQuarters && t < timeUnitSize.quarter) ||
(!useQuarters && t < timeUnitSize.year)) {
if (span < timeUnitSize.year) {
fmt = "%b";
} else {
fmt = "%b %Y";
}
} else if (useQuarters && t < timeUnitSize.year) {
if (span < timeUnitSize.year) {
fmt = "Q%q";
} else {
fmt = "Q%q %Y";
}
} else {
fmt = "%Y";
}
var rt = formatDate(d, fmt, opts.monthNames, opts.dayNames);
return rt;
};
}
});
});
}
$.plot.plugins.push({
init: init,
options: options,
name: 'time',
version: '1.0'
});
// Time-axis support used to be in Flot core, which exposed the
// formatDate function on the plot object. Various plugins depend
// on the function, so we need to re-expose it here.
$.plot.formatDate = formatDate;
})(jQuery);
/* Pretty handling of time axes.
Copyright (c) 2007-2013 IOLA and Ole Laursen.
Licensed under the MIT license.
Set axis.mode to "time" to enable. See the section "Time series data" in
API.txt for details.
*/(function(e){function n(e,t){return t*Math.floor(e/t)}function r(e,t,n,r){if(typeof e.strftime=="function")return e.strftime(t);var i=function(e,t){return e=""+e,t=""+(t==null?"0":t),e.length==1?t+e:e},s=[],o=!1,u=e.getHours(),a=u<12;n==null&&(n=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]),r==null&&(r=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]);var f;u>12?f=u-12:u==0?f=12:f=u;for(var l=0;l<t.length;++l){var c=t.charAt(l);if(o){switch(c){case"a":c=""+r[e.getDay()];break;case"b":c=""+n[e.getMonth()];break;case"d":c=i(e.getDate());break;case"e":c=i(e.getDate()," ");break;case"h":case"H":c=i(u);break;case"I":c=i(f);break;case"l":c=i(f," ");break;case"m":c=i(e.getMonth()+1);break;case"M":c=i(e.getMinutes());break;case"q":c=""+(Math.floor(e.getMonth()/3)+1);break;case"S":c=i(e.getSeconds());break;case"y":c=i(e.getFullYear()%100);break;case"Y":c=""+e.getFullYear();break;case"p":c=a?"am":"pm";break;case"P":c=a?"AM":"PM";break;case"w":c=""+e.getDay()}s.push(c),o=!1}else c=="%"?o=!0:s.push(c)}return s.join("")}function i(e){function t(e,t,n,r){e[t]=function(){return n[r].apply(n,arguments)}}var n={date:e};e.strftime!=undefined&&t(n,"strftime",e,"strftime"),t(n,"getTime",e,"getTime"),t(n,"setTime",e,"setTime");var r=["Date","Day","FullYear","Hours","Milliseconds","Minutes","Month","Seconds"];for(var i=0;i<r.length;i++)t(n,"get"+r[i],e,"getUTC"+r[i]),t(n,"set"+r[i],e,"setUTC"+r[i]);return n}function s(e,t){if(t.timezone=="browser")return new Date(e);if(!t.timezone||t.timezone=="utc")return i(new Date(e));if(typeof timezoneJS!="undefined"&&typeof timezoneJS.Date!="undefined"){var n=new timezoneJS.Date;return n.setTimezone(t.timezone),n.setTime(e),n}return i(new Date(e))}function l(t){t.hooks.processOptions.push(function(t,i){e.each(t.getAxes(),function(e,t){var i=t.options;i.mode=="time"&&(t.tickGenerator=function(e){var t=[],r=s(e.min,i),u=0,l=i.tickSize&&i.tickSize[1]==="quarter"||i.minTickSize&&i.minTickSize[1]==="quarter"?f:a;i.minTickSize!=null&&(typeof i.tickSize=="number"?u=i.tickSize:u=i.minTickSize[0]*o[i.minTickSize[1]]);for(var c=0;c<l.length-1;++c)if(e.delta<(l[c][0]*o[l[c][1]]+l[c+1][0]*o[l[c+1][1]])/2&&l[c][0]*o[l[c][1]]>=u)break;var h=l[c][0],p=l[c][1];if(p=="year"){if(i.minTickSize!=null&&i.minTickSize[1]=="year")h=Math.floor(i.minTickSize[0]);else{var d=Math.pow(10,Math.floor(Math.log(e.delta/o.year)/Math.LN10)),v=e.delta/o.year/d;v<1.5?h=1:v<3?h=2:v<7.5?h=5:h=10,h*=d}h<1&&(h=1)}e.tickSize=i.tickSize||[h,p];var m=e.tickSize[0];p=e.tickSize[1];var g=m*o[p];p=="second"?r.setSeconds(n(r.getSeconds(),m)):p=="minute"?r.setMinutes(n(r.getMinutes(),m)):p=="hour"?r.setHours(n(r.getHours(),m)):p=="month"?r.setMonth(n(r.getMonth(),m)):p=="quarter"?r.setMonth(3*n(r.getMonth()/3,m)):p=="year"&&r.setFullYear(n(r.getFullYear(),m)),r.setMilliseconds(0),g>=o.minute&&r.setSeconds(0),g>=o.hour&&r.setMinutes(0),g>=o.day&&r.setHours(0),g>=o.day*4&&r.setDate(1),g>=o.month*2&&r.setMonth(n(r.getMonth(),3)),g>=o.quarter*2&&r.setMonth(n(r.getMonth(),6)),g>=o.year&&r.setMonth(0);var y=0,b=Number.NaN,w;do{w=b,b=r.getTime(),t.push(b);if(p=="month"||p=="quarter")if(m<1){r.setDate(1);var E=r.getTime();r.setMonth(r.getMonth()+(p=="quarter"?3:1));var S=r.getTime();r.setTime(b+y*o.hour+(S-E)*m),y=r.getHours(),r.setHours(0)}else r.setMonth(r.getMonth()+m*(p=="quarter"?3:1));else p=="year"?r.setFullYear(r.getFullYear()+m):r.setTime(b+g)}while(b<e.max&&b!=w);return t},t.tickFormatter=function(e,t){var n=s(e,t.options);if(i.timeformat!=null)return r(n,i.timeformat,i.monthNames,i.dayNames);var u=t.options.tickSize&&t.options.tickSize[1]=="quarter"||t.options.minTickSize&&t.options.minTickSize[1]=="quarter",a=t.tickSize[0]*o[t.tickSize[1]],f=t.max-t.min,l=i.twelveHourClock?" %p":"",c=i.twelveHourClock?"%I":"%H",h;a<o.minute?h=c+":%M:%S"+l:a<o.day?f<2*o.day?h=c+":%M"+l:h="%b %d "+c+":%M"+l:a<o.month?h="%b %d":u&&a<o.quarter||!u&&a<o.year?f<o.year?h="%b":h="%b %Y":u&&a<o.year?f<o.year?h="Q%q":h="Q%q %Y":h="%Y";var p=r(n,h,i.monthNames,i.dayNames);return p})})})}var t={xaxis:{timezone:null,timeformat:null,twelveHourClock:!1,monthNames:null}},o={second:1e3,minute:6e4,hour:36e5,day:864e5,month:2592e6,quarter:7776e6,year:525949.2*60*1e3},u=[[1,"second"],[2,"second"],[5,"second"],[10,"second"],[30,"second"],[1,"minute"],[2,"minute"],[5,"minute"],[10,"minute"],[30,"minute"],[1,"hour"],[2,"hour"],[4,"hour"],[8,"hour"],[12,"hour"],[1,"day"],[2,"day"],[3,"day"],[.25,"month"],[.5,"month"],[1,"month"],[2,"month"]],a=u.concat([[3,"month"],[6,"month"],[1,"year"]]),f=u.concat([[1,"quarter"],[2,"quarter"],[1,"year"]]);e.plot.plugins.push({init:l,options:t,name:"time",version:"1.0"}),e.plot.formatDate=r})(jQuery);
\ No newline at end of file
// json2.js
// 2016-10-28
// Public Domain.
// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
// See https://www.JSON.org/js.html
// This code should be minified before deployment.
// See https://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. This file provides the ES5 JSON capability to ES3 systems.
// If a project might run on IE8 or earlier, then this file should be included.
// This file does nothing on ES5 systems.
// 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
eval, for, this
*/
/*property
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";
var rx_one = /^[\],:{}\s]*$/;
var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
function f(n) {
// Format integers to have at least two digits.
return n < 10
? "0" + n
: n;
}
function this_value() {
return this.valueOf();
}
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;
};
Boolean.prototype.toJSON = this_value;
Number.prototype.toJSON = this_value;
String.prototype.toJSON = this_value;
}
var gap;
var indent;
var meta;
var 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.
rx_escapable.lastIndex = 0;
return rx_escapable.test(string)
? "\"" + string.replace(rx_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.
var k; // The member key.
var v; // The member value.
var length;
var mind = gap;
var partial;
var 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") {
meta = { // table of character substitutions
"\b": "\\b",
"\t": "\\t",
"\n": "\\n",
"\f": "\\f",
"\r": "\\r",
"\"": "\\\"",
"\\": "\\\\"
};
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;
var v;
var 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);
rx_dangerous.lastIndex = 0;
if (rx_dangerous.test(text)) {
text = text.replace(rx_dangerous, 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 (
rx_one.test(
text
.replace(rx_two, "@")
.replace(rx_three, "]")
.replace(rx_four, "")
)
) {
// 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");
};
}
}());
$(document).ready(function() {
replace_content('outer', format('login', {}));
start_app_login();
});
function dispatcher_add(fun) {
dispatcher_modules.push(fun);
if (dispatcher_modules.length == extension_count) {
start_app();
}
}
function dispatcher() {
for (var i in dispatcher_modules) {
dispatcher_modules[i](this);
}
}
function set_auth_pref(userinfo) {
// clear a local storage value used by earlier versions
clear_local_pref('auth');
var b64 = b64_encode_utf8(userinfo);
var date = new Date();
var login_session_timeout = get_login_session_timeout();
if (login_session_timeout) {
date.setMinutes(date.getMinutes() + login_session_timeout);
} else {
// 8 hours from now
date.setHours(date.getHours() + 8);
}
store_cookie_value_with_expiration('auth', encodeURIComponent(b64), date);
}
function login_route () {
var userpass = '' + this.params['username'] + ':' + this.params['password'],
location = window.location.href,
hash = window.location.hash;
set_auth_pref(decodeURIComponent(userpass));
location = location.substr(0, location.length - hash.length);
window.location.replace(location);
// because we change url, we don't need to hit check_login as
// we'll end up doing that at the bottom of start_app_login after
// we've changed url.
}
function login_route_with_path() {
var params = ('' + this.params['splat']).split('/');
var user = params.shift();
var pass = params.shift();
var userpass = '' + user + ':' + pass,
location = window.location.href,
hash = window.location.hash;
set_auth_pref(decodeURIComponent(userpass));
location = location.substr(0, location.length - hash.length) + '#/' + params.join('/');
check_login();
window.location.replace(location);
}
function start_app_login() {
app = new Sammy.Application(function () {
this.get('#/', function() {});
this.put('#/login', function() {
username = this.params['username'];
password = this.params['password'];
set_auth_pref(username + ':' + password);
check_login();
});
this.get('#/login/:username/:password', login_route);
this.get(/\#\/login\/(.*)/, login_route_with_path);
});
app.run();
if (get_cookie_value('auth') != null) {
check_login();
}
}
function check_login() {
user = JSON.parse(sync_get('/whoami'));
if (user == false) {
// clear a local storage value used by earlier versions
clear_pref('auth');
clear_cookie_value('auth');
replace_content('login-status', '<p>Login failed</p>');
}
else {
hide_popup_warn();
replace_content('outer', format('layout', {}));
var user_login_session_timeout = parseInt(user.login_session_timeout);
// Update auth login_session_timeout if changed
if (has_auth_cookie_value() && !isNaN(user_login_session_timeout) &&
user_login_session_timeout !== get_login_session_timeout()) {
update_login_session_timeout(user_login_session_timeout);
}
setup_global_vars();
setup_constant_events();
update_vhosts();
update_interval();
setup_extensions();
}
}
function get_login_session_timeout() {
parseInt(get_cookie_value('login_session_timeout'));
}
function update_login_session_timeout(login_session_timeout) {
var auth_info = get_cookie_value('auth');
var date = new Date();
// `login_session_timeout` minutes from now
date.setMinutes(date.getMinutes() + login_session_timeout);
store_cookie_value('login_session_timeout', login_session_timeout);
store_cookie_value_with_expiration('auth', auth_info, date);
}
function start_app() {
if (app !== undefined) {
app.unload();
}
// Oh boy. Sammy uses various different methods to determine if
// the URL hash has changed. Unsurprisingly this is a native event
// in modern browsers, and falls back to an icky polling function
// in MSIE. But it looks like there's a bug. The polling function
// should get installed when the app is started. But it's guarded
// behind if (Sammy.HashLocationProxy._interval != null). And of
// course that's not specific to the application; it's pretty
// global. So we need to manually clear that in order for links to
// work in MSIE.
// Filed as https://github.com/quirkey/sammy/issues/171
//
// Note for when we upgrade: HashLocationProxy has become
// DefaultLocationProxy in later versions, but otherwise the issue
// remains.
// updated to the version 0.7.6 this _interval = null is fixed
// just leave the history here.
//Sammy.HashLocationProxy._interval = null;
app = new Sammy.Application(dispatcher);
app.run();
var url = this.location.toString();
if (url.indexOf('#') == -1) {
this.location = url + '#/';
}
}
function setup_constant_events() {
$('#update-every').on('change', function() {
var interval = $(this).val();
store_pref('interval', interval);
if (interval == '')
interval = null;
else
interval = parseInt(interval);
set_timer_interval(interval);
});
$('#show-vhost').on('change', function() {
current_vhost = $(this).val();
store_pref('vhost', current_vhost);
update();
});
if (!vhosts_interesting) {
$('#vhost-form').hide();
}
}
function update_vhosts() {
var vhosts = JSON.parse(sync_get('/vhosts'));
vhosts_interesting = vhosts.length > 1;
if (vhosts_interesting)
$('#vhost-form').show();
else
$('#vhost-form').hide();
var select = $('#show-vhost').get(0);
select.options.length = vhosts.length + 1;
var index = 0;
for (var i = 0; i < vhosts.length; i++) {
var vhost = vhosts[i].name;
select.options[i + 1] = new Option(vhost, vhost);
if (vhost == current_vhost) index = i + 1;
}
select.selectedIndex = index;
current_vhost = select.options[index].value;
store_pref('vhost', current_vhost);
}
function setup_extensions() {
var extensions = JSON.parse(sync_get('/extensions'));
extension_count = 0;
for (var i in extensions) {
var extension = extensions[i];
if ($.isPlainObject(extension) && extension.hasOwnProperty("javascript")) {
dynamic_load(extension.javascript);
extension_count++;
}
}
}
function dynamic_load(filename) {
var element = document.createElement('script');
element.setAttribute('type', 'text/javascript');
element.setAttribute('src', 'js/' + filename);
document.getElementsByTagName("head")[0].appendChild(element);
}
function update_interval() {
var intervalStr = get_pref('interval');
var interval;
if (intervalStr == null) interval = 5000;
else if (intervalStr == '') interval = null;
else interval = parseInt(intervalStr);
if (isNaN(interval)) interval = null; // Prevent DoS if cookie malformed
set_timer_interval(interval);
var select = $('#update-every').get(0);
var opts = select.options;
for (var i = 0; i < opts.length; i++) {
if (opts[i].value == intervalStr) {
select.selectedIndex = i;
break;
}
}
}
function go_to(url) {
this.location = url;
}
function set_timer_interval(interval) {
timer_interval = interval;
reset_timer();
}
function reset_timer() {
clearInterval(timer);
if (timer_interval != null) {
timer = setInterval(partial_update, timer_interval);
}
}
function update_manual(div, query) {
var path;
var template;
if (query == 'memory' || query == 'binary') {
path = current_reqs['node']['path'] + '?' + query + '=true';
template = query;
}
var data = JSON.parse(sync_get(path));
replace_content(div, format(template, data));
postprocess_partial();
}
function render(reqs, template, highlight) {
var old_template = current_template;
current_template = template;
current_reqs = reqs;
for (var i in outstanding_reqs) {
outstanding_reqs[i].abort();
}
outstanding_reqs = [];
current_highlight = highlight;
if (old_template !== current_template) {
window.scrollTo(0, 0);
}
update();
}
function update() {
replace_content('debug', '');
clearInterval(timer);
with_update(function(html) {
update_navigation();
replace_content('main', html);
postprocess();
postprocess_partial();
render_charts();
maybe_scroll();
reset_timer();
});
}
function partial_update() {
if (!$(".pagination_class").is(":focus")) {
if ($('.updatable').length > 0) {
if (update_counter >= 200) {
update_counter = 0;
full_refresh();
return;
}
with_update(function(html) {
update_counter++;
replace_content('scratch', html);
var befores = $('#main .updatable');
var afters = $('#scratch .updatable');
if (befores.length != afters.length) {
console.log("before/after mismatch! Doing a full reload...");
full_refresh();
}
for (var i = 0; i < befores.length; i++) {
$(befores[i]).empty().append($(afters[i]).contents());
}
replace_content('scratch', '');
postprocess_partial();
render_charts();
});
}
}
}
function update_navigation() {
var l1 = '';
var l2 = '';
var descend = null;
for (var k in NAVIGATION) {
var val = NAVIGATION[k];
var path = val;
while (!leaf(path)) {
path = first_showable_child(path);
}
var selected = false;
if (contains_current_highlight(val)) {
selected = true;
if (!leaf(val)) {
descend = nav(val);
}
}
if (show(path)) {
l1 += '<li><a href="' + nav(path) + '"' +
(selected ? ' class="selected"' : '') + '>' + k + '</a></li>';
}
}
if (descend) {
l2 = obj_to_ul(descend);
$('#main').addClass('with-rhs');
}
else {
$('#main').removeClass('with-rhs');
}
replace_content('tabs', l1);
replace_content('rhs', l2);
}
function nav(pair) {
return pair[0];
}
function show(pair) {
return jQuery.inArray(pair[1], user_tags) != -1;
}
function leaf(pair) {
return typeof(nav(pair)) == 'string';
}
function first_showable_child(pair) {
var items = pair[0];
var ks = keys(items);
for (var i = 0; i < ks.length; i++) {
var child = items[ks[i]];
if (show(child)) return child;
}
return items[ks[0]]; // We'll end up not showing it anyway
}
function contains_current_highlight(val) {
if (leaf(val)) {
return current_highlight == nav(val);
}
else {
var b = false;
for (var k in val) {
b |= contains_current_highlight(val[k]);
}
return b;
}
}
function obj_to_ul(val) {
var res = '<ul>';
for (var k in val) {
var obj = val[k];
if (show(obj)) {
res += '<li>';
if (leaf(obj)) {
res += '<a href="' + nav(obj) + '"' +
(current_highlight == nav(obj) ? ' class="selected"' : '') +
'>' + k + '</a>';
}
else {
res += obj_to_ul(nav(obj));
}
res += '</li>';
}
}
return res + '</ul>';
}
function full_refresh() {
store_pref('position', x_position() + ',' + y_position());
location.reload();
}
function maybe_scroll() {
var pos = get_pref('position');
if (pos) {
clear_pref('position');
var xy = pos.split(",");
window.scrollTo(parseInt(xy[0]), parseInt(xy[1]));
}
}
function x_position() {
return window.pageXOffset ?
window.pageXOffset :
document.documentElement.scrollLeft ?
document.documentElement.scrollLeft :
document.body.scrollLeft;
}
function y_position() {
return window.pageYOffset ?
window.pageYOffset :
document.documentElement.scrollTop ?
document.documentElement.scrollTop :
document.body.scrollTop;
}
function with_update(fun) {
if(outstanding_reqs.length > 0){
return false;
}
with_reqs(apply_state(current_reqs), [], function(json) {
var html = format(current_template, json);
fun(html);
update_status('ok');
});
return true;
}
function apply_state(reqs) {
var reqs2 = {};
for (k in reqs) {
var req = reqs[k];
var options = {};
if (typeof(req) == "object") {
options = req.options;
req = req.path;
}
var req2;
if (options['vhost'] != undefined && current_vhost != '') {
var indexPage = req.indexOf("?page=");
if (indexPage >- 1) {
pageUrl = req.substr(indexPage);
req2 = req.substr(0,indexPage) + '/' + esc(current_vhost) + pageUrl;
} else
req2 = req + '/' + esc(current_vhost);
}
else {
req2 = req;
}
var qs = [];
if (options['sort'] != undefined && current_sort != null) {
qs.push('sort=' + current_sort);
qs.push('sort_reverse=' + current_sort_reverse);
}
if (options['ranges'] != undefined) {
for (i in options['ranges']) {
var type = options['ranges'][i];
var range = get_pref('chart-range').split('|');
var prefix;
if (type.substring(0, 8) == 'lengths-') {
prefix = 'lengths';
}
else if (type.substring(0, 10) == 'msg-rates-') {
prefix = 'msg_rates';
}
else if (type.substring(0, 11) == 'data-rates-') {
prefix = 'data_rates';
}
else if (type == 'node-stats') {
prefix = 'node_stats';
}
qs.push(prefix + '_age=' + parseInt(range[0]));
qs.push(prefix + '_incr=' + parseInt(range[1]));
}
}
/* Unknown options are used as query parameters as is. */
Object.keys(options).forEach(function (key) {
/* Skip known keys we already handled and undefined parameters. */
if (key == 'vhost' || key == 'sort' || key == 'ranges')
return;
if (!key || options[key] == undefined)
return;
qs.push(esc(key) + '=' + esc(options[key]));
});
qs = qs.join('&');
if (qs != '')
if (req2.indexOf("?page=") >- 1)
qs = '&' + qs;
else
qs = '?' + qs;
reqs2[k] = req2 + qs;
}
return reqs2;
}
function show_popup(type, text, _mode) {
var cssClass = '.form-popup-' + type;
function hide() {
$(cssClass).fadeOut(100, function() {
$(this).remove();
});
}
hide();
$('#outer').after(format('popup', {'type': type, 'text': text}));
$(cssClass).fadeIn(100);
$(cssClass + ' span').on('click', function () {
$('.popup-owner').removeClass('popup-owner');
hide();
});
}
function hide_popup_warn() {
var cssClass = '.form-popup-warn';
$('.popup-owner').removeClass('popup-owner');
$(cssClass).fadeOut(100, function() {
$(this).remove();
});
}
function submit_import(form) {
if (form.file.value) {
var confirm_upload = confirm('Are you sure you want to import a definitions file? Some entities (vhosts, users, queues, etc) may be overwritten!');
if (confirm_upload === true) {
var file = form.file.files[0]; // FUTURE: limit upload file size (?)
var vhost_upload = $("select[name='vhost-upload'] option:selected");
var vhost_selected = vhost_upload.index() > 0;
var vhost_name = null;
if (vhost_selected) {
vhost_name = vhost_upload.val();
}
var vhost_part = '';
if (vhost_name) {
vhost_part = '/' + esc(vhost_name);
}
var form_action = "/definitions" + vhost_part + '?auth=' + get_cookie_value('auth');
var fd = new FormData();
fd.append('file', file);
with_req('POST', form_action, fd, function(resp) {
show_popup('info', 'Your definitions were imported successfully.');
});
}
}
return false;
};
function postprocess() {
$('form.confirm-queue').on('submit', function() {
return confirm("Are you sure? The queue is going to be deleted. " +
"Messages cannot be recovered after deletion.");
});
$('form.confirm-purge-queue').on('submit', function() {
return confirm("Are you sure? Messages cannot be recovered after purging.");
});
$('form.confirm').on('submit', function() {
return confirm("Are you sure? This object cannot be recovered " +
"after deletion.");
});
$('label').map(function() {
if ($(this).attr('for') == '') {
var id = 'auto-label-' + Math.floor(Math.random()*1000000000);
var input = $(this).parents('tr').first().find('input, select');
if (input.attr('id') == '') {
$(this).attr('for', id);
input.attr('id', id);
}
}
});
$('#download-definitions').on('click', function() {
var idx = $("select[name='vhost-download'] option:selected").index();
var vhost = ((idx <=0 ) ? "" : "/" + esc($("select[name='vhost-download'] option:selected").val()));
var path = 'api/definitions' + vhost + '?download=' +
esc($('#download-filename').val()) +
'&auth=' + get_cookie_value('auth');
window.location = path;
setTimeout('app.run()');
return false;
});
$('.update-manual').on('click', function() {
update_manual($(this).attr('for'), $(this).attr('query'));
});
$(document).on('keyup', '.multifield input', function() {
update_multifields();
});
$(document).on('change', '.multifield select', function() {
update_multifields();
});
$('.controls-appearance').on('change', function() {
var params = $(this).get(0).options;
var selected = $(this).val();
for (i = 0; i < params.length; i++) {
var param = params[i].value;
if (param == selected) {
$('#' + param + '-div').slideDown(100);
} else {
$('#' + param + '-div').slideUp(100);
}
}
});
$(document).on('click', '.help', function() {
show_popup('help', HELP[$(this).attr('id')]);
});
$(document).on('click', '.popup-options-link', function() {
$('.popup-owner').removeClass('popup-owner');
$(this).addClass('popup-owner');
var template = $(this).attr('type') + '-options';
show_popup('options', format(template, {span: $(this)}), 'fade');
});
$(document).on('click', '.rate-visibility-option', function() {
var k = $(this).attr('data-pref');
var show = get_pref(k) !== 'true';
store_pref(k, '' + show);
partial_update();
});
$(document).on('focus', 'input, select', function() {
update_counter = 0; // If there's interaction, reset the counter.
});
$('.tag-link').on('click', function() {
$('#tags').val($(this).attr('tag'));
});
$('.argument-link').on('click', function() {
var field = $(this).attr('field');
var row = $('#' + field).find('.mf').last();
var key = row.find('input').first();
var value = row.find('input').last();
var type = row.find('select').last();
key.val($(this).attr('key'));
value.val($(this).attr('value'));
type.val($(this).attr('type'));
update_multifields();
});
$(document).on('click', 'form.auto-submit select, form.auto-submit input', function(){
$(this).parents('form').submit();
});
$('#filter').on('keyup', debounce(update_filter, 500));
$('#filter-regex-mode').on('change', update_filter_regex_mode);
$('#truncate').on('keyup', debounce(update_truncate, 500));
if (! user_administrator) {
$('.administrator-only').remove();
}
update_multifields();
}
function url_pagination_template(template, defaultPage, defaultPageSize){
var page_number_request = fmt_page_number_request(template, defaultPage);
var page_size = fmt_page_size_request(template, defaultPageSize);
var name_request = fmt_filter_name_request(template, "");
var use_regex = fmt_regex_request(template, "") == "checked";
if (use_regex) {
name_request = esc(name_request);
}
return '/' + template +
'?page=' + page_number_request +
'&page_size=' + page_size +
'&name=' + name_request +
'&use_regex=' + use_regex;
}
function stored_page_info(template, page_start){
var pageSize = fmt_strip_tags($('#' + template+'-pagesize').val());
var filterName = fmt_strip_tags($('#' + template+'-name').val());
store_pref(template + '_current_page_number', page_start);
if (filterName != null && filterName != undefined) {
store_pref(template + '_current_filter_name', filterName);
}
var regex_on = $("#" + template + "-filter-regex-mode").is(':checked');
if (regex_on != null && regex_on != undefined) {
store_pref(template + '_current_regex', regex_on ? "checked" : " " );
}
if (pageSize != null && pageSize != undefined) {
store_pref(template + '_current_page_size', pageSize);
}
}
function update_pages(template, page_start){
stored_page_info(template, page_start);
switch (template) {
case 'queues' : renderQueues(); break;
case 'exchanges' : renderExchanges(); break;
case 'connections' : renderConnections(); break;
case 'channels' : renderChannels(); break;
}
}
function renderQueues() {
ensure_queues_chart_range();
render({'queues': {
path: url_pagination_template('queues', 1, 100),
options: {
sort: true,
vhost: true,
pagination: true
}
}, 'vhosts': '/vhosts'}, 'queues', '#/queues');
}
function renderExchanges() {
render({'exchanges': {path: url_pagination_template('exchanges', 1, 100),
options: {sort:true, vhost:true, pagination:true}},
'vhosts': '/vhosts'}, 'exchanges', '#/exchanges');
}
function renderConnections() {
render({'connections': {path: url_pagination_template('connections', 1, 100),
options: {sort:true}}},
'connections', '#/connections');
}
function renderChannels() {
render({'channels': {path: url_pagination_template('channels', 1, 100),
options: {sort:true}}},
'channels', '#/channels');
}
function update_pages_from_ui(sender) {
var val = $(sender).val();
var raw = !!$(sender).attr('data-page-start') ? $(sender).attr('data-page-start') : val;
var s = fmt_escape_html(fmt_strip_tags(raw));
update_pages(current_template, s);
}
function postprocess_partial() {
$('.pagination_class_input').on('keypress', function(e) {
if (e.keyCode == 13) {
update_pages_from_ui(this);
}
});
$('.pagination_class_checkbox').on('click', function(e) {
update_pages_from_ui(this);
});
$('.pagination_class_select').on('change', function(e) {
update_pages_from_ui(this);
});
setup_visibility();
$('#main').off('click', 'div.section h2, div.section-hidden h2');
$('#main').on('click', 'div.section h2, div.section-hidden h2', function() {
toggle_visibility($(this));
});
$('.sort').on('click', function() {
var sort = $(this).attr('sort');
if (current_sort == sort) {
current_sort_reverse = ! current_sort_reverse;
}
else {
current_sort = sort;
current_sort_reverse = false;
}
update();
});
// TODO remove this hack when we get rid of "updatable"
if ($('#filter-warning-show').length > 0) {
$('#filter-truncate').addClass('filter-warning');
}
else {
$('#filter-truncate').removeClass('filter-warning');
}
}
function update_multifields() {
$('div.multifield').each(function(index) {
update_multifield($(this), true);
});
}
function update_multifield(multifield, dict) {
var largest_id = 0;
var empty_found = false;
var name = multifield.attr('id');
var type_inputs = $('#' + name + ' *[name$="_mftype"]');
type_inputs.each(function(index) {
var re = new RegExp(name + '_([0-9]*)_mftype');
var match = $(this).attr('name').match(re);
if (!match) return;
var id = parseInt(match[1]);
largest_id = Math.max(id, largest_id);
var prefix = name + '_' + id;
var type = $(this).val();
var input = $('#' + prefix + '_mfvalue');
if (type == 'list') {
if (input.length == 1) {
input.replaceWith('<div class="multifield-sub" id="' + prefix +
'"></div>');
}
update_multifield($('#' + prefix), false);
}
else {
if (input.length == 1) {
var key = dict ? $('#' + prefix + '_mfkey').val() : '';
var value = input.val();
if (key == '' && value == '') {
if (index == type_inputs.length - 1) {
empty_found = true;
}
else {
$(this).parents('.mf').first().remove();
}
}
}
else {
$('#' + prefix).replaceWith(multifield_input(prefix, 'value',
'text'));
}
}
});
if (!empty_found) {
var prefix = name + '_' + (largest_id + 1);
var t = multifield.hasClass('string-only') ? 'hidden' : 'select';
var val_type = multifield_input(prefix, 'value', 'text') + ' ' +
multifield_input(prefix, 'type', t);
if (dict) {
multifield.append('<table class="mf"><tr><td>' +
multifield_input(prefix, 'key', 'text') +
'</td><td class="equals"> = </td><td>' +
val_type + '</td></tr></table>');
}
else {
multifield.append('<div class="mf">' + val_type + '</div>');
}
}
}
function multifield_input(prefix, suffix, type) {
if (type == 'hidden' ) {
return '<input type="hidden" id="' + prefix + '_mf' + suffix +
'" name="' + prefix + '_mf' + suffix + '" value="string"/>';
}
else if (type == 'text' ) {
return '<input type="text" id="' + prefix + '_mf' + suffix +
'" name="' + prefix + '_mf' + suffix + '" value=""/>';
}
else if (type == 'select' ) {
return '<select id="' + prefix + '_mf' + suffix + '" name="' + prefix +
'_mf' + suffix + '">' +
'<option value="string">String</option>' +
'<option value="number">Number</option>' +
'<option value="boolean">Boolean</option>' +
'<option value="list">List</option>' +
'</select>';
}
}
function update_filter_regex(jElem) {
current_filter_regex = null;
jElem.parents('.filter').children('.status-error').remove();
if (current_filter_regex_on && $.trim(current_filter).length > 0) {
try {
current_filter_regex = new RegExp(current_filter,'i');
} catch (e) {
jElem.parents('.filter').append('<p class="status-error">' +
fmt_escape_html(e.message) + '</p>');
}
}
}
function update_filter_regex_mode() {
current_filter_regex_on = $(this).is(':checked');
update_filter_regex($(this));
partial_update();
}
function update_filter() {
current_filter = $(this).val();
var table = $(this).parents('table').first();
table.removeClass('filter-active');
if ($(this).val() != '') {
table.addClass('filter-active');
}
update_filter_regex($(this));
partial_update();
}
function update_truncate() {
var current_truncate_str =
$(this).val().replace(new RegExp('\\D', 'g'), '');
if (current_truncate_str == '') {
current_truncate_str = '0';
}
if ($(this).val() != current_truncate_str) {
$(this).val(current_truncate_str);
}
var current_truncate = parseInt(current_truncate_str, 10);
store_pref('truncate', current_truncate);
partial_update();
}
function setup_visibility() {
$('div.section,div.section-hidden').each(function(_index) {
var pref = section_pref(current_template,
$(this).children('h2').text());
var show = get_pref(pref);
if (show == null) {
show = $(this).hasClass('section');
}
else {
show = show == 't';
}
if (show) {
$(this).addClass('section-visible');
// Workaround for... something. Although div.hider is
// display:block anyway, not explicitly setting this
// prevents the first slideToggle() from animating
// successfully; instead the element just vanishes.
$(this).find('.hider').attr('style', 'display:block;');
}
else {
$(this).addClass('section-invisible');
}
});
}
function toggle_visibility(item) {
var hider = item.next();
var all = item.parent();
var pref = section_pref(current_template, item.text());
item.next().slideToggle(100);
if (all.hasClass('section-visible')) {
if (all.hasClass('section'))
store_pref(pref, 'f');
else
clear_pref(pref);
all.removeClass('section-visible');
all.addClass('section-invisible');
}
else {
if (all.hasClass('section-hidden')) {
store_pref(pref, 't');
} else {
clear_pref(pref);
}
all.removeClass('section-invisible');
all.addClass('section-visible');
}
}
function publish_msg(params0) {
try {
var params = params_magic(params0);
publish_msg0(params);
} catch (e) {
show_popup('warn', fmt_escape_html(e));
return false;
}
}
function publish_msg0(params) {
var path = fill_path_template('/exchanges/:vhost/:name/publish', params);
params['payload_encoding'] = 'string';
params['properties'] = {};
params['properties']['delivery_mode'] = parseInt(params['delivery_mode']);
if (params['headers'] != '')
params['properties']['headers'] = params['headers'];
var props = [['content_type', 'str'],
['content_encoding', 'str'],
['correlation_id', 'str'],
['reply_to', 'str'],
['expiration', 'str'],
['message_id', 'str'],
['type', 'str'],
['user_id', 'str'],
['app_id', 'str'],
['cluster_id', 'str'],
['priority', 'int'],
['timestamp', 'int']];
for (var i in props) {
var name = props[i][0];
var type = props[i][1];
if (params['props'][name] != undefined && params['props'][name] != '') {
var value = params['props'][name];
if (type == 'int') value = parseInt(value);
params['properties'][name] = value;
}
}
with_req('POST', path, JSON.stringify(params), function(resp) {
var result = JSON.parse(resp.responseText);
if (result.routed) {
show_popup('info', 'Message published.');
} else {
show_popup('warn', 'Message published, but not routed.');
}
});
}
function get_msgs(params) {
var path = fill_path_template('/queues/:vhost/:name/get', params);
with_req('POST', path, JSON.stringify(params), function(resp) {
var msgs = JSON.parse(resp.responseText);
if (msgs.length == 0) {
show_popup('info', 'Queue is empty');
} else {
$('#msg-wrapper').slideUp(200);
replace_content('msg-wrapper', format('messages', {'msgs': msgs}));
$('#msg-wrapper').slideDown(200);
}
});
}
function with_reqs(reqs, acc, fun) {
if (keys(reqs).length > 0) {
var key = keys(reqs)[0];
with_req('GET', reqs[key], null, function(resp) {
acc[key] = JSON.parse(resp.responseText);
var remainder = {};
for (var k in reqs) {
if (k != key) remainder[k] = reqs[k];
}
with_reqs(remainder, acc, fun);
});
}
else {
fun(acc);
}
}
function replace_content(id, html) {
$("#" + id).html(html);
}
var ejs_cached = {};
function format(template, json) {
try {
var cache = true;
if (!(template in ejs_cached)) {
ejs_cached[template] = true;
cache = false;
}
var tmpl = new EJS({url: 'js/tmpl/' + template + '.ejs', cache: cache});
return tmpl.render(json);
} catch (err) {
clearInterval(timer);
console.log("Uncaught error: " + err);
console.log("Stack: " + err['stack']);
debug(err['name'] + ": " + err['message'] + "\n" + err['stack'] + "\n");
}
}
function update_status(status) {
var text;
if (status == 'ok')
text = "Refreshed " + fmt_date(new Date());
else if (status == 'error') {
var next_try = new Date(new Date().getTime() + timer_interval);
text = "Error: could not connect to server since " +
fmt_date(last_successful_connect) + ". Will retry at " +
fmt_date(next_try) + ".";
}
else
throw("Unknown status " + status);
var html = format('status', {status: status, text: text});
replace_content('status', html);
}
function has_auth_cookie_value() {
return get_cookie_value('auth') != null;
}
function auth_header() {
if(has_auth_cookie_value()) {
return "Basic " + decodeURIComponent(get_cookie_value('auth'));
} else {
return null;
}
}
function with_req(method, path, body, fun) {
if(!has_auth_cookie_value()) {
// navigate to the login form
location.reload();
return;
}
var json;
var req = xmlHttpRequest();
req.open(method, 'api' + path, true );
req.setRequestHeader('authorization', auth_header());
req.setRequestHeader('x-vhost', current_vhost);
req.onreadystatechange = function () {
if (req.readyState == 4) {
var ix = jQuery.inArray(req, outstanding_reqs);
if (ix != -1) {
outstanding_reqs.splice(ix, 1);
}
if (check_bad_response(req, true)) {
last_successful_connect = new Date();
fun(req);
}
}
};
outstanding_reqs.push(req);
req.send(body);
}
function sync_get(path) {
return sync_req('GET', [], path);
}
function sync_put(sammy, path_template) {
return sync_req('PUT', sammy.params, path_template);
}
function sync_delete(sammy, path_template, options) {
return sync_req('DELETE', sammy.params, path_template, options);
}
function sync_post(sammy, path_template) {
return sync_req('POST', sammy.params, path_template);
}
function sync_req(type, params0, path_template, options) {
var params;
var path;
try {
params = params_magic(params0);
path = fill_path_template(path_template, params);
} catch (e) {
show_popup('warn', fmt_escape_html(e));
return false;
}
var req = xmlHttpRequest();
req.open(type, 'api' + path, false);
req.setRequestHeader('content-type', 'application/json');
req.setRequestHeader('authorization', auth_header());
if (options != undefined || options != null) {
if (options.headers != undefined || options.headers != null) {
jQuery.each(options.headers, function (k, v) {
req.setRequestHeader(k, v);
});
}
}
try {
if (type == 'GET')
req.send(null);
else
req.send(JSON.stringify(params));
}
catch (e) {
if (e.number == 0x80004004) {
// 0x80004004 means "Operation aborted."
// https://support.microsoft.com/kb/186063
// MSIE6 appears to do this in response to HTTP 204.
}
}
if (check_bad_response(req, false)) {
if (type == 'GET')
return req.responseText;
else
// rabbitmq/rabbitmq-management#732
// https://developer.mozilla.org/en-US/docs/Glossary/Truthy
return {result: true, http_status: req.status, req_params: params};
}
else {
return false;
}
}
function check_bad_response(req, full_page_404) {
// 1223 == 204 - see https://www.enhanceie.com/ie/bugs.asp
// MSIE7 and 8 appear to do this in response to HTTP 204.
if ((req.status >= 200 && req.status < 300) || req.status == 1223) {
return true;
}
else if (req.status == 404 && full_page_404) {
var html = format('404', {});
replace_content('main', html);
}
else if (req.status >= 400 && req.status <= 404) {
var reason = JSON.parse(req.responseText).reason;
if (typeof(reason) != 'string') reason = JSON.stringify(reason);
var error = JSON.parse(req.responseText).error;
if (typeof(error) != 'string') error = JSON.stringify(error);
if (error == 'bad_request' || error == 'not_found' || error == 'not_authorised') {
show_popup('warn', fmt_escape_html(reason));
} else if (error == 'page_out_of_range') {
var seconds = 60;
if (last_page_out_of_range_error > 0)
seconds = (new Date().getTime() - last_page_out_of_range_error.getTime())/1000;
if (seconds > 3) {
Sammy.log('server reports page is out of range, redirecting to page 1');
var contexts = ["queues", "exchanges", "connections", "channels"];
var matches = /api\/(.*)\?/.exec(req.responseURL);
if (matches != null && matches.length > 1) {
contexts.forEach(function(item) {
if (matches[1].indexOf(item) == 0) {update_pages(item, 1)};
});
} else update_pages(current_template, 1);
last_page_out_of_range_error = new Date();
}
}
}
else if (req.status == 408) {
update_status('timeout');
}
else if (req.status == 0) { // Non-MSIE: could not connect
update_status('error');
}
else if (req.status > 12000) { // MSIE: could not connect
update_status('error');
}
else if (req.status == 503) { // Proxy: could not connect
update_status('error');
}
else {
debug("Management API returned status code " + req.status + " - <strong>" + fmt_escape_html_one_line(req.responseText) + "</strong>");
clearInterval(timer);
}
return false;
}
function fill_path_template(template, params) {
var re = /:[a-zA-Z_]*/g;
return template.replace(re, function(m) {
var str = esc(params[m.substring(1)]);
if (str == '') {
throw(m.substring(1) + " is required");
}
return str;
});
}
function params_magic(params) {
return check_password(maybe_remove_fields(collapse_multifields(params)));
}
function collapse_multifields(params0) {
function set(x) { return x != '' && x != undefined }
var params = {};
var ks = keys(params0);
var ids = [];
for (i in ks) {
var key = ks[i];
var match = key.match(/([a-z]*)_([0-9_]*)_mftype/);
var match2 = key.match(/[a-z]*_[0-9_]*_mfkey/);
var match3 = key.match(/[a-z]*_[0-9_]*_mfvalue/);
if (match == null && match2 == null && match3 == null) {
params[key] = params0[key];
}
else if (match == null) {
// Do nothing, value is handled below
}
else {
var name = match[1];
var id = match[2];
ids.push([name, id]);
}
}
ids.sort();
var id_map = {};
for (i in ids) {
var name = ids[i][0];
var id = ids[i][1];
if (params[name] == undefined) {
params[name] = {};
id_map[name] = {};
}
var id_parts = id.split('_');
var k = params0[name + '_' + id_parts[0] + '_mfkey'];
var v = params0[name + '_' + id + '_mfvalue'];
var t = params0[name + '_' + id + '_mftype'];
var val = null;
var top_level = id_parts.length == 1;
if (t == 'list') {
val = [];
id_map[name][id] = val;
}
else if ((set(k) && top_level) || set(v)) {
if (t == 'boolean') {
if (v != 'true' && v != 'false')
throw(k + ' must be "true" or "false"; got ' + v);
val = (v == 'true');
}
else if (t == 'number') {
var n = parseFloat(v);
if (isNaN(n))
throw(k + ' must be a number; got ' + v);
val = n;
}
else {
val = v;
}
}
if (val != null) {
if (top_level) {
params[name][k] = val;
}
else {
var prefix = id_parts.slice(0, id_parts.length - 1).join('_');
id_map[name][prefix].push(val);
}
}
}
return params;
}
function check_password(params) {
if (params['password'] != undefined) {
if (params['password'] == '') {
throw("Please specify a password.");
}
if (params['password'] != params['password_confirm']) {
throw("Passwords do not match.");
}
delete params['password_confirm'];
}
return params;
}
function maybe_remove_fields(params) {
$('.controls-appearance').each(function(index) {
var options = $(this).get(0).options;
var selected = $(this).val();
for (i = 0; i < options.length; i++) {
var option = options[i].value;
if (option != selected) {
delete params[option];
}
}
delete params[$(this).attr('name')];
});
return params;
}
function put_parameter(sammy, mandatory_keys, num_keys, bool_keys,
arrayable_keys) {
for (var i in sammy.params) {
if (i === 'length' || !sammy.params.hasOwnProperty(i)) continue;
if (sammy.params[i] == '' && jQuery.inArray(i, mandatory_keys) == -1) {
delete sammy.params[i];
}
else if (jQuery.inArray(i, num_keys) != -1) {
sammy.params[i] = parseInt(sammy.params[i]);
}
else if (jQuery.inArray(i, bool_keys) != -1) {
sammy.params[i] = sammy.params[i] == 'true';
}
else if (jQuery.inArray(i, arrayable_keys) != -1) {
sammy.params[i] = sammy.params[i].split(' ');
if (sammy.params[i].length == 1) {
sammy.params[i] = sammy.params[i][0];
}
}
}
var params = {"component": sammy.params.component,
"vhost": sammy.params.vhost,
"name": sammy.params.name,
"value": params_magic(sammy.params)};
delete params.value.vhost;
delete params.value.component;
delete params.value.name;
sammy.params = params;
if (sync_put(sammy, '/parameters/:component/:vhost/:name')) update();
}
function put_cast_params(sammy, path, mandatory_keys, num_keys, bool_keys) {
for (var i in sammy.params) {
if (i === 'length' || !sammy.params.hasOwnProperty(i)) continue;
if (sammy.params[i] == '' && jQuery.inArray(i, mandatory_keys) == -1) {
delete sammy.params[i];
}
else if (jQuery.inArray(i, num_keys) != -1) {
sammy.params[i] = parseInt(sammy.params[i]);
}
else if (jQuery.inArray(i, bool_keys) != -1) {
sammy.params[i] = sammy.params[i] == 'true';
}
}
if (sync_put(sammy, path)) update();
}
function update_column_options(sammy) {
var mode = sammy.params['mode'];
for (var group in COLUMNS[mode]) {
var options = COLUMNS[mode][group];
for (var i = 0; i < options.length; i++) {
var key = options[i][0];
var value = sammy.params[mode + '-' + key] != undefined;
store_pref('column-' + mode + '-' + key, value);
}
}
partial_update();
}
function debug(str) {
$('<p>' + str + '</p>').appendTo('#debug');
}
function keys(obj) {
var ks = [];
for (var k in obj) {
ks.push(k);
}
return ks;
}
// Don't use the jQuery AJAX support, it seems to have trouble reporting
// server-down type errors.
function xmlHttpRequest() {
var res;
try {
res = new XMLHttpRequest();
}
catch(e) {
res = new ActiveXObject("Microsoft.XMLHttp");
}
return res;
}
// Our base64 library takes a string that is really a byte sequence,
// and will throw if given a string with chars > 255 (and hence not
// DTRT for chars > 127). So encode a unicode string as a UTF-8
// sequence of "bytes".
function b64_encode_utf8(str) {
return base64.encode(encode_utf8(str));
}
// encodeURIComponent handles utf-8, unescape does not. Neat!
function encode_utf8(str) {
return unescape(encodeURIComponent(str));
}
(function($){
$.fn.extend({
center: function () {
return this.each(function() {
var top = ($(window).height() - $(this).outerHeight()) / 2;
var left = ($(window).width() - $(this).outerWidth()) / 2;
$(this).css({margin:0, top: (top > 0 ? top : 0)+'px', left: (left > 0 ? left : 0)+'px'});
});
}
});
})(jQuery);
function debounce(f, delay) {
var timeout = null;
return function() {
var obj = this;
var args = arguments;
function delayed () {
f.apply(obj, args);
timeout = null;
}
if (timeout) clearTimeout(timeout);
timeout = setTimeout(delayed, delay);
}
}
function rename_multifield(params, from, to) {
var new_params = {};
for(var key in params){
var match = key.match("^" + from + "_[0-9_]*_mftype$");
var match2 = key.match("^" + from + "_[0-9_]*_mfkey$");
var match3 = key.match("^" + from + "_[0-9_]*_mfvalue$");
if (match != null) {
new_params[match[0].replace(from, to)] = params[match];
}
else if (match2 != null) {
new_params[match2[0].replace(from, to)] = params[match2];
}
else if (match3 != null) {
new_params[match3[0].replace(from, to)] = params[match3];
}
else {
new_params[key] = params[key]
}
}
return new_params;
}
function ensure_queues_chart_range() {
var range = get_pref('chart-range');
// Note: the queues page uses the 'basic' range type
var fixup_range;
var valid_range = false;
var range_type = get_chart_range_type('queues');
var chart_periods = CHART_RANGES[range_type];
for (var i = 0; i < chart_periods.length; ++i) {
var data = chart_periods[i];
var val = data[0];
if (range === val) {
valid_range = true;
break;
}
// If the range needs to be adjusted, use the last
// valid one
fixup_range = val;
}
if (!valid_range) {
store_pref('chart-range', fixup_range);
}
}
function get_chart_range_type(arg) {
/*
* 'arg' can be:
* lengths-over for the Overview page
* lengths-q for the per-queue page
* queues for setting up the queues range
*/
if (arg === 'lengths-over') {
return 'global';
}
if (arg === 'msg-rates-over') {
return 'global';
}
if (arg === 'lengths-q') {
return 'basic';
}
if (arg === 'msg-rates-q') {
return 'basic';
}
if (arg === 'queues') {
return 'basic';
}
if (arg === 'queue-churn') {
return 'basic';
}
if (arg === 'channel-churn') {
return 'basic';
}
if (arg === 'connection-churn') {
return 'basic';
}
console.log('[WARNING]: range type not found for arg: ' + arg);
return 'basic';
}
function local_storage_available() {
try {
return 'localStorage' in window && window['localStorage'] !== null;
} catch (e) {
return false;
}
}
function store_cookie_value(k, v) {
var d = parse_cookie();
d[short_key(k)] = v;
store_cookie(d);
}
function store_cookie_value_with_expiration(k, v, expiration_date) {
var d = parse_cookie();
d[short_key(k)] = v;
store_cookie_with_expiration(d, expiration_date);
}
function clear_cookie_value(k) {
var d = parse_cookie();
delete d[short_key(k)];
store_cookie(d);
}
function get_cookie_value(k) {
var r;
r = parse_cookie()[short_key(k)];
return r == undefined ? default_pref(k) : r;
}
function store_pref(k, v) {
if (local_storage_available()) {
window.localStorage['rabbitmq.' + k] = v;
}
else {
var d = parse_cookie();
d[short_key(k)] = v;
store_cookie(d);
}
}
function clear_pref(k) {
if (local_storage_available()) {
window.localStorage.removeItem('rabbitmq.' + k);
}
else {
var d = parse_cookie();
delete d[short_key(k)];
store_cookie(d);
}
}
function clear_local_pref(k) {
if (local_storage_available()) {
window.localStorage.removeItem('rabbitmq.' + k);
}
}
function get_pref(k) {
var val;
if (local_storage_available()) {
val = window.localStorage['rabbitmq.' + k];
}
else {
val = parse_cookie()[short_key(k)];
}
var res = (val == undefined) ? default_pref(k) : val;
return res;
}
function section_pref(template, name) {
return 'visible|' + template + '|' + name;
}
function show_column(mode, column) {
return get_pref('column-' + mode + '-' + column) == 'true';
}
// ---------------------------------------------------------------------------
function default_pref(k) {
if (k.substring(0, 11) == 'chart-size-') return 'small';
if (k.substring(0, 10) == 'rate-mode-') return 'chart';
if (k.substring(0, 11) == 'chart-line-') return 'true';
if (k == 'truncate') return '100';
if (k == 'chart-range') return '60|5';
if (k.substring(0, 7) == 'column-')
return default_column_pref(k.substring(7));
return null;
}
function default_column_pref(key0) {
var ix = key0.indexOf('-');
var mode = key0.substring(0, ix);
var key = key0.substring(ix + 1);
for (var group in COLUMNS[mode]) {
var options = COLUMNS[mode][group];
for (var i = 0; i < options.length; i++) {
if (options[i][0] == key) {
return '' + options[i][2];
}
}
}
return 'false';
}
// ---------------------------------------------------------------------------
function parse_cookie() {
var c = get_cookie('m');
var items = c.length == 0 ? [] : c.split('|');
var start = 0;
var dict = {};
for (var i in items) {
var kv = items[i].split(':');
dict[kv[0]] = unescape(kv[1]);
}
return dict;
}
function store_cookie(dict) {
var date = new Date();
date.setFullYear(date.getFullYear() + 1);
store_cookie_with_expiration(dict, date);
}
function store_cookie_with_expiration(dict, expiration_date) {
var enc = [];
for (var k in dict) {
enc.push(k + ':' + escape(dict[k]));
}
document.cookie = 'm=' + enc.join('|') + '; expires=' + expiration_date.toUTCString();
}
function get_cookie(key) {
var cookies = document.cookie.split(';');
for (var i in cookies) {
var kv = jQuery.trim(cookies[i]).split('=');
if (kv[0] == key) return kv[1];
}
return '';
}
// Try to economise on space since cookies have limited length.
function short_key(k) {
var res = Math.abs(k.hashCode() << 16 >> 16);
res = res.toString(16);
return res;
}
String.prototype.hashCode = function() {
var hash = 0;
if (this.length == 0) return code;
for (i = 0; i < this.length; i++) {
char = this.charCodeAt(i);
hash = 31*hash+char;
hash = hash & hash; // Convert to 32bit integer
}
return hash;
}
// name: sammy
// version: 0.7.6
// Sammy.js / http://sammyjs.org
(function(factory){
// Support module loading scenarios
if (typeof define === 'function' && define.amd){
// AMD Anonymous Module
define(['jquery'], factory);
} else {
// No module loader (plain <script> tag) - put directly in global namespace
jQuery.sammy = window.Sammy = factory(jQuery);
}
})(function($){
var Sammy,
PATH_REPLACER = "([^\/]+)",
PATH_NAME_MATCHER = /:([\w\d]+)/g,
QUERY_STRING_MATCHER = /\?([^#]*)?$/,
// mainly for making `arguments` an Array
_makeArray = function(nonarray) { return Array.prototype.slice.call(nonarray); },
// borrowed from jQuery
_isFunction = function( obj ) { return Object.prototype.toString.call(obj) === "[object Function]"; },
_isArray = function( obj ) { return Object.prototype.toString.call(obj) === "[object Array]"; },
_isRegExp = function( obj ) { return Object.prototype.toString.call(obj) === "[object RegExp]"; },
_decode = function( str ) { return decodeURIComponent((str || '').replace(/\+/g, ' ')); },
_encode = encodeURIComponent,
_escapeHTML = function(s) {
return String(s).replace(/&(?!\w+;)/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
},
_routeWrapper = function(verb) {
return function() {
return this.route.apply(this, [verb].concat(Array.prototype.slice.call(arguments)));
};
},
_template_cache = {},
_has_history = !!(window.history && history.pushState),
loggers = [];
// `Sammy` (also aliased as $.sammy) is not only the namespace for a
// number of prototypes, its also a top level method that allows for easy
// creation/management of `Sammy.Application` instances. There are a
// number of different forms for `Sammy()` but each returns an instance
// of `Sammy.Application`. When a new instance is created using
// `Sammy` it is added to an Object called `Sammy.apps`. This
// provides for an easy way to get at existing Sammy applications. Only one
// instance is allowed per `element_selector` so when calling
// `Sammy('selector')` multiple times, the first time will create
// the application and the following times will extend the application
// already added to that selector.
//
// ### Example
//
// // returns the app at #main or a new app
// Sammy('#main')
//
// // equivalent to "new Sammy.Application", except appends to apps
// Sammy();
// Sammy(function() { ... });
//
// // extends the app at '#main' with function.
// Sammy('#main', function() { ... });
//
Sammy = function() {
var args = _makeArray(arguments),
app, selector;
Sammy.apps = Sammy.apps || {};
if (args.length === 0 || args[0] && _isFunction(args[0])) { // Sammy()
return Sammy.apply(Sammy, ['body'].concat(args));
} else if (typeof (selector = args.shift()) == 'string') { // Sammy('#main')
app = Sammy.apps[selector] || new Sammy.Application();
app.element_selector = selector;
if (args.length > 0) {
$.each(args, function(i, plugin) {
app.use(plugin);
});
}
// if the selector changes make sure the reference in Sammy.apps changes
if (app.element_selector != selector) {
delete Sammy.apps[selector];
}
Sammy.apps[app.element_selector] = app;
return app;
}
};
Sammy.VERSION = '0.7.6';
// Add to the global logger pool. Takes a function that accepts an
// unknown number of arguments and should print them or send them somewhere
// The first argument is always a timestamp.
Sammy.addLogger = function(logger) {
loggers.push(logger);
};
// Sends a log message to each logger listed in the global
// loggers pool. Can take any number of arguments.
// Also prefixes the arguments with a timestamp.
Sammy.log = function() {
var args = _makeArray(arguments);
args.unshift("[" + Date() + "]");
$.each(loggers, function(i, logger) {
logger.apply(Sammy, args);
});
};
if (typeof window.console != 'undefined') {
if (typeof window.console.log === 'function' && _isFunction(window.console.log.apply)) {
Sammy.addLogger(function() {
window.console.log.apply(window.console, arguments);
});
} else {
Sammy.addLogger(function() {
window.console.log(arguments);
});
}
} else if (typeof console != 'undefined') {
Sammy.addLogger(function() {
console.log.apply(console, arguments);
});
}
$.extend(Sammy, {
makeArray: _makeArray,
isFunction: _isFunction,
isArray: _isArray
});
// Sammy.Object is the base for all other Sammy classes. It provides some useful
// functionality, including cloning, iterating, etc.
Sammy.Object = function(obj) { // constructor
return $.extend(this, obj || {});
};
$.extend(Sammy.Object.prototype, {
// Escape HTML in string, use in templates to prevent script injection.
// Also aliased as `h()`
escapeHTML: _escapeHTML,
h: _escapeHTML,
// Returns a copy of the object with Functions removed.
toHash: function() {
var json = {};
$.each(this, function(k,v) {
if (!_isFunction(v)) {
json[k] = v;
}
});
return json;
},
// Renders a simple HTML version of this Objects attributes.
// Does not render functions.
// For example. Given this Sammy.Object:
//
// var s = new Sammy.Object({first_name: 'Sammy', last_name: 'Davis Jr.'});
// s.toHTML()
// //=> '<strong>first_name</strong> Sammy<br /><strong>last_name</strong> Davis Jr.<br />'
//
toHTML: function() {
var display = "";
$.each(this, function(k, v) {
if (!_isFunction(v)) {
display += "<strong>" + k + "</strong> " + v + "<br />";
}
});
return display;
},
// Returns an array of keys for this object. If `attributes_only`
// is true will not return keys that map to a `function()`
keys: function(attributes_only) {
var keys = [];
for (var property in this) {
if (!_isFunction(this[property]) || !attributes_only) {
keys.push(property);
}
}
return keys;
},
// Checks if the object has a value at `key` and that the value is not empty
has: function(key) {
return this[key] && $.trim(this[key].toString()) !== '';
},
// convenience method to join as many arguments as you want
// by the first argument - useful for making paths
join: function() {
var args = _makeArray(arguments);
var delimiter = args.shift();
return args.join(delimiter);
},
// Shortcut to Sammy.log
log: function() {
Sammy.log.apply(Sammy, arguments);
},
// Returns a string representation of this object.
// if `include_functions` is true, it will also toString() the
// methods of this object. By default only prints the attributes.
toString: function(include_functions) {
var s = [];
$.each(this, function(k, v) {
if (!_isFunction(v) || include_functions) {
s.push('"' + k + '": ' + v.toString());
}
});
return "Sammy.Object: {" + s.join(',') + "}";
}
});
// Return whether the event targets this window.
Sammy.targetIsThisWindow = function targetIsThisWindow(event, tagName) {
var targetElement = $(event.target).closest(tagName);
if (targetElement.length === 0) { return true; }
var targetWindow = targetElement.attr('target');
if (!targetWindow || targetWindow === window.name || targetWindow === '_self') { return true; }
if (targetWindow === '_blank') { return false; }
if (targetWindow === 'top' && window === window.top) { return true; }
return false;
};
// The DefaultLocationProxy is the default location proxy for all Sammy applications.
// A location proxy is a prototype that conforms to a simple interface. The purpose
// of a location proxy is to notify the Sammy.Application its bound to when the location
// or 'external state' changes.
//
// The `DefaultLocationProxy` watches for changes to the path of the current window and
// is also able to set the path based on changes in the application. It does this by
// using different methods depending on what is available in the current browser. In
// the latest and greatest browsers it used the HTML5 History API and the `pushState`
// `popState` events/methods. This allows you to use Sammy to serve a site behind normal
// URI paths as opposed to the older default of hash (#) based routing. Because the server
// can interpret the changed path on a refresh or re-entry, though, it requires additional
// support on the server side. If you'd like to force disable HTML5 history support, please
// use the `disable_push_state` setting on `Sammy.Application`. If pushState support
// is enabled, `DefaultLocationProxy` also binds to all links on the page. If a link is clicked
// that matches the current set of routes, the URL is changed using pushState instead of
// fully setting the location and the app is notified of the change.
//
// If the browser does not have support for HTML5 History, `DefaultLocationProxy` automatically
// falls back to the older hash based routing. The newest browsers (IE, Safari > 4, FF >= 3.6)
// support a 'onhashchange' DOM event, thats fired whenever the location.hash changes.
// In this situation the DefaultLocationProxy just binds to this event and delegates it to
// the application. In the case of older browsers a poller is set up to track changes to the
// hash.
Sammy.DefaultLocationProxy = function(app, run_interval_every) {
this.app = app;
// set is native to false and start the poller immediately
this.is_native = false;
this.has_history = _has_history;
this._startPolling(run_interval_every);
};
Sammy.DefaultLocationProxy.fullPath = function(location_obj) {
// Bypass the `window.location.hash` attribute. If a question mark
// appears in the hash IE6 will strip it and all of the following
// characters from `window.location.hash`.
var matches = location_obj.toString().match(/^[^#]*(#.+)$/);
var hash = matches ? matches[1] : '';
return [location_obj.pathname, location_obj.search, hash].join('');
};
$.extend(Sammy.DefaultLocationProxy.prototype , {
// bind the proxy events to the current app.
bind: function() {
var proxy = this, app = this.app, lp = Sammy.DefaultLocationProxy;
$(window).bind('hashchange.' + this.app.eventNamespace(), function(e, non_native) {
// if we receive a native hash change event, set the proxy accordingly
// and stop polling
if (proxy.is_native === false && !non_native) {
proxy.is_native = true;
window.clearInterval(lp._interval);
lp._interval = null;
}
app.trigger('location-changed');
});
if (_has_history && !app.disable_push_state) {
// bind to popstate
$(window).bind('popstate.' + this.app.eventNamespace(), function(e) {
app.trigger('location-changed');
});
// bind to link clicks that have routes
$(document).delegate('a', 'click.history-' + this.app.eventNamespace(), function (e) {
if (e.isDefaultPrevented() || e.metaKey || e.ctrlKey) {
return;
}
var full_path = lp.fullPath(this),
// Get anchor's host name in a cross browser compatible way.
// IE looses hostname property when setting href in JS
// with a relative URL, e.g. a.setAttribute('href',"/whatever").
// Circumvent this problem by creating a new link with given URL and
// querying that for a hostname.
hostname = this.hostname ? this.hostname : function (a) {
var l = document.createElement("a");
l.href = a.href;
return l.hostname;
}(this);
if (hostname == window.location.hostname &&
app.lookupRoute('get', full_path) &&
Sammy.targetIsThisWindow(e, 'a')) {
e.preventDefault();
proxy.setLocation(full_path);
return false;
}
});
}
if (!lp._bindings) {
lp._bindings = 0;
}
lp._bindings++;
},
// unbind the proxy events from the current app
unbind: function() {
$(window).unbind('hashchange.' + this.app.eventNamespace());
$(window).unbind('popstate.' + this.app.eventNamespace());
$(document).undelegate('a', 'click.history-' + this.app.eventNamespace());
Sammy.DefaultLocationProxy._bindings--;
if (Sammy.DefaultLocationProxy._bindings <= 0) {
window.clearInterval(Sammy.DefaultLocationProxy._interval);
Sammy.DefaultLocationProxy._interval = null;
}
},
// get the current location from the hash.
getLocation: function() {
return Sammy.DefaultLocationProxy.fullPath(window.location);
},
// set the current location to `new_location`
setLocation: function(new_location) {
if (/^([^#\/]|$)/.test(new_location)) { // non-prefixed url
if (_has_history && !this.app.disable_push_state) {
new_location = '/' + new_location;
} else {
new_location = '#!/' + new_location;
}
}
if (new_location != this.getLocation()) {
// HTML5 History exists and new_location is a full path
if (_has_history && !this.app.disable_push_state && /^\//.test(new_location)) {
history.pushState({ path: new_location }, window.title, new_location);
this.app.trigger('location-changed');
} else {
return (window.location = new_location);
}
}
},
_startPolling: function(every) {
// set up interval
var proxy = this;
if (!Sammy.DefaultLocationProxy._interval) {
if (!every) { every = 10; }
var hashCheck = function() {
var current_location = proxy.getLocation();
if (typeof Sammy.DefaultLocationProxy._last_location == 'undefined' ||
current_location != Sammy.DefaultLocationProxy._last_location) {
window.setTimeout(function() {
$(window).trigger('hashchange', [true]);
}, 0);
}
Sammy.DefaultLocationProxy._last_location = current_location;
};
hashCheck();
Sammy.DefaultLocationProxy._interval = window.setInterval(hashCheck, every);
}
}
});
// Sammy.Application is the Base prototype for defining 'applications'.
// An 'application' is a collection of 'routes' and bound events that is
// attached to an element when `run()` is called.
// The only argument an 'app_function' is evaluated within the context of the application.
Sammy.Application = function(app_function) {
var app = this;
this.routes = {};
this.listeners = new Sammy.Object({});
this.arounds = [];
this.befores = [];
// generate a unique namespace
this.namespace = (new Date()).getTime() + '-' + parseInt(Math.random() * 1000, 10);
this.context_prototype = function() { Sammy.EventContext.apply(this, arguments); };
this.context_prototype.prototype = new Sammy.EventContext();
if (_isFunction(app_function)) {
app_function.apply(this, [this]);
}
// set the location proxy if not defined to the default (DefaultLocationProxy)
if (!this._location_proxy) {
this.setLocationProxy(new Sammy.DefaultLocationProxy(this, this.run_interval_every));
}
if (this.debug) {
this.bindToAllEvents(function(e, data) {
app.log(app.toString(), e.cleaned_type, data || {});
});
}
};
Sammy.Application.prototype = $.extend({}, Sammy.Object.prototype, {
// the four route verbs
ROUTE_VERBS: ['get','post','put','delete'],
// An array of the default events triggered by the
// application during its lifecycle
APP_EVENTS: ['run', 'unload', 'lookup-route', 'run-route', 'route-found', 'event-context-before', 'event-context-after', 'changed', 'error', 'check-form-submission', 'redirect', 'location-changed'],
_last_route: null,
_location_proxy: null,
_running: false,
// Defines what element the application is bound to. Provide a selector
// (parseable by `jQuery()`) and this will be used by `$element()`
element_selector: 'body',
// When set to true, logs all of the default events using `log()`
debug: false,
// When set to true, and the error() handler is not overridden, will actually
// raise JS errors in routes (500) and when routes can't be found (404)
raise_errors: false,
// The time in milliseconds that the URL is queried for changes
run_interval_every: 50,
// if using the `DefaultLocationProxy` setting this to true will force the app to use
// traditional hash based routing as opposed to the new HTML5 PushState support
disable_push_state: false,
// The default template engine to use when using `partial()` in an
// `EventContext`. `template_engine` can either be a string that
// corresponds to the name of a method/helper on EventContext or it can be a function
// that takes two arguments, the content of the unrendered partial and an optional
// JS object that contains interpolation data. Template engine is only called/referred
// to if the extension of the partial is null or unknown. See `partial()`
// for more information
template_engine: null,
// //=> Sammy.Application: body
toString: function() {
return 'Sammy.Application:' + this.element_selector;
},
// returns a jQuery object of the Applications bound element.
$element: function(selector) {
return selector ? $(this.element_selector).find(selector) : $(this.element_selector);
},
// `use()` is the entry point for including Sammy plugins.
// The first argument to use should be a function() that is evaluated
// in the context of the current application, just like the `app_function`
// argument to the `Sammy.Application` constructor.
//
// Any additional arguments are passed to the app function sequentially.
//
// For much more detail about plugins, check out:
// [http://sammyjs.org/docs/plugins](http://sammyjs.org/docs/plugins)
//
// ### Example
//
// var MyPlugin = function(app, prepend) {
//
// this.helpers({
// myhelper: function(text) {
// alert(prepend + " " + text);
// }
// });
//
// };
//
// var app = $.sammy(function() {
//
// this.use(MyPlugin, 'This is my plugin');
//
// this.get('#/', function() {
// this.myhelper('and dont you forget it!');
// //=> Alerts: This is my plugin and dont you forget it!
// });
//
// });
//
// If plugin is passed as a string it assumes your are trying to load
// Sammy."Plugin". This is the preferred way of loading core Sammy plugins
// as it allows for better error-messaging.
//
// ### Example
//
// $.sammy(function() {
// this.use('Mustache'); //=> Sammy.Mustache
// this.use('Storage'); //=> Sammy.Storage
// });
//
use: function() {
// flatten the arguments
var args = _makeArray(arguments),
plugin = args.shift(),
plugin_name = plugin || '';
try {
args.unshift(this);
if (typeof plugin == 'string') {
plugin_name = 'Sammy.' + plugin;
plugin = Sammy[plugin];
}
plugin.apply(this, args);
} catch(e) {
if (typeof plugin === 'undefined') {
this.error("Plugin Error: called use() but plugin (" + plugin_name.toString() + ") is not defined", e);
} else if (!_isFunction(plugin)) {
this.error("Plugin Error: called use() but '" + plugin_name.toString() + "' is not a function", e);
} else {
this.error("Plugin Error", e);
}
}
return this;
},
// Sets the location proxy for the current app. By default this is set to
// a new `Sammy.DefaultLocationProxy` on initialization. However, you can set
// the location_proxy inside you're app function to give your app a custom
// location mechanism. See `Sammy.DefaultLocationProxy` and `Sammy.DataLocationProxy`
// for examples.
//
// `setLocationProxy()` takes an initialized location proxy.
//
// ### Example
//
// // to bind to data instead of the default hash;
// var app = $.sammy(function() {
// this.setLocationProxy(new Sammy.DataLocationProxy(this));
// });
//
setLocationProxy: function(new_proxy) {
var original_proxy = this._location_proxy;
this._location_proxy = new_proxy;
if (this.isRunning()) {
if (original_proxy) {
// if there is already a location proxy, unbind it.
original_proxy.unbind();
}
this._location_proxy.bind();
}
},
// provide log() override for inside an app that includes the relevant application element_selector
log: function() {
Sammy.log.apply(Sammy, Array.prototype.concat.apply([this.element_selector],arguments));
},
// `route()` is the main method for defining routes within an application.
// For great detail on routes, check out:
// [http://sammyjs.org/docs/routes](http://sammyjs.org/docs/routes)
//
// This method also has aliases for each of the different verbs (eg. `get()`, `post()`, etc.)
//
// ### Arguments
//
// * `verb` A String in the set of ROUTE_VERBS or 'any'. 'any' will add routes for each
// of the ROUTE_VERBS. If only two arguments are passed,
// the first argument is the path, the second is the callback and the verb
// is assumed to be 'any'.
// * `path` A Regexp or a String representing the path to match to invoke this verb.
// * `callback` A Function that is called/evaluated when the route is run see: `runRoute()`.
// It is also possible to pass a string as the callback, which is looked up as the name
// of a method on the application.
//
route: function(verb, path) {
var app = this, param_names = [], add_route, path_match, callback = Array.prototype.slice.call(arguments,2);
// if the method signature is just (path, callback)
// assume the verb is 'any'
if (callback.length === 0 && _isFunction(path)) {
callback = [path];
path = verb;
verb = 'any';
}
verb = verb.toLowerCase(); // ensure verb is lower case
// if path is a string turn it into a regex
if (path.constructor == String) {
// Needs to be explicitly set because IE will maintain the index unless NULL is returned,
// which means that with two consecutive routes that contain params, the second set of params will not be found and end up in splat instead of params
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp/lastIndex
PATH_NAME_MATCHER.lastIndex = 0;
// find the names
while ((path_match = PATH_NAME_MATCHER.exec(path)) !== null) {
param_names.push(path_match[1]);
}
// replace with the path replacement
path = new RegExp(path.replace(PATH_NAME_MATCHER, PATH_REPLACER) + "$");
}
// lookup callbacks
$.each(callback,function(i,cb){
if (typeof(cb) === 'string') {
callback[i] = app[cb];
}
});
add_route = function(with_verb) {
var r = {verb: with_verb, path: path, callback: callback, param_names: param_names};
// add route to routes array
app.routes[with_verb] = app.routes[with_verb] || [];
// place routes in order of definition
app.routes[with_verb].push(r);
};
if (verb === 'any') {
$.each(this.ROUTE_VERBS, function(i, v) { add_route(v); });
} else {
add_route(verb);
}
// return the app
return this;
},
// Alias for route('get', ...)
get: _routeWrapper('get'),
// Alias for route('post', ...)
post: _routeWrapper('post'),
// Alias for route('put', ...)
put: _routeWrapper('put'),
// Alias for route('delete', ...)
del: _routeWrapper('delete'),
// Alias for route('any', ...)
any: _routeWrapper('any'),
// `mapRoutes` takes an array of arrays, each array being passed to route()
// as arguments, this allows for mass definition of routes. Another benefit is
// this makes it possible/easier to load routes via remote JSON.
//
// ### Example
//
// var app = $.sammy(function() {
//
// this.mapRoutes([
// ['get', '#/', function() { this.log('index'); }],
// // strings in callbacks are looked up as methods on the app
// ['post', '#/create', 'addUser'],
// // No verb assumes 'any' as the verb
// [/dowhatever/, function() { this.log(this.verb, this.path)}];
// ]);
// });
//
mapRoutes: function(route_array) {
var app = this;
$.each(route_array, function(i, route_args) {
app.route.apply(app, route_args);
});
return this;
},
// A unique event namespace defined per application.
// All events bound with `bind()` are automatically bound within this space.
eventNamespace: function() {
return ['sammy-app', this.namespace].join('-');
},
// Works just like `jQuery.fn.bind()` with a couple notable differences.
//
// * It binds all events to the application element
// * All events are bound within the `eventNamespace()`
// * Events are not actually bound until the application is started with `run()`
// * callbacks are evaluated within the context of a Sammy.EventContext
//
bind: function(name, data, callback) {
var app = this;
// build the callback
// if the arity is 2, callback is the second argument
if (typeof callback == 'undefined') { callback = data; }
var listener_callback = function() {
// pull off the context from the arguments to the callback
var e, context, data;
e = arguments[0];
data = arguments[1];
if (data && data.context) {
context = data.context;
delete data.context;
} else {
context = new app.context_prototype(app, 'bind', e.type, data, e.target);
}
e.cleaned_type = e.type.replace(app.eventNamespace(), '');
callback.apply(context, [e, data]);
};
// it could be that the app element doesnt exist yet
// so attach to the listeners array and then run()
// will actually bind the event.
if (!this.listeners[name]) { this.listeners[name] = []; }
this.listeners[name].push(listener_callback);
if (this.isRunning()) {
// if the app is running
// *actually* bind the event to the app element
this._listen(name, listener_callback);
}
return this;
},
// Triggers custom events defined with `bind()`
//
// ### Arguments
//
// * `name` The name of the event. Automatically prefixed with the `eventNamespace()`
// * `data` An optional Object that can be passed to the bound callback.
// * `context` An optional context/Object in which to execute the bound callback.
// If no context is supplied a the context is a new `Sammy.EventContext`
//
trigger: function(name, data) {
this.$element().trigger([name, this.eventNamespace()].join('.'), [data]);
return this;
},
// Reruns the current route
refresh: function() {
this.last_location = null;
this.trigger('location-changed');
return this;
},
// Takes a single callback that is pushed on to a stack.
// Before any route is run, the callbacks are evaluated in order within
// the current `Sammy.EventContext`
//
// If any of the callbacks explicitly return false, execution of any
// further callbacks and the route itself is halted.
//
// You can also provide a set of options that will define when to run this
// before based on the route it proceeds.
//
// ### Example
//
// var app = $.sammy(function() {
//
// // will run at #/route but not at #/
// this.before('#/route', function() {
// //...
// });
//
// // will run at #/ but not at #/route
// this.before({except: {path: '#/route'}}, function() {
// this.log('not before #/route');
// });
//
// this.get('#/', function() {});
//
// this.get('#/route', function() {});
//
// });
//
// See `contextMatchesOptions()` for a full list of supported options
//
before: function(options, callback) {
if (_isFunction(options)) {
callback = options;
options = {};
}
this.befores.push([options, callback]);
return this;
},
// A shortcut for binding a callback to be run after a route is executed.
// After callbacks have no guarunteed order.
after: function(callback) {
return this.bind('event-context-after', callback);
},
// Adds an around filter to the application. around filters are functions
// that take a single argument `callback` which is the entire route
// execution path wrapped up in a closure. This means you can decide whether
// or not to proceed with execution by not invoking `callback` or,
// more usefully wrapping callback inside the result of an asynchronous execution.
//
// ### Example
//
// The most common use case for around() is calling a _possibly_ async function
// and executing the route within the functions callback:
//
// var app = $.sammy(function() {
//
// var current_user = false;
//
// function checkLoggedIn(callback) {
// // /session returns a JSON representation of the logged in user
// // or an empty object
// if (!current_user) {
// $.getJSON('/session', function(json) {
// if (json.login) {
// // show the user as logged in
// current_user = json;
// // execute the route path
// callback();
// } else {
// // show the user as not logged in
// current_user = false;
// // the context of aroundFilters is an EventContext
// this.redirect('#/login');
// }
// });
// } else {
// // execute the route path
// callback();
// }
// };
//
// this.around(checkLoggedIn);
//
// });
//
around: function(callback) {
this.arounds.push(callback);
return this;
},
// Adds a onComplete function to the application. onComplete functions are executed
// at the end of a chain of route callbacks, if they call next(). Unlike after,
// which is called as soon as the route is complete, onComplete is like a final next()
// for all routes, and is thus run asynchronously
//
// ### Example
//
// app.get('/chain',function(context,next) {
// console.log('chain1');
// next();
// },function(context,next) {
// console.log('chain2');
// next();
// });
//
// app.get('/link',function(context,next) {
// console.log('link1');
// next();
// },function(context,next) {
// console.log('link2');
// next();
// });
//
// app.onComplete(function() {
// console.log("Running finally");
// });
//
// If you go to '/chain', you will get the following messages:
//
// chain1
// chain2
// Running onComplete
//
//
// If you go to /link, you will get the following messages:
//
// link1
// link2
// Running onComplete
//
//
// It really comes to play when doing asynchronous:
//
// app.get('/chain',function(context,next) {
// $.get('/my/url',function() {
// console.log('chain1');
// next();
// });
// },function(context,next) {
// console.log('chain2');
// next();
// });
//
onComplete: function(callback) {
this._onComplete = callback;
return this;
},
// Returns `true` if the current application is running.
isRunning: function() {
return this._running;
},
// Helpers extends the EventContext prototype specific to this app.
// This allows you to define app specific helper functions that can be used
// whenever you're inside of an event context (templates, routes, bind).
//
// ### Example
//
// var app = $.sammy(function() {
//
// helpers({
// upcase: function(text) {
// return text.toString().toUpperCase();
// }
// });
//
// get('#/', function() { with(this) {
// // inside of this context I can use the helpers
// $('#main').html(upcase($('#main').text());
// }});
//
// });
//
//
// ### Arguments
//
// * `extensions` An object collection of functions to extend the context.
//
helpers: function(extensions) {
$.extend(this.context_prototype.prototype, extensions);
return this;
},
// Helper extends the event context just like `helpers()` but does it
// a single method at a time. This is especially useful for dynamically named
// helpers
//
// ### Example
//
// // Trivial example that adds 3 helper methods to the context dynamically
// var app = $.sammy(function(app) {
//
// $.each([1,2,3], function(i, num) {
// app.helper('helper' + num, function() {
// this.log("I'm helper number " + num);
// });
// });
//
// this.get('#/', function() {
// this.helper2(); //=> I'm helper number 2
// });
// });
//
// ### Arguments
//
// * `name` The name of the method
// * `method` The function to be added to the prototype at `name`
//
helper: function(name, method) {
this.context_prototype.prototype[name] = method;
return this;
},
// Actually starts the application's lifecycle. `run()` should be invoked
// within a document.ready block to ensure the DOM exists before binding events, etc.
//
// ### Example
//
// var app = $.sammy(function() { ... }); // your application
// $(function() { // document.ready
// app.run();
// });
//
// ### Arguments
//
// * `start_url` Optionally, a String can be passed which the App will redirect to
// after the events/routes have been bound.
run: function(start_url) {
if (this.isRunning()) { return false; }
var app = this;
// actually bind all the listeners
$.each(this.listeners.toHash(), function(name, callbacks) {
$.each(callbacks, function(i, listener_callback) {
app._listen(name, listener_callback);
});
});
this.trigger('run', {start_url: start_url});
this._running = true;
// set last location
this.last_location = null;
if (!(/\#(.+)/.test(this.getLocation())) && typeof start_url != 'undefined') {
this.setLocation(start_url);
}
// check url
this._checkLocation();
this._location_proxy.bind();
this.bind('location-changed', function() {
app._checkLocation();
});
// bind to submit to capture post/put/delete routes
this.bind('submit', function(e) {
if ( !Sammy.targetIsThisWindow(e, 'form') ) { return true; }
var returned = app._checkFormSubmission($(e.target).closest('form'));
return (returned === false) ? e.preventDefault() : false;
});
// bind unload to body unload
$(window).bind('unload', function() {
app.unload();
});
// trigger html changed
return this.trigger('changed');
},
// The opposite of `run()`, un-binds all event listeners and intervals
// `run()` Automatically binds a `onunload` event to run this when
// the document is closed.
unload: function() {
if (!this.isRunning()) { return false; }
var app = this;
this.trigger('unload');
// clear interval
this._location_proxy.unbind();
// unbind form submits
this.$element().unbind('submit').removeClass(app.eventNamespace());
// unbind all events
$.each(this.listeners.toHash() , function(name, listeners) {
$.each(listeners, function(i, listener_callback) {
app._unlisten(name, listener_callback);
});
});
this._running = false;
return this;
},
// Not only runs `unbind` but also destroys the app reference.
destroy: function() {
this.unload();
delete Sammy.apps[this.element_selector];
return this;
},
// Will bind a single callback function to every event that is already
// being listened to in the app. This includes all the `APP_EVENTS`
// as well as any custom events defined with `bind()`.
//
// Used internally for debug logging.
bindToAllEvents: function(callback) {
var app = this;
// bind to the APP_EVENTS first
$.each(this.APP_EVENTS, function(i, e) {
app.bind(e, callback);
});
// next, bind to listener names (only if they dont exist in APP_EVENTS)
$.each(this.listeners.keys(true), function(i, name) {
if ($.inArray(name, app.APP_EVENTS) == -1) {
app.bind(name, callback);
}
});
return this;
},
// Returns a copy of the given path with any query string after the hash
// removed.
routablePath: function(path) {
return path.replace(QUERY_STRING_MATCHER, '');
},
// Given a verb and a String path, will return either a route object or false
// if a matching route can be found within the current defined set.
lookupRoute: function(verb, path) {
var app = this, routed = false, i = 0, l, route;
if (typeof this.routes[verb] != 'undefined') {
l = this.routes[verb].length;
for (; i < l; i++) {
route = this.routes[verb][i];
if (app.routablePath(path).match(route.path)) {
routed = route;
break;
}
}
}
return routed;
},
// First, invokes `lookupRoute()` and if a route is found, parses the
// possible URL params and then invokes the route's callback within a new
// `Sammy.EventContext`. If the route can not be found, it calls
// `notFound()`. If `raise_errors` is set to `true` and
// the `error()` has not been overridden, it will throw an actual JS
// error.
//
// You probably will never have to call this directly.
//
// ### Arguments
//
// * `verb` A String for the verb.
// * `path` A String path to lookup.
// * `params` An Object of Params pulled from the URI or passed directly.
//
// ### Returns
//
// Either returns the value returned by the route callback or raises a 404 Not Found error.
//
runRoute: function(verb, path, params, target) {
var app = this,
route = this.lookupRoute(verb, path),
context,
wrapped_route,
arounds,
around,
befores,
before,
callback_args,
path_params,
final_returned;
if (this.debug) {
this.log('runRoute', [verb, path].join(' '));
}
this.trigger('run-route', {verb: verb, path: path, params: params});
if (typeof params == 'undefined') { params = {}; }
$.extend(params, this._parseQueryString(path));
if (route) {
this.trigger('route-found', {route: route});
// pull out the params from the path
if ((path_params = route.path.exec(this.routablePath(path))) !== null) {
// first match is the full path
path_params.shift();
// for each of the matches
$.each(path_params, function(i, param) {
// if theres a matching param name
if (route.param_names[i]) {
// set the name to the match
params[route.param_names[i]] = _decode(param);
} else {
// initialize 'splat'
if (!params.splat) { params.splat = []; }
params.splat.push(_decode(param));
}
});
}
// set event context
context = new this.context_prototype(this, verb, path, params, target);
// ensure arrays
arounds = this.arounds.slice(0);
befores = this.befores.slice(0);
// set the callback args to the context + contents of the splat
callback_args = [context];
if (params.splat) {
callback_args = callback_args.concat(params.splat);
}
// wrap the route up with the before filters
wrapped_route = function() {
var returned, i, nextRoute;
while (befores.length > 0) {
before = befores.shift();
// check the options
if (app.contextMatchesOptions(context, before[0])) {
returned = before[1].apply(context, [context]);
if (returned === false) { return false; }
}
}
app.last_route = route;
context.trigger('event-context-before', {context: context});
// run multiple callbacks
if (typeof(route.callback) === "function") {
route.callback = [route.callback];
}
if (route.callback && route.callback.length) {
i = -1;
nextRoute = function() {
i++;
if (route.callback[i]) {
returned = route.callback[i].apply(context,callback_args);
} else if (app._onComplete && typeof(app._onComplete === "function")) {
app._onComplete(context);
}
};
callback_args.push(nextRoute);
nextRoute();
}
context.trigger('event-context-after', {context: context});
return returned;
};
$.each(arounds.reverse(), function(i, around) {
var last_wrapped_route = wrapped_route;
wrapped_route = function() { return around.apply(context, [last_wrapped_route]); };
});
try {
final_returned = wrapped_route();
} catch(e) {
this.error(['500 Error', verb, path].join(' '), e);
}
return final_returned;
} else {
return this.notFound(verb, path);
}
},
// Matches an object of options against an `EventContext` like object that
// contains `path` and `verb` attributes. Internally Sammy uses this
// for matching `before()` filters against specific options. You can set the
// object to _only_ match certain paths or verbs, or match all paths or verbs _except_
// those that match the options.
//
// ### Example
//
// var app = $.sammy(),
// context = {verb: 'get', path: '#/mypath'};
//
// // match against a path string
// app.contextMatchesOptions(context, '#/mypath'); //=> true
// app.contextMatchesOptions(context, '#/otherpath'); //=> false
// // equivalent to
// app.contextMatchesOptions(context, {only: {path:'#/mypath'}}); //=> true
// app.contextMatchesOptions(context, {only: {path:'#/otherpath'}}); //=> false
// // match against a path regexp
// app.contextMatchesOptions(context, /path/); //=> true
// app.contextMatchesOptions(context, /^path/); //=> false
// // match only a verb
// app.contextMatchesOptions(context, {only: {verb:'get'}}); //=> true
// app.contextMatchesOptions(context, {only: {verb:'post'}}); //=> false
// // match all except a verb
// app.contextMatchesOptions(context, {except: {verb:'post'}}); //=> true
// app.contextMatchesOptions(context, {except: {verb:'get'}}); //=> false
// // match all except a path
// app.contextMatchesOptions(context, {except: {path:'#/otherpath'}}); //=> true
// app.contextMatchesOptions(context, {except: {path:'#/mypath'}}); //=> false
// // match all except a verb and a path
// app.contextMatchesOptions(context, {except: {path:'#/otherpath', verb:'post'}}); //=> true
// app.contextMatchesOptions(context, {except: {path:'#/mypath', verb:'post'}}); //=> true
// app.contextMatchesOptions(context, {except: {path:'#/mypath', verb:'get'}}); //=> false
// // match multiple paths
// app.contextMatchesOptions(context, {path: ['#/mypath', '#/otherpath']}); //=> true
// app.contextMatchesOptions(context, {path: ['#/otherpath', '#/thirdpath']}); //=> false
// // equivalent to
// app.contextMatchesOptions(context, {only: {path: ['#/mypath', '#/otherpath']}}); //=> true
// app.contextMatchesOptions(context, {only: {path: ['#/otherpath', '#/thirdpath']}}); //=> false
// // match all except multiple paths
// app.contextMatchesOptions(context, {except: {path: ['#/mypath', '#/otherpath']}}); //=> false
// app.contextMatchesOptions(context, {except: {path: ['#/otherpath', '#/thirdpath']}}); //=> true
// // match all except multiple paths and verbs
// app.contextMatchesOptions(context, {except: {path: ['#/mypath', '#/otherpath'], verb: ['get', 'post']}}); //=> false
// app.contextMatchesOptions(context, {except: {path: ['#/otherpath', '#/thirdpath'], verb: ['get', 'post']}}); //=> true
//
contextMatchesOptions: function(context, match_options, positive) {
var options = match_options;
// normalize options
if (typeof options === 'string' || _isRegExp(options)) {
options = {path: options};
}
if (typeof positive === 'undefined') {
positive = true;
}
// empty options always match
if ($.isEmptyObject(options)) {
return true;
}
// Do we have to match against multiple paths?
if (_isArray(options.path)){
var results, numopt, opts, len;
results = [];
for (numopt = 0, len = options.path.length; numopt < len; numopt += 1) {
opts = $.extend({}, options, {path: options.path[numopt]});
results.push(this.contextMatchesOptions(context, opts));
}
var matched = $.inArray(true, results) > -1 ? true : false;
return positive ? matched : !matched;
}
if (options.only) {
return this.contextMatchesOptions(context, options.only, true);
} else if (options.except) {
return this.contextMatchesOptions(context, options.except, false);
}
var path_matched = true, verb_matched = true;
if (options.path) {
if (!_isRegExp(options.path)) {
options.path = new RegExp(options.path.toString() + '$');
}
path_matched = options.path.test(context.path);
}
if (options.verb) {
if(typeof options.verb === 'string') {
verb_matched = options.verb === context.verb;
} else {
verb_matched = options.verb.indexOf(context.verb) > -1;
}
}
return positive ? (verb_matched && path_matched) : !(verb_matched && path_matched);
},
// Delegates to the `location_proxy` to get the current location.
// See `Sammy.DefaultLocationProxy` for more info on location proxies.
getLocation: function() {
return this._location_proxy.getLocation();
},
// Delegates to the `location_proxy` to set the current location.
// See `Sammy.DefaultLocationProxy` for more info on location proxies.
//
// ### Arguments
//
// * `new_location` A new location string (e.g. '#/')
//
setLocation: function(new_location) {
return this._location_proxy.setLocation(new_location);
},
// Swaps the content of `$element()` with `content`
// You can override this method to provide an alternate swap behavior
// for `EventContext.partial()`.
//
// ### Example
//
// var app = $.sammy(function() {
//
// // implements a 'fade out'/'fade in'
// this.swap = function(content, callback) {
// var context = this;
// context.$element().fadeOut('slow', function() {
// context.$element().html(content);
// context.$element().fadeIn('slow', function() {
// if (callback) {
// callback.apply();
// }
// });
// });
// };
//
// });
//
swap: function(content, callback) {
var $el = this.$element().html(content);
if (_isFunction(callback)) { callback(content); }
return $el;
},
// a simple global cache for templates. Uses the same semantics as
// `Sammy.Cache` and `Sammy.Storage` so can easily be replaced with
// a persistent storage that lasts beyond the current request.
templateCache: function(key, value) {
if (typeof value != 'undefined') {
return _template_cache[key] = value;
} else {
return _template_cache[key];
}
},
// clear the templateCache
clearTemplateCache: function() {
return (_template_cache = {});
},
// This throws a '404 Not Found' error by invoking `error()`.
// Override this method or `error()` to provide custom
// 404 behavior (i.e redirecting to / or showing a warning)
notFound: function(verb, path) {
var ret = this.error(['404 Not Found', verb, path].join(' '));
return (verb === 'get') ? ret : true;
},
// The base error handler takes a string `message` and an `Error`
// object. If `raise_errors` is set to `true` on the app level,
// this will re-throw the error to the browser. Otherwise it will send the error
// to `log()`. Override this method to provide custom error handling
// e.g logging to a server side component or displaying some feedback to the
// user.
error: function(message, original_error) {
if (!original_error) { original_error = new Error(); }
original_error.message = [message, original_error.message].join(' ');
this.trigger('error', {message: original_error.message, error: original_error});
if (this.raise_errors) {
throw(original_error);
} else {
this.log(original_error.message, original_error);
}
},
_checkLocation: function() {
var location, returned;
// get current location
location = this.getLocation();
// compare to see if hash has changed
if (!this.last_location || this.last_location[0] != 'get' || this.last_location[1] != location) {
// reset last location
this.last_location = ['get', location];
// lookup route for current hash
returned = this.runRoute('get', location);
}
return returned;
},
_getFormVerb: function(form) {
var $form = $(form), verb, $_method;
$_method = $form.find('input[name="_method"]');
if ($_method.length > 0) { verb = $_method.val(); }
if (!verb) { verb = $form[0].getAttribute('method'); }
if (!verb || verb === '') { verb = 'get'; }
return $.trim(verb.toString().toLowerCase());
},
_checkFormSubmission: function(form) {
var $form, path, verb, params, returned;
this.trigger('check-form-submission', {form: form});
$form = $(form);
path = $form.attr('action') || '';
verb = this._getFormVerb($form);
if (this.debug) {
this.log('_checkFormSubmission', $form, path, verb);
}
if (verb === 'get') {
params = this._serializeFormParams($form);
if (params !== '') { path += '?' + params; }
this.setLocation(path);
returned = false;
} else {
params = $.extend({}, this._parseFormParams($form));
returned = this.runRoute(verb, path, params, form.get(0));
}
return (typeof returned == 'undefined') ? false : returned;
},
_serializeFormParams: function($form) {
var queryString = "",
fields = $form.serializeArray(),
i;
if (fields.length > 0) {
queryString = this._encodeFormPair(fields[0].name, fields[0].value);
for (i = 1; i < fields.length; i++) {
queryString = queryString + "&" + this._encodeFormPair(fields[i].name, fields[i].value);
}
}
return queryString;
},
_encodeFormPair: function(name, value){
return _encode(name) + "=" + _encode(value);
},
_parseFormParams: function($form) {
var params = {},
form_fields = $form.serializeArray(),
i;
for (i = 0; i < form_fields.length; i++) {
params = this._parseParamPair(params, form_fields[i].name, form_fields[i].value);
}
return params;
},
_parseQueryString: function(path) {
var params = {}, parts, pairs, pair, i;
parts = path.match(QUERY_STRING_MATCHER);
if (parts && parts[1]) {
pairs = parts[1].split('&');
for (i = 0; i < pairs.length; i++) {
pair = pairs[i].split('=');
params = this._parseParamPair(params, _decode(pair[0]), _decode(pair[1] || ""));
}
}
return params;
},
_parseParamPair: function(params, key, value) {
if (typeof params[key] !== 'undefined') {
if (_isArray(params[key])) {
params[key].push(value);
} else {
params[key] = [params[key], value];
}
} else {
params[key] = value;
}
return params;
},
_listen: function(name, callback) {
return this.$element().bind([name, this.eventNamespace()].join('.'), callback);
},
_unlisten: function(name, callback) {
return this.$element().unbind([name, this.eventNamespace()].join('.'), callback);
}
});
// `Sammy.RenderContext` is an object that makes sequential template loading,
// rendering and interpolation seamless even when dealing with asynchronous
// operations.
//
// `RenderContext` objects are not usually created directly, rather they are
// instantiated from an `Sammy.EventContext` by using `render()`, `load()` or
// `partial()` which all return `RenderContext` objects.
//
// `RenderContext` methods always returns a modified `RenderContext`
// for chaining (like jQuery itself).
//
// The core magic is in the `then()` method which puts the callback passed as
// an argument into a queue to be executed once the previous callback is complete.
// All the methods of `RenderContext` are wrapped in `then()` which allows you
// to queue up methods by chaining, but maintaining a guaranteed execution order
// even with remote calls to fetch templates.
//
Sammy.RenderContext = function(event_context) {
this.event_context = event_context;
this.callbacks = [];
this.previous_content = null;
this.content = null;
this.next_engine = false;
this.waiting = false;
};
Sammy.RenderContext.prototype = $.extend({}, Sammy.Object.prototype, {
// The "core" of the `RenderContext` object, adds the `callback` to the
// queue. If the context is `waiting` (meaning an async operation is happening)
// then the callback will be executed in order, once the other operations are
// complete. If there is no currently executing operation, the `callback`
// is executed immediately.
//
// The value returned from the callback is stored in `content` for the
// subsequent operation. If you return `false`, the queue will pause, and
// the next callback in the queue will not be executed until `next()` is
// called. This allows for the guaranteed order of execution while working
// with async operations.
//
// If then() is passed a string instead of a function, the string is looked
// up as a helper method on the event context.
//
// ### Example
//
// this.get('#/', function() {
// // initialize the RenderContext
// // Even though `load()` executes async, the next `then()`
// // wont execute until the load finishes
// this.load('myfile.txt')
// .then(function(content) {
// // the first argument to then is the content of the
// // prev operation
// $('#main').html(content);
// });
// });
//
then: function(callback) {
if (!_isFunction(callback)) {
// if a string is passed to then, assume we want to call
// a helper on the event context in its context
if (typeof callback === 'string' && callback in this.event_context) {
var helper = this.event_context[callback];
callback = function(content) {
return helper.apply(this.event_context, [content]);
};
} else {
return this;
}
}
var context = this;
if (this.waiting) {
this.callbacks.push(callback);
} else {
this.wait();
window.setTimeout(function() {
var returned = callback.apply(context, [context.content, context.previous_content]);
if (returned !== false) {
context.next(returned);
}
}, 0);
}
return this;
},
// Pause the `RenderContext` queue. Combined with `next()` allows for async
// operations.
//
// ### Example
//
// this.get('#/', function() {
// this.load('mytext.json')
// .then(function(content) {
// var context = this,
// data = JSON.parse(content);
// // pause execution
// context.wait();
// // post to a url
// $.post(data.url, {}, function(response) {
// context.next(JSON.parse(response));
// });
// })
// .then(function(data) {
// // data is json from the previous post
// $('#message').text(data.status);
// });
// });
wait: function() {
this.waiting = true;
},
// Resume the queue, setting `content` to be used in the next operation.
// See `wait()` for an example.
next: function(content) {
this.waiting = false;
if (typeof content !== 'undefined') {
this.previous_content = this.content;
this.content = content;
}
if (this.callbacks.length > 0) {
this.then(this.callbacks.shift());
}
},
// Load a template into the context.
// The `location` can either be a string specifying the remote path to the
// file, a jQuery object, or a DOM element.
//
// No interpolation happens by default, the content is stored in
// `content`.
//
// In the case of a path, unless the option `{cache: false}` is passed the
// data is stored in the app's `templateCache()`.
//
// If a jQuery or DOM object is passed the `innerHTML` of the node is pulled in.
// This is useful for nesting templates as part of the initial page load wrapped
// in invisible elements or `<script>` tags. With template paths, the template
// engine is looked up by the extension. For DOM/jQuery embedded templates,
// this isnt possible, so there are a couple of options:
//
// * pass an `{engine:}` option.
// * define the engine in the `data-engine` attribute of the passed node.
// * just store the raw template data and use `interpolate()` manually
//
// If a `callback` is passed it is executed after the template load.
load: function(location, options, callback) {
var context = this;
return this.then(function() {
var should_cache, cached, is_json, location_array;
if (_isFunction(options)) {
callback = options;
options = {};
} else {
options = $.extend({}, options);
}
if (callback) { this.then(callback); }
if (typeof location === 'string') {
// it's a path
is_json = (location.match(/\.json(\?|$)/) || options.json);
should_cache = is_json ? options.cache === true : options.cache !== false;
context.next_engine = context.event_context.engineFor(location);
delete options.cache;
delete options.json;
if (options.engine) {
context.next_engine = options.engine;
delete options.engine;
}
if (should_cache && (cached = this.event_context.app.templateCache(location))) {
return cached;
}
this.wait();
$.ajax($.extend({
url: location,
data: {},
dataType: is_json ? 'json' : 'text',
type: 'get',
success: function(data) {
if (should_cache) {
context.event_context.app.templateCache(location, data);
}
context.next(data);
}
}, options));
return false;
} else {
// it's a dom/jQuery
if (location.nodeType) {
return location.innerHTML;
}
if (location.selector) {
// it's a jQuery
context.next_engine = location.attr('data-engine');
if (options.clone === false) {
return location.remove()[0].innerHTML.toString();
} else {
return location[0].innerHTML.toString();
}
}
}
});
},
// Load partials
//
// ### Example
//
// this.loadPartials({mypartial: '/path/to/partial'});
//
loadPartials: function(partials) {
var name;
if(partials) {
this.partials = this.partials || {};
for(name in partials) {
(function(context, name) {
context.load(partials[name])
.then(function(template) {
this.partials[name] = template;
});
})(this, name);
}
}
return this;
},
// `load()` a template and then `interpolate()` it with data.
//
// can be called with multiple different signatures:
//
// this.render(callback);
// this.render('/location');
// this.render('/location', {some: data});
// this.render('/location', callback);
// this.render('/location', {some: data}, callback);
// this.render('/location', {some: data}, {my: partials});
// this.render('/location', callback, {my: partials});
// this.render('/location', {some: data}, callback, {my: partials});
//
// ### Example
//
// this.get('#/', function() {
// this.render('mytemplate.template', {name: 'test'});
// });
//
render: function(location, data, callback, partials) {
if (_isFunction(location) && !data) {
// invoked as render(callback)
return this.then(location);
} else {
if(_isFunction(data)) {
// invoked as render(location, callback, [partials])
partials = callback;
callback = data;
data = null;
} else if(callback && !_isFunction(callback)) {
// invoked as render(location, data, partials)
partials = callback;
callback = null;
}
return this.loadPartials(partials)
.load(location)
.interpolate(data, location)
.then(callback);
}
},
// `render()` the `location` with `data` and then `swap()` the
// app's `$element` with the rendered content.
partial: function(location, data, callback, partials) {
if (_isFunction(callback)) {
// invoked as partial(location, data, callback, [partials])
return this.render(location, data, partials).swap(callback);
} else if (_isFunction(data)) {
// invoked as partial(location, callback, [partials])
return this.render(location, {}, callback).swap(data);
} else {
// invoked as partial(location, data, [partials])
return this.render(location, data, callback).swap();
}
},
// defers the call of function to occur in order of the render queue.
// The function can accept any number of arguments as long as the last
// argument is a callback function. This is useful for putting arbitrary
// asynchronous functions into the queue. The content passed to the
// callback is passed as `content` to the next item in the queue.
//
// ### Example
//
// this.send($.getJSON, '/app.json')
// .then(function(json) {
// $('#message).text(json['message']);
// });
//
//
send: function() {
var context = this,
args = _makeArray(arguments),
fun = args.shift();
if (_isArray(args[0])) { args = args[0]; }
return this.then(function(content) {
args.push(function(response) { context.next(response); });
context.wait();
fun.apply(fun, args);
return false;
});
},
// iterates over an array, applying the callback for each item item. the
// callback takes the same style of arguments as `jQuery.each()` (index, item).
// The return value of each callback is collected as a single string and stored
// as `content` to be used in the next iteration of the `RenderContext`.
collect: function(array, callback, now) {
var context = this;
var coll = function() {
if (_isFunction(array)) {
callback = array;
array = this.content;
}
var contents = [], doms = false;
$.each(array, function(i, item) {
var returned = callback.apply(context, [i, item]);
if (returned.jquery && returned.length == 1) {
returned = returned[0];
doms = true;
}
contents.push(returned);
return returned;
});
return doms ? contents : contents.join('');
};
return now ? coll() : this.then(coll);
},
// loads a template, and then interpolates it for each item in the `data`
// array. If a callback is passed, it will call the callback with each
// item in the array _after_ interpolation
renderEach: function(location, name, data, callback) {
if (_isArray(name)) {
callback = data;
data = name;
name = null;
}
return this.load(location).then(function(content) {
var rctx = this;
if (!data) {
data = _isArray(this.previous_content) ? this.previous_content : [];
}
if (callback) {
$.each(data, function(i, value) {
var idata = {}, engine = this.next_engine || location;
if (name) {
idata[name] = value;
} else {
idata = value;
}
callback(value, rctx.event_context.interpolate(content, idata, engine));
});
} else {
return this.collect(data, function(i, value) {
var idata = {}, engine = this.next_engine || location;
if (name) {
idata[name] = value;
} else {
idata = value;
}
return this.event_context.interpolate(content, idata, engine);
}, true);
}
});
},
// uses the previous loaded `content` and the `data` object to interpolate
// a template. `engine` defines the templating/interpolation method/engine
// that should be used. If `engine` is not passed, the `next_engine` is
// used. If `retain` is `true`, the final interpolated data is appended to
// the `previous_content` instead of just replacing it.
interpolate: function(data, engine, retain) {
var context = this;
return this.then(function(content, prev) {
if (!data && prev) { data = prev; }
if (this.next_engine) {
engine = this.next_engine;
this.next_engine = false;
}
var rendered = context.event_context.interpolate(content, data, engine, this.partials);
return retain ? prev + rendered : rendered;
});
},
// Swap the return contents ensuring order. See `Application#swap`
swap: function(callback) {
return this.then(function(content) {
this.event_context.swap(content, callback);
return content;
}).trigger('changed', {});
},
// Same usage as `jQuery.fn.appendTo()` but uses `then()` to ensure order
appendTo: function(selector) {
return this.then(function(content) {
$(selector).append(content);
}).trigger('changed', {});
},
// Same usage as `jQuery.fn.prependTo()` but uses `then()` to ensure order
prependTo: function(selector) {
return this.then(function(content) {
$(selector).prepend(content);
}).trigger('changed', {});
},
// Replaces the `$(selector)` using `html()` with the previously loaded
// `content`
replace: function(selector) {
return this.then(function(content) {
$(selector).html(content);
}).trigger('changed', {});
},
// trigger the event in the order of the event context. Same semantics
// as `Sammy.EventContext#trigger()`. If data is omitted, `content`
// is sent as `{content: content}`
trigger: function(name, data) {
return this.then(function(content) {
if (typeof data == 'undefined') { data = {content: content}; }
this.event_context.trigger(name, data);
return content;
});
}
});
// `Sammy.EventContext` objects are created every time a route is run or a
// bound event is triggered. The callbacks for these events are evaluated within a `Sammy.EventContext`
// This within these callbacks the special methods of `EventContext` are available.
//
// ### Example
//
// $.sammy(function() {
// // The context here is this Sammy.Application
// this.get('#/:name', function() {
// // The context here is a new Sammy.EventContext
// if (this.params['name'] == 'sammy') {
// this.partial('name.html.erb', {name: 'Sammy'});
// } else {
// this.redirect('#/somewhere-else')
// }
// });
// });
//
// Initialize a new EventContext
//
// ### Arguments
//
// * `app` The `Sammy.Application` this event is called within.
// * `verb` The verb invoked to run this context/route.
// * `path` The string path invoked to run this context/route.
// * `params` An Object of optional params to pass to the context. Is converted
// to a `Sammy.Object`.
// * `target` a DOM element that the event that holds this context originates
// from. For post, put and del routes, this is the form element that triggered
// the route.
//
Sammy.EventContext = function(app, verb, path, params, target) {
this.app = app;
this.verb = verb;
this.path = path;
this.params = new Sammy.Object(params);
this.target = target;
};
Sammy.EventContext.prototype = $.extend({}, Sammy.Object.prototype, {
// A shortcut to the app's `$element()`
$element: function() {
return this.app.$element(_makeArray(arguments).shift());
},
// Look up a templating engine within the current app and context.
// `engine` can be one of the following:
//
// * a function: should conform to `function(content, data) { return interpolated; }`
// * a template path: 'template.ejs', looks up the extension to match to
// the `ejs()` helper
// * a string referring to the helper: "mustache" => `mustache()`
//
// If no engine is found, use the app's default `template_engine`
//
engineFor: function(engine) {
var context = this, engine_match;
// if path is actually an engine function just return it
if (_isFunction(engine)) { return engine; }
// lookup engine name by path extension
engine = (engine || context.app.template_engine).toString();
if ((engine_match = engine.match(/\.([^\.\?\#]+)(\?|$)/))) {
engine = engine_match[1];
}
// set the engine to the default template engine if no match is found
if (engine && _isFunction(context[engine])) {
return context[engine];
}
if (context.app.template_engine) {
return this.engineFor(context.app.template_engine);
}
return function(content, data) { return content; };
},
// using the template `engine` found with `engineFor()`, interpolate the
// `data` into `content`
interpolate: function(content, data, engine, partials) {
return this.engineFor(engine).apply(this, [content, data, partials]);
},
// Create and return a `Sammy.RenderContext` calling `render()` on it.
// Loads the template and interpolate the data, however does not actual
// place it in the DOM.
//
// ### Example
//
// // mytemplate.mustache <div class="name">{{name}}</div>
// render('mytemplate.mustache', {name: 'quirkey'});
// // sets the `content` to <div class="name">quirkey</div>
// render('mytemplate.mustache', {name: 'quirkey'})
// .appendTo('ul');
// // appends the rendered content to $('ul')
//
render: function(location, data, callback, partials) {
return new Sammy.RenderContext(this).render(location, data, callback, partials);
},
// Create and return a `Sammy.RenderContext` calling `renderEach()` on it.
// Loads the template and interpolates the data for each item,
// however does not actually place it in the DOM.
//
// `name` is an optional parameter (if it is an array, it is used as `data`,
// and the third parameter used as `callback`, if set).
//
// If `data` is not provided, content from the previous step in the chain
// (if it is an array) is used, and `name` is used as the key for each
// element of the array (useful for referencing in template).
//
// ### Example
//
// // mytemplate.mustache <div class="name">{{name}}</div>
// renderEach('mytemplate.mustache', [{name: 'quirkey'}, {name: 'endor'}])
// // sets the `content` to <div class="name">quirkey</div><div class="name">endor</div>
// renderEach('mytemplate.mustache', [{name: 'quirkey'}, {name: 'endor'}]).appendTo('ul');
// // appends the rendered content to $('ul')
//
// // names.json: ["quirkey", "endor"]
// this.load('names.json').renderEach('mytemplate.mustache', 'name').appendTo('ul');
// // uses the template to render each item in the JSON array
//
renderEach: function(location, name, data, callback) {
return new Sammy.RenderContext(this).renderEach(location, name, data, callback);
},
// create a new `Sammy.RenderContext` calling `load()` with `location` and
// `options`. Called without interpolation or placement, this allows for
// preloading/caching the templates.
load: function(location, options, callback) {
return new Sammy.RenderContext(this).load(location, options, callback);
},
// create a new `Sammy.RenderContext` calling `loadPartials()` with `partials`.
loadPartials: function(partials) {
return new Sammy.RenderContext(this).loadPartials(partials);
},
// `render()` the `location` with `data` and then `swap()` the
// app's `$element` with the rendered content.
partial: function(location, data, callback, partials) {
return new Sammy.RenderContext(this).partial(location, data, callback, partials);
},
// create a new `Sammy.RenderContext` calling `send()` with an arbitrary
// function
send: function() {
var rctx = new Sammy.RenderContext(this);
return rctx.send.apply(rctx, arguments);
},
// Changes the location of the current window. If `to` begins with
// '#' it only changes the document's hash. If passed more than 1 argument
// redirect will join them together with forward slashes.
//
// ### Example
//
// redirect('#/other/route');
// // equivalent to
// redirect('#', 'other', 'route');
//
redirect: function() {
var to, args = _makeArray(arguments),
current_location = this.app.getLocation(),
l = args.length;
if (l > 1) {
var i = 0, paths = [], pairs = [], params = {}, has_params = false;
for (; i < l; i++) {
if (typeof args[i] == 'string') {
paths.push(args[i]);
} else {
$.extend(params, args[i]);
has_params = true;
}
}
to = paths.join('/');
if (has_params) {
for (var k in params) {
pairs.push(this.app._encodeFormPair(k, params[k]));
}
to += '?' + pairs.join('&');
}
} else {
to = args[0];
}
this.trigger('redirect', {to: to});
this.app.last_location = [this.verb, this.path];
this.app.setLocation(to);
if (new RegExp(to).test(current_location)) {
this.app.trigger('location-changed');
}
},
// Triggers events on `app` within the current context.
trigger: function(name, data) {
if (typeof data == 'undefined') { data = {}; }
if (!data.context) { data.context = this; }
return this.app.trigger(name, data);
},
// A shortcut to app's `eventNamespace()`
eventNamespace: function() {
return this.app.eventNamespace();
},
// A shortcut to app's `swap()`
swap: function(contents, callback) {
return this.app.swap(contents, callback);
},
// Raises a possible `notFound()` error for the current path.
notFound: function() {
return this.app.notFound(this.verb, this.path);
},
// Default JSON parsing uses jQuery's `parseJSON()`. Include `Sammy.JSON`
// plugin for the more conformant "crockford special".
json: function(string) {
return $.parseJSON(string);
},
// //=> Sammy.EventContext: get #/ {}
toString: function() {
return "Sammy.EventContext: " + [this.verb, this.path, this.params].join(' ');
}
});
return Sammy;
});
// -- Sammy.js -- /sammy.js
// http://sammyjs.org
// Version: 0.7.6
// Built: 2014-08-26 10:45:34 +0300
(function(factory){if(typeof define==="function"&&define.amd){define(["jquery"],factory)}else{jQuery.sammy=window.Sammy=factory(jQuery)}})(function($){var Sammy,PATH_REPLACER="([^/]+)",PATH_NAME_MATCHER=/:([\w\d]+)/g,QUERY_STRING_MATCHER=/\?([^#]*)?$/,_makeArray=function(nonarray){return Array.prototype.slice.call(nonarray)},_isFunction=function(obj){return Object.prototype.toString.call(obj)==="[object Function]"},_isArray=function(obj){return Object.prototype.toString.call(obj)==="[object Array]"},_isRegExp=function(obj){return Object.prototype.toString.call(obj)==="[object RegExp]"},_decode=function(str){return decodeURIComponent((str||"").replace(/\+/g," "))},_encode=encodeURIComponent,_escapeHTML=function(s){return String(s).replace(/&(?!\w+;)/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},_routeWrapper=function(verb){return function(){return this.route.apply(this,[verb].concat(Array.prototype.slice.call(arguments)))}},_template_cache={},_has_history=!!(window.history&&history.pushState),loggers=[];Sammy=function(){var args=_makeArray(arguments),app,selector;Sammy.apps=Sammy.apps||{};if(args.length===0||args[0]&&_isFunction(args[0])){return Sammy.apply(Sammy,["body"].concat(args))}else if(typeof(selector=args.shift())=="string"){app=Sammy.apps[selector]||new Sammy.Application;app.element_selector=selector;if(args.length>0){$.each(args,function(i,plugin){app.use(plugin)})}if(app.element_selector!=selector){delete Sammy.apps[selector]}Sammy.apps[app.element_selector]=app;return app}};Sammy.VERSION="0.7.6";Sammy.addLogger=function(logger){loggers.push(logger)};Sammy.log=function(){var args=_makeArray(arguments);args.unshift("["+Date()+"]");$.each(loggers,function(i,logger){logger.apply(Sammy,args)})};if(typeof window.console!="undefined"){if(typeof window.console.log==="function"&&_isFunction(window.console.log.apply)){Sammy.addLogger(function(){window.console.log.apply(window.console,arguments)})}else{Sammy.addLogger(function(){window.console.log(arguments)})}}else if(typeof console!="undefined"){Sammy.addLogger(function(){console.log.apply(console,arguments)})}$.extend(Sammy,{makeArray:_makeArray,isFunction:_isFunction,isArray:_isArray});Sammy.Object=function(obj){return $.extend(this,obj||{})};$.extend(Sammy.Object.prototype,{escapeHTML:_escapeHTML,h:_escapeHTML,toHash:function(){var json={};$.each(this,function(k,v){if(!_isFunction(v)){json[k]=v}});return json},toHTML:function(){var display="";$.each(this,function(k,v){if(!_isFunction(v)){display+="<strong>"+k+"</strong> "+v+"<br />"}});return display},keys:function(attributes_only){var keys=[];for(var property in this){if(!_isFunction(this[property])||!attributes_only){keys.push(property)}}return keys},has:function(key){return this[key]&&$.trim(this[key].toString())!==""},join:function(){var args=_makeArray(arguments);var delimiter=args.shift();return args.join(delimiter)},log:function(){Sammy.log.apply(Sammy,arguments)},toString:function(include_functions){var s=[];$.each(this,function(k,v){if(!_isFunction(v)||include_functions){s.push('"'+k+'": '+v.toString())}});return"Sammy.Object: {"+s.join(",")+"}"}});Sammy.targetIsThisWindow=function targetIsThisWindow(event,tagName){var targetElement=$(event.target).closest(tagName);if(targetElement.length===0){return true}var targetWindow=targetElement.attr("target");if(!targetWindow||targetWindow===window.name||targetWindow==="_self"){return true}if(targetWindow==="_blank"){return false}if(targetWindow==="top"&&window===window.top){return true}return false};Sammy.DefaultLocationProxy=function(app,run_interval_every){this.app=app;this.is_native=false;this.has_history=_has_history;this._startPolling(run_interval_every)};Sammy.DefaultLocationProxy.fullPath=function(location_obj){var matches=location_obj.toString().match(/^[^#]*(#.+)$/);var hash=matches?matches[1]:"";return[location_obj.pathname,location_obj.search,hash].join("")};$.extend(Sammy.DefaultLocationProxy.prototype,{bind:function(){var proxy=this,app=this.app,lp=Sammy.DefaultLocationProxy;$(window).bind("hashchange."+this.app.eventNamespace(),function(e,non_native){if(proxy.is_native===false&&!non_native){proxy.is_native=true;window.clearInterval(lp._interval);lp._interval=null}app.trigger("location-changed")});if(_has_history&&!app.disable_push_state){$(window).bind("popstate."+this.app.eventNamespace(),function(e){app.trigger("location-changed")});$(document).delegate("a","click.history-"+this.app.eventNamespace(),function(e){if(e.isDefaultPrevented()||e.metaKey||e.ctrlKey){return}var full_path=lp.fullPath(this),hostname=this.hostname?this.hostname:function(a){var l=document.createElement("a");l.href=a.href;return l.hostname}(this);if(hostname==window.location.hostname&&app.lookupRoute("get",full_path)&&Sammy.targetIsThisWindow(e,"a")){e.preventDefault();proxy.setLocation(full_path);return false}})}if(!lp._bindings){lp._bindings=0}lp._bindings++},unbind:function(){$(window).unbind("hashchange."+this.app.eventNamespace());$(window).unbind("popstate."+this.app.eventNamespace());$(document).undelegate("a","click.history-"+this.app.eventNamespace());Sammy.DefaultLocationProxy._bindings--;if(Sammy.DefaultLocationProxy._bindings<=0){window.clearInterval(Sammy.DefaultLocationProxy._interval);Sammy.DefaultLocationProxy._interval=null}},getLocation:function(){return Sammy.DefaultLocationProxy.fullPath(window.location)},setLocation:function(new_location){if(/^([^#\/]|$)/.test(new_location)){if(_has_history&&!this.app.disable_push_state){new_location="/"+new_location}else{new_location="#!/"+new_location}}if(new_location!=this.getLocation()){if(_has_history&&!this.app.disable_push_state&&/^\//.test(new_location)){history.pushState({path:new_location},window.title,new_location);this.app.trigger("location-changed")}else{return window.location=new_location}}},_startPolling:function(every){var proxy=this;if(!Sammy.DefaultLocationProxy._interval){if(!every){every=10}var hashCheck=function(){var current_location=proxy.getLocation();if(typeof Sammy.DefaultLocationProxy._last_location=="undefined"||current_location!=Sammy.DefaultLocationProxy._last_location){window.setTimeout(function(){$(window).trigger("hashchange",[true])},0)}Sammy.DefaultLocationProxy._last_location=current_location};hashCheck();Sammy.DefaultLocationProxy._interval=window.setInterval(hashCheck,every)}}});Sammy.Application=function(app_function){var app=this;this.routes={};this.listeners=new Sammy.Object({});this.arounds=[];this.befores=[];this.namespace=(new Date).getTime()+"-"+parseInt(Math.random()*1e3,10);this.context_prototype=function(){Sammy.EventContext.apply(this,arguments)};this.context_prototype.prototype=new Sammy.EventContext;if(_isFunction(app_function)){app_function.apply(this,[this])}if(!this._location_proxy){this.setLocationProxy(new Sammy.DefaultLocationProxy(this,this.run_interval_every))}if(this.debug){this.bindToAllEvents(function(e,data){app.log(app.toString(),e.cleaned_type,data||{})})}};Sammy.Application.prototype=$.extend({},Sammy.Object.prototype,{ROUTE_VERBS:["get","post","put","delete"],APP_EVENTS:["run","unload","lookup-route","run-route","route-found","event-context-before","event-context-after","changed","error","check-form-submission","redirect","location-changed"],_last_route:null,_location_proxy:null,_running:false,element_selector:"body",debug:false,raise_errors:false,run_interval_every:50,disable_push_state:false,template_engine:null,toString:function(){return"Sammy.Application:"+this.element_selector},$element:function(selector){return selector?$(this.element_selector).find(selector):$(this.element_selector)},use:function(){var args=_makeArray(arguments),plugin=args.shift(),plugin_name=plugin||"";try{args.unshift(this);if(typeof plugin=="string"){plugin_name="Sammy."+plugin;plugin=Sammy[plugin]}plugin.apply(this,args)}catch(e){if(typeof plugin==="undefined"){this.error("Plugin Error: called use() but plugin ("+plugin_name.toString()+") is not defined",e)}else if(!_isFunction(plugin)){this.error("Plugin Error: called use() but '"+plugin_name.toString()+"' is not a function",e)}else{this.error("Plugin Error",e)}}return this},setLocationProxy:function(new_proxy){var original_proxy=this._location_proxy;this._location_proxy=new_proxy;if(this.isRunning()){if(original_proxy){original_proxy.unbind()}this._location_proxy.bind()}},log:function(){Sammy.log.apply(Sammy,Array.prototype.concat.apply([this.element_selector],arguments))},route:function(verb,path){var app=this,param_names=[],add_route,path_match,callback=Array.prototype.slice.call(arguments,2);if(callback.length===0&&_isFunction(path)){callback=[path];path=verb;verb="any"}verb=verb.toLowerCase();if(path.constructor==String){PATH_NAME_MATCHER.lastIndex=0;while((path_match=PATH_NAME_MATCHER.exec(path))!==null){param_names.push(path_match[1])}path=new RegExp(path.replace(PATH_NAME_MATCHER,PATH_REPLACER)+"$")}$.each(callback,function(i,cb){if(typeof cb==="string"){callback[i]=app[cb]}});add_route=function(with_verb){var r={verb:with_verb,path:path,callback:callback,param_names:param_names};app.routes[with_verb]=app.routes[with_verb]||[];app.routes[with_verb].push(r)};if(verb==="any"){$.each(this.ROUTE_VERBS,function(i,v){add_route(v)})}else{add_route(verb)}return this},get:_routeWrapper("get"),post:_routeWrapper("post"),put:_routeWrapper("put"),del:_routeWrapper("delete"),any:_routeWrapper("any"),mapRoutes:function(route_array){var app=this;$.each(route_array,function(i,route_args){app.route.apply(app,route_args)});return this},eventNamespace:function(){return["sammy-app",this.namespace].join("-")},bind:function(name,data,callback){var app=this;if(typeof callback=="undefined"){callback=data}var listener_callback=function(){var e,context,data;e=arguments[0];data=arguments[1];if(data&&data.context){context=data.context;delete data.context}else{context=new app.context_prototype(app,"bind",e.type,data,e.target)}e.cleaned_type=e.type.replace(app.eventNamespace(),"");callback.apply(context,[e,data])};if(!this.listeners[name]){this.listeners[name]=[]}this.listeners[name].push(listener_callback);if(this.isRunning()){this._listen(name,listener_callback)}return this},trigger:function(name,data){this.$element().trigger([name,this.eventNamespace()].join("."),[data]);return this},refresh:function(){this.last_location=null;this.trigger("location-changed");return this},before:function(options,callback){if(_isFunction(options)){callback=options;options={}}this.befores.push([options,callback]);return this},after:function(callback){return this.bind("event-context-after",callback)},around:function(callback){this.arounds.push(callback);return this},onComplete:function(callback){this._onComplete=callback;return this},isRunning:function(){return this._running},helpers:function(extensions){$.extend(this.context_prototype.prototype,extensions);return this},helper:function(name,method){this.context_prototype.prototype[name]=method;return this},run:function(start_url){if(this.isRunning()){return false}var app=this;$.each(this.listeners.toHash(),function(name,callbacks){$.each(callbacks,function(i,listener_callback){app._listen(name,listener_callback)})});this.trigger("run",{start_url:start_url});this._running=true;this.last_location=null;if(!/\#(.+)/.test(this.getLocation())&&typeof start_url!="undefined"){this.setLocation(start_url)}this._checkLocation();this._location_proxy.bind();this.bind("location-changed",function(){app._checkLocation()});this.bind("submit",function(e){if(!Sammy.targetIsThisWindow(e,"form")){return true}var returned=app._checkFormSubmission($(e.target).closest("form"));return returned===false?e.preventDefault():false});$(window).bind("unload",function(){app.unload()});return this.trigger("changed")},unload:function(){if(!this.isRunning()){return false}var app=this;this.trigger("unload");this._location_proxy.unbind();this.$element().unbind("submit").removeClass(app.eventNamespace());$.each(this.listeners.toHash(),function(name,listeners){$.each(listeners,function(i,listener_callback){app._unlisten(name,listener_callback)})});this._running=false;return this},destroy:function(){this.unload();delete Sammy.apps[this.element_selector];return this},bindToAllEvents:function(callback){var app=this;$.each(this.APP_EVENTS,function(i,e){app.bind(e,callback)});$.each(this.listeners.keys(true),function(i,name){if($.inArray(name,app.APP_EVENTS)==-1){app.bind(name,callback)}});return this},routablePath:function(path){return path.replace(QUERY_STRING_MATCHER,"")},lookupRoute:function(verb,path){var app=this,routed=false,i=0,l,route;if(typeof this.routes[verb]!="undefined"){l=this.routes[verb].length;for(;i<l;i++){route=this.routes[verb][i];if(app.routablePath(path).match(route.path)){routed=route;break}}}return routed},runRoute:function(verb,path,params,target){var app=this,route=this.lookupRoute(verb,path),context,wrapped_route,arounds,around,befores,before,callback_args,path_params,final_returned;if(this.debug){this.log("runRoute",[verb,path].join(" "))}this.trigger("run-route",{verb:verb,path:path,params:params});if(typeof params=="undefined"){params={}}$.extend(params,this._parseQueryString(path));if(route){this.trigger("route-found",{route:route});if((path_params=route.path.exec(this.routablePath(path)))!==null){path_params.shift();$.each(path_params,function(i,param){if(route.param_names[i]){params[route.param_names[i]]=_decode(param)}else{if(!params.splat){params.splat=[]}params.splat.push(_decode(param))}})}context=new this.context_prototype(this,verb,path,params,target);arounds=this.arounds.slice(0);befores=this.befores.slice(0);callback_args=[context];if(params.splat){callback_args=callback_args.concat(params.splat)}wrapped_route=function(){var returned,i,nextRoute;while(befores.length>0){before=befores.shift();if(app.contextMatchesOptions(context,before[0])){returned=before[1].apply(context,[context]);if(returned===false){return false}}}app.last_route=route;context.trigger("event-context-before",{context:context});if(typeof route.callback==="function"){route.callback=[route.callback]}if(route.callback&&route.callback.length){i=-1;nextRoute=function(){i++;if(route.callback[i]){returned=route.callback[i].apply(context,callback_args)}else if(app._onComplete&&typeof(app._onComplete==="function")){app._onComplete(context)}};callback_args.push(nextRoute);nextRoute()}context.trigger("event-context-after",{context:context});return returned};$.each(arounds.reverse(),function(i,around){var last_wrapped_route=wrapped_route;wrapped_route=function(){return around.apply(context,[last_wrapped_route])}});try{final_returned=wrapped_route()}catch(e){this.error(["500 Error",verb,path].join(" "),e)}return final_returned}else{return this.notFound(verb,path)}},contextMatchesOptions:function(context,match_options,positive){var options=match_options;if(typeof options==="string"||_isRegExp(options)){options={path:options}}if(typeof positive==="undefined"){positive=true}if($.isEmptyObject(options)){return true}if(_isArray(options.path)){var results,numopt,opts,len;results=[];for(numopt=0,len=options.path.length;numopt<len;numopt+=1){opts=$.extend({},options,{path:options.path[numopt]});results.push(this.contextMatchesOptions(context,opts))}var matched=$.inArray(true,results)>-1?true:false;return positive?matched:!matched}if(options.only){return this.contextMatchesOptions(context,options.only,true)}else if(options.except){return this.contextMatchesOptions(context,options.except,false)}var path_matched=true,verb_matched=true;if(options.path){if(!_isRegExp(options.path)){options.path=new RegExp(options.path.toString()+"$")}path_matched=options.path.test(context.path)}if(options.verb){if(typeof options.verb==="string"){verb_matched=options.verb===context.verb}else{verb_matched=options.verb.indexOf(context.verb)>-1}}return positive?verb_matched&&path_matched:!(verb_matched&&path_matched)},getLocation:function(){return this._location_proxy.getLocation()},setLocation:function(new_location){return this._location_proxy.setLocation(new_location)},swap:function(content,callback){var $el=this.$element().html(content);if(_isFunction(callback)){callback(content)}return $el},templateCache:function(key,value){if(typeof value!="undefined"){return _template_cache[key]=value}else{return _template_cache[key]}},clearTemplateCache:function(){return _template_cache={}},notFound:function(verb,path){var ret=this.error(["404 Not Found",verb,path].join(" "));return verb==="get"?ret:true},error:function(message,original_error){if(!original_error){original_error=new Error}original_error.message=[message,original_error.message].join(" ");this.trigger("error",{message:original_error.message,error:original_error});if(this.raise_errors){throw original_error}else{this.log(original_error.message,original_error)}},_checkLocation:function(){var location,returned;location=this.getLocation();if(!this.last_location||this.last_location[0]!="get"||this.last_location[1]!=location){this.last_location=["get",location];returned=this.runRoute("get",location)}return returned},_getFormVerb:function(form){var $form=$(form),verb,$_method;$_method=$form.find('input[name="_method"]');if($_method.length>0){verb=$_method.val()}if(!verb){verb=$form[0].getAttribute("method")}if(!verb||verb===""){verb="get"}return $.trim(verb.toString().toLowerCase())},_checkFormSubmission:function(form){var $form,path,verb,params,returned;this.trigger("check-form-submission",{form:form});$form=$(form);path=$form.attr("action")||"";verb=this._getFormVerb($form);if(this.debug){this.log("_checkFormSubmission",$form,path,verb)}if(verb==="get"){params=this._serializeFormParams($form);if(params!==""){path+="?"+params}this.setLocation(path);returned=false}else{params=$.extend({},this._parseFormParams($form));returned=this.runRoute(verb,path,params,form.get(0))}return typeof returned=="undefined"?false:returned},_serializeFormParams:function($form){var queryString="",fields=$form.serializeArray(),i;if(fields.length>0){queryString=this._encodeFormPair(fields[0].name,fields[0].value);for(i=1;i<fields.length;i++){queryString=queryString+"&"+this._encodeFormPair(fields[i].name,fields[i].value)}}return queryString},_encodeFormPair:function(name,value){return _encode(name)+"="+_encode(value)},_parseFormParams:function($form){var params={},form_fields=$form.serializeArray(),i;for(i=0;i<form_fields.length;i++){params=this._parseParamPair(params,form_fields[i].name,form_fields[i].value)}return params},_parseQueryString:function(path){var params={},parts,pairs,pair,i;parts=path.match(QUERY_STRING_MATCHER);if(parts&&parts[1]){pairs=parts[1].split("&");for(i=0;i<pairs.length;i++){pair=pairs[i].split("=");params=this._parseParamPair(params,_decode(pair[0]),_decode(pair[1]||""))}}return params},_parseParamPair:function(params,key,value){if(typeof params[key]!=="undefined"){if(_isArray(params[key])){params[key].push(value)}else{params[key]=[params[key],value]}}else{params[key]=value}return params},_listen:function(name,callback){return this.$element().bind([name,this.eventNamespace()].join("."),callback)},_unlisten:function(name,callback){return this.$element().unbind([name,this.eventNamespace()].join("."),callback)}});Sammy.RenderContext=function(event_context){this.event_context=event_context;this.callbacks=[];this.previous_content=null;this.content=null;this.next_engine=false;this.waiting=false};Sammy.RenderContext.prototype=$.extend({},Sammy.Object.prototype,{then:function(callback){if(!_isFunction(callback)){if(typeof callback==="string"&&callback in this.event_context){var helper=this.event_context[callback];callback=function(content){return helper.apply(this.event_context,[content])}}else{return this}}var context=this;if(this.waiting){this.callbacks.push(callback)}else{this.wait();window.setTimeout(function(){var returned=callback.apply(context,[context.content,context.previous_content]);if(returned!==false){context.next(returned)}},0)}return this},wait:function(){this.waiting=true},next:function(content){this.waiting=false;if(typeof content!=="undefined"){this.previous_content=this.content;this.content=content}if(this.callbacks.length>0){this.then(this.callbacks.shift())}},load:function(location,options,callback){var context=this;return this.then(function(){var should_cache,cached,is_json,location_array;if(_isFunction(options)){callback=options;options={}}else{options=$.extend({},options)}if(callback){this.then(callback)}if(typeof location==="string"){is_json=location.match(/\.json(\?|$)/)||options.json;should_cache=is_json?options.cache===true:options.cache!==false;context.next_engine=context.event_context.engineFor(location);delete options.cache;delete options.json;if(options.engine){context.next_engine=options.engine;delete options.engine}if(should_cache&&(cached=this.event_context.app.templateCache(location))){return cached}this.wait();$.ajax($.extend({url:location,data:{},dataType:is_json?"json":"text",type:"get",success:function(data){if(should_cache){context.event_context.app.templateCache(location,data)}context.next(data)}},options));return false}else{if(location.nodeType){return location.innerHTML}if(location.selector){context.next_engine=location.attr("data-engine");if(options.clone===false){return location.remove()[0].innerHTML.toString()}else{return location[0].innerHTML.toString()}}}})},loadPartials:function(partials){var name;if(partials){this.partials=this.partials||{};for(name in partials){(function(context,name){context.load(partials[name]).then(function(template){this.partials[name]=template})})(this,name)}}return this},render:function(location,data,callback,partials){if(_isFunction(location)&&!data){return this.then(location)}else{if(_isFunction(data)){partials=callback;callback=data;data=null}else if(callback&&!_isFunction(callback)){partials=callback;callback=null}return this.loadPartials(partials).load(location).interpolate(data,location).then(callback)}},partial:function(location,data,callback,partials){if(_isFunction(callback)){return this.render(location,data,partials).swap(callback)}else if(_isFunction(data)){return this.render(location,{},callback).swap(data)}else{return this.render(location,data,callback).swap()}},send:function(){var context=this,args=_makeArray(arguments),fun=args.shift();if(_isArray(args[0])){args=args[0]}return this.then(function(content){args.push(function(response){context.next(response)});context.wait();fun.apply(fun,args);return false})},collect:function(array,callback,now){var context=this;var coll=function(){if(_isFunction(array)){callback=array;array=this.content}var contents=[],doms=false;$.each(array,function(i,item){var returned=callback.apply(context,[i,item]);if(returned.jquery&&returned.length==1){returned=returned[0];doms=true}contents.push(returned);return returned});return doms?contents:contents.join("")};return now?coll():this.then(coll)},renderEach:function(location,name,data,callback){if(_isArray(name)){callback=data;data=name;name=null}return this.load(location).then(function(content){var rctx=this;if(!data){data=_isArray(this.previous_content)?this.previous_content:[]}if(callback){$.each(data,function(i,value){var idata={},engine=this.next_engine||location;if(name){idata[name]=value}else{idata=value}callback(value,rctx.event_context.interpolate(content,idata,engine))})}else{return this.collect(data,function(i,value){var idata={},engine=this.next_engine||location;if(name){idata[name]=value}else{idata=value}return this.event_context.interpolate(content,idata,engine)},true)}})},interpolate:function(data,engine,retain){var context=this;return this.then(function(content,prev){if(!data&&prev){data=prev}if(this.next_engine){engine=this.next_engine;this.next_engine=false}var rendered=context.event_context.interpolate(content,data,engine,this.partials);return retain?prev+rendered:rendered})},swap:function(callback){return this.then(function(content){this.event_context.swap(content,callback);return content}).trigger("changed",{})},appendTo:function(selector){return this.then(function(content){$(selector).append(content)}).trigger("changed",{})},prependTo:function(selector){return this.then(function(content){$(selector).prepend(content)}).trigger("changed",{})},replace:function(selector){return this.then(function(content){$(selector).html(content)}).trigger("changed",{})},trigger:function(name,data){return this.then(function(content){if(typeof data=="undefined"){data={content:content}}this.event_context.trigger(name,data);return content})}});Sammy.EventContext=function(app,verb,path,params,target){this.app=app;this.verb=verb;this.path=path;this.params=new Sammy.Object(params);this.target=target};Sammy.EventContext.prototype=$.extend({},Sammy.Object.prototype,{$element:function(){return this.app.$element(_makeArray(arguments).shift())},engineFor:function(engine){var context=this,engine_match;if(_isFunction(engine)){return engine}engine=(engine||context.app.template_engine).toString();if(engine_match=engine.match(/\.([^\.\?\#]+)(\?|$)/)){engine=engine_match[1]}if(engine&&_isFunction(context[engine])){return context[engine]}if(context.app.template_engine){return this.engineFor(context.app.template_engine)}return function(content,data){return content}},interpolate:function(content,data,engine,partials){return this.engineFor(engine).apply(this,[content,data,partials])},render:function(location,data,callback,partials){return new Sammy.RenderContext(this).render(location,data,callback,partials)},renderEach:function(location,name,data,callback){return new Sammy.RenderContext(this).renderEach(location,name,data,callback)},load:function(location,options,callback){return new Sammy.RenderContext(this).load(location,options,callback)},loadPartials:function(partials){return new Sammy.RenderContext(this).loadPartials(partials)},partial:function(location,data,callback,partials){return new Sammy.RenderContext(this).partial(location,data,callback,partials)},send:function(){var rctx=new Sammy.RenderContext(this);return rctx.send.apply(rctx,arguments)},redirect:function(){var to,args=_makeArray(arguments),current_location=this.app.getLocation(),l=args.length;if(l>1){var i=0,paths=[],pairs=[],params={},has_params=false;for(;i<l;i++){if(typeof args[i]=="string"){paths.push(args[i])}else{$.extend(params,args[i]);has_params=true}}to=paths.join("/");if(has_params){for(var k in params){pairs.push(this.app._encodeFormPair(k,params[k]))}to+="?"+pairs.join("&")}}else{to=args[0]}this.trigger("redirect",{to:to});this.app.last_location=[this.verb,this.path];this.app.setLocation(to);if(new RegExp(to).test(current_location)){this.app.trigger("location-changed")}},trigger:function(name,data){if(typeof data=="undefined"){data={}}if(!data.context){data.context=this}return this.app.trigger(name,data)},eventNamespace:function(){return this.app.eventNamespace()},swap:function(contents,callback){return this.app.swap(contents,callback)},notFound:function(){return this.app.notFound(this.verb,this.path)},json:function(string){return $.parseJSON(string)},toString:function(){return"Sammy.EventContext: "+[this.verb,this.path,this.params].join(" ")}});return Sammy});
\ No newline at end of file
<h1>Not found</h1>
<p>The object you clicked on was not found; it may have been deleted on the server.</p>
<% if (mode == 'queue') { %>
<h3 style="padding-top: 20px;">Add binding to this queue</h3>
<% } else { %>
<h3 style="padding-top: 20px;">Add binding from this exchange</h3>
<% } %>
<form action="#/bindings" method="post">
<input type="hidden" name="vhost" value="<%= fmt_string(parent.vhost) %>"/>
<% if (mode == 'queue') { %>
<input type="hidden" name="destination" value="<%= fmt_string(parent.name) %>"/>
<% } else { %>
<input type="hidden" name="source" value="<%= fmt_string(parent.name) %>"/>
<% } %>
<table class="form">
<% if (mode == 'queue') { %>
<tr>
<th>
<label>From exchange:</label>
</th>
<td>
<input type="hidden" name="destination_type" value="q"/>
<input type="text" name="source" value=""/>
<span class="mand">*</span>
</td>
</tr>
<% } else { %>
<tr>
<th>
<select name="destination_type" class="narrow">
<option value="e">To exchange</option>
<option value="q" selected="selected">To queue</option>
</select>:
</th>
<td>
<input type="text" name="destination" value=""/>
<span class="mand">*</span>
</td>
</tr>
<% } %>
<tr>
<th><label>Routing key:</label></th>
<td><input type="text" name="routing_key" value=""/></td>
</tr>
<tr>
<th><label>Arguments:</label></th>
<td><div class="multifield" id="arguments"></div></td>
</tr>
</table>
<input type="submit" value="Bind"/>
</form>
<%
if (binary == "not_available") {
%>
<p class="warning">
Binary statistics not available.
</p>
<% } else { %>
<%
var sections = {'queue_procs' : ['queue', 'Queues (masters)'],
'queue_slave_procs' : ['queue', 'Queues (mirrors)'],
'connection_readers' : ['conn', 'Connection readers'],
'connection_writers' : ['conn', 'Connection writers'],
'connection_channels' : ['conn', 'Connection channels'],
'connection_other' : ['conn', 'Connections (other)'],
'msg_index' : ['table', 'Message store index'],
'mgmt_db' : ['table', 'Management database'],
'plugins' : ['proc', 'Plugins'],
'other' : ['system', 'Other binary references']};
var total_out = [];
%>
<%= format('memory-bar', {sections: sections, memory: binary, total_out: total_out}) %>
<span class="clear">&nbsp;</span>
<div class="box">
<%
var key = [[{name: 'Queues', colour: 'queue',
keys: [['queue_procs', 'queues'],
['queue_slave_procs', 'mirrors']]}],
[{name: 'Connections', colour: 'conn',
keys: [['connection_readers', 'readers'],
['connection_writers', 'writers'],
['connection_channels', 'channels'],
['connection_other', 'other']]}],
[{name: 'Tables', colour: 'table',
keys: [['msg_index', 'message store index'],
['mgmt_db', 'management database']]}],
[{name: 'Processes', colour: 'proc',
keys: [['plugins', 'plugins']]},
{name: 'System', colour: 'system',
keys: [['other', 'other']]}]];
%>
<%= format('memory-table', {key: key, memory: binary}) %>
</div>
<div class="memory-info">
Last updated: <b><%= fmt_date(new Date()) %></b>.<br/>
Total referenced binaries at last update: <b><%= fmt_bytes(total_out[0]) %></b>
<span class="help" id="binary-use"></span>
</div>
<% } %>
<%= maybe_truncate(bindings) %>
<% if (bindings.length > 0) { %>
<table class="list updatable">
<thead>
<tr>
<% if (mode == 'exchange_source') { %>
<th>To</th>
<% } else { %>
<th>From</th>
<% } %>
<th>Routing key</th>
<th>Arguments</th>
<th></th>
</tr>
</thead>
<tbody>
<%
for (var i = 0; i < bindings.length; i++) {
var binding = bindings[i];
%>
<tr<%= alt_rows(i)%>>
<% if (binding.source == '') { %>
<td colspan="4">
(Default exchange binding)
</td>
<% } else { %>
<% if (mode == 'queue' || mode == 'exchange_destination') { %>
<td>
<span class="exchange">
<%= link_exchange(binding.vhost, binding.source) %>
</span>
</td>
<% } else if (binding.destination_type == 'exchange') { %>
<td>
<span class="exchange" title="Exchange">
<%= link_exchange(binding.vhost, binding.destination) %>
</span>
</td>
<% } else { %>
<td>
<span class="queue" title="Queue">
<%= link_queue(binding.vhost, binding.destination) %>
</span>
</td>
<% } %>
<td><%= fmt_string(binding.routing_key) %></td>
<td><%= fmt_table_short(binding.arguments) %></td>
<td class="c">
<form action="#/bindings" method="delete" class="confirm">
<input type="hidden" name="vhost" value="<%= fmt_string(binding.vhost) %>"/>
<input type="hidden" name="source" value="<%= fmt_exchange_url(binding.source) %>"/>
<input type="hidden" name="destination" value="<%= fmt_string(binding.destination) %>"/>
<input type="hidden" name="destination_type" value="<%= binding.destination_type.substring(0, 1) %>"/>
<input type="hidden" name="properties_key" value="<%= fmt_string(binding.properties_key) %>"/>
<input type="submit" value="Unbind"/>
</form>
</td>
<% } %>
</tr>
<% } %>
</tbody>
</table>
<% } else { %>
<p>... no bindings ...</p>
<% } %>
<h1>Channel: <b><%= fmt_escape_html(channel.name) %></b><%= fmt_maybe_vhost(channel.vhost) %></h1>
<div class="section">
<h2>Overview</h2>
<div class="hider updatable">
<% if (rates_mode != 'none') { %>
<%= message_rates('msg-rates-ch', channel.message_stats) %>
<% } %>
<h3>Details</h3>
<table class="facts facts-l">
<tr>
<th>Connection</th>
<td><%= link_conn(channel.connection_details.name) %></td>
</tr>
<% if (nodes_interesting) { %>
<tr>
<th>Node</th>
<td><%= fmt_node(channel.node) %></td>
</tr>
<% } %>
<tr>
<th>Username</th>
<td><%= fmt_string(channel.user) %></td>
</tr>
<tr>
<th>Mode <span class="help" id="channel-mode"></span></th>
<td><%= fmt_channel_mode(channel) %></td>
</tr>
</table>
<table class="facts facts-l">
<tr>
<th>State</th>
<td><%= fmt_object_state(channel) %></td>
</tr>
<tr>
<th>Prefetch count</th>
<td><%= channel.prefetch_count %></td>
</tr>
<tr>
<th>Global prefetch count</th>
<td><%= channel.global_prefetch_count %></td>
</tr>
</table>
<table class="facts">
<tr>
<th>Messages unacknowledged</th>
<td><%= channel.messages_unacknowledged %></td>
</tr>
<tr>
<th>Messages unconfirmed</th>
<td><%= channel.messages_unconfirmed %></td>
</tr>
<tr>
<th>Messages uncommitted</th>
<td><%= channel.messages_uncommitted %></td>
</tr>
<tr>
<th>Acks uncommitted</th>
<td><%= channel.acks_uncommitted %></td>
</tr>
</table>
</div>
</div>
<div class="section">
<h2>Consumers</h2>
<div class="hider updatable">
<%= format('consumers', {'mode': 'channel', 'consumers': channel.consumer_details}) %>
</div>
</div>
<% if (rates_mode == 'detailed') { %>
<div class="section">
<h2>Message rates breakdown</h2>
<div class="hider updatable">
<table class="two-col-layout">
<tr>
<td>
<%= format('msg-detail-publishes',
{'mode': 'channel',
'object': channel.publishes,
'label': 'Publishes'}) %>
</td>
<td>
<%= format('msg-detail-deliveries',
{'mode': 'channel',
'object': channel.deliveries}) %>
</td>
</tr>
</table>
</div>
</div>
<% } %>
<% if(channel.reductions || channel.garbage_collection) { %>
<div class="section-hidden">
<h2>Runtime Metrics (Advanced)</h2>
<div class="hider updatable">
<%= data_reductions('reductions-rates-conn', channel) %>
<table class="facts">
<% if (channel.garbage_collection.min_bin_vheap_size) { %>
<tr>
<th>Minimum binary virtual heap size in words (min_bin_vheap_size)</th>
<td><%= channel.garbage_collection.min_bin_vheap_size %></td>
</tr>
<% } %>
<% if (channel.garbage_collection.min_heap_size) { %>
<tr>
<th>Minimum heap size in words (min_heap_size)</th>
<td><%= channel.garbage_collection.min_heap_size %></td>
</tr>
<% } %>
<% if (channel.garbage_collection.fullsweep_after) { %>
<tr>
<th>Maximum generational collections before fullsweep (fullsweep_after)</th>
<td><%= channel.garbage_collection.fullsweep_after %></td>
</tr>
<% } %>
<% if (channel.garbage_collection.minor_gcs) { %>
<tr>
<th>Number of minor GCs (minor_gcs)</th>
<td><%= channel.garbage_collection.minor_gcs %></td>
</tr>
<% } %>
</table>
</div>
</div>
<% } %>
<% if (channels.length > 0) { %>
<table class="list">
<thead>
<tr>
<% if (mode == 'standalone') { %>
<%= group_heading('channels', 'Overview', [true, vhosts_interesting, nodes_interesting]) %>
<% } else { %>
<%= group_heading('channels', 'Overview', [true]) %>
<% } %>
<%= group_heading('channels', 'Details', []) %>
<%= group_heading('channels', 'Transactions', []) %>
<% if (rates_mode != 'none') { %>
<%= group_heading('channels', 'Message rates', []) %>
<% } %>
<th class="plus-minus"><span class="popup-options-link" title="Click to change columns" type="columns" for="channels">+/-</span></th>
</tr>
<tr>
<% if (mode == 'standalone') { %>
<th><%= fmt_sort('Channel', 'name') %></th>
<% if (nodes_interesting) { %>
<th><%= fmt_sort('Node', 'node') %></th>
<% } %>
<% if (vhosts_interesting) { %>
<th><%= fmt_sort('Virtual host', 'vhost') %></th>
<% } %>
<% if (show_column('channels', 'user')) { %>
<th><%= fmt_sort('User name', 'user') %></th>
<% } %>
<% if (show_column('channels', 'mode')) { %>
<th>Mode <span class="help" id="channel-mode"></span></th>
<% } %>
<% if (show_column('channels', 'state')) { %>
<th><%= fmt_sort('State', 'state') %></th>
<% } %>
<% if (show_column('channels', 'msgs-unconfirmed')) { %>
<th><%= fmt_sort('Unconfirmed', 'messages_unconfirmed') %></th>
<% } %>
<% if (show_column('channels', 'prefetch')) { %>
<th>Prefetch <span class="help" id="channel-prefetch"></span></th>
<% } %>
<% if (show_column('channels', 'msgs-unacked')) { %>
<th><%= fmt_sort('Unacked', 'messages_unacknowledged') %></th>
<% } %>
<% if (show_column('channels', 'msgs-uncommitted')) { %>
<th><%= fmt_sort('Uncommitted msgs', 'messages_uncommitted') %></th>
<% } %>
<% if (show_column('channels', 'acks-uncommitted')) { %>
<th><%= fmt_sort('Uncommitted acks', 'acks_uncommitted') %></th>
<% } %>
<% if (rates_mode != 'none') { %>
<% if (show_column('channels', 'rate-publish')) { %>
<th><%= fmt_sort('publish', 'message_stats.publish_details.rate') %></th>
<% } %>
<% if (show_column('channels', 'rate-confirm')) { %>
<th><%= fmt_sort('confirm', 'message_stats.confirm_details.rate') %></th>
<% } %>
<% if (show_column('channels', 'rate-return')) { %>
<th><%= fmt_sort('return (mandatory)', 'message_stats.return_unroutable_details.rate') %></th>
<% } %>
<% if (show_column('channels', 'rate-deliver')) { %>
<th><%= fmt_sort('deliver / get', 'message_stats.deliver_get_details.rate') %></th>
<% } %>
<% if (show_column('channels', 'rate-redeliver')) { %>
<th><%= fmt_sort('redelivered', 'message_stats.redeliver_details.rate') %></th>
<% } %>
<% if (show_column('channels', 'rate-ack')) { %>
<th><%= fmt_sort('ack', 'message_stats.ack_details.rate') %></th>
<% } %>
<% } %>
<% } else { %>
<!-- TODO make sortable after bug 23401 -->
<th>Channel</th>
<% if (show_column('channels', 'user')) { %>
<th>User name</th>
<% } %>
<% if (show_column('channels', 'mode')) { %>
<th>Mode <span class="help" id="channel-mode"></span></th>
<% } %>
<% if (show_column('channels', 'state')) { %>
<th>State</th>
<% } %>
<% if (show_column('channels', 'msgs-unconfirmed')) { %>
<th>Unconfirmed</th>
<% } %>
<% if (show_column('channels', 'prefetch')) { %>
<th>Prefetch <span class="help" id="channel-prefetch"></span></th>
<% } %>
<% if (show_column('channels', 'msgs-unacked')) { %>
<th>Unacked</th>
<% } %>
<% if (show_column('channels', 'msgs-uncommitted')) { %>
<th>Uncommitted msgs</th>
<% } %>
<% if (show_column('channels', 'acks-uncommitted')) { %>
<th>Uncommitted acks</th>
<% } %>
<% if (rates_mode != 'none') { %>
<% if (show_column('channels', 'rate-publish')) { %>
<th>publish</th>
<% } %>
<% if (show_column('channels', 'rate-confirm')) { %>
<th>confirm</th>
<% } %>
<% if (show_column('channels', 'rate-return')) { %>
<th>return (mandatory)</th>
<% } %>
<% if (show_column('channels', 'rate-deliver')) { %>
<th>deliver / get</th>
<% } %>
<% if (show_column('channels', 'rate-redeliver')) { %>
<th>redelivered</th>
<% } %>
<% if (show_column('channels', 'rate-ack')) { %>
<th>ack</th>
<% } %>
<% } %>
<% } %>
</tr>
</thead>
<tbody>
<%
for (var i = 0; i < channels.length; i++) {
var channel = channels[i];
%>
<tr<%= alt_rows(i)%>>
<td>
<%= link_channel(channel.name) %>
</td>
<% if (mode == 'standalone' && nodes_interesting) { %>
<td><%= fmt_node(channel.node) %></td>
<% } %>
<% if (mode == 'standalone' && vhosts_interesting) { %>
<td class="c"><%= fmt_string(channel.vhost) %></td>
<% } %>
<% if (show_column('channels', 'user')) { %>
<td class="c"><%= fmt_string(channel.user) %></td>
<% } %>
<% if (show_column('channels', 'mode')) { %>
<td class="c">
<%= fmt_channel_mode(channel) %>
</td>
<% } %>
<% if (show_column('channels', 'state')) { %>
<td class="c"><%= fmt_object_state(channel) %></td>
<% } %>
<% if (show_column('channels', 'msgs-unconfirmed')) { %>
<td class="c"><%= channel.messages_unconfirmed %></td>
<% } %>
<% if (show_column('channels', 'prefetch')) { %>
<td class="c">
<% if (channel.prefetch_count != 0) { %>
<%= channel.prefetch_count %><br/>
<% } %>
<% if (channel.global_prefetch_count != 0) { %>
<%= channel.global_prefetch_count %> (global)
<% } %>
</td>
<% } %>
<% if (show_column('channels', 'msgs-unacked')) { %>
<td class="c"><%= channel.messages_unacknowledged %></td>
<% } %>
<% if (show_column('channels', 'msgs-uncommitted')) { %>
<td class="c"><%= channel.messages_uncommitted %></td>
<% } %>
<% if (show_column('channels', 'acks-uncommitted')) { %>
<td class="c"><%= channel.acks_uncommitted %></td>
<% } %>
<% if (rates_mode != 'none') { %>
<% if (show_column('channels', 'rate-publish')) { %>
<td class="r"><%= fmt_detail_rate(channel.message_stats, 'publish') %></td>
<% } %>
<% if (show_column('channels', 'rate-confirm')) { %>
<td class="r"><%= fmt_detail_rate(channel.message_stats, 'confirm') %></td>
<% } %>
<% if (show_column('channels', 'rate-return')) { %>
<td class="r"><%= fmt_detail_rate(channel.message_stats, 'return_unroutable') %></td>
<% } %>
<% if (show_column('channels', 'rate-deliver')) { %>
<td class="r"><%= fmt_detail_rate(channel.message_stats, 'deliver_get') %></td>
<% } %>
<% if (show_column('channels', 'rate-redeliver')) { %>
<td class="r"><%= fmt_detail_rate(channel.message_stats, 'redeliver') %></td>
<% } %>
<% if (show_column('channels', 'rate-ack')) { %>
<td class="r"><%= fmt_detail_rate(channel.message_stats, 'ack') %></td>
<% } %>
<% } %>
</tr>
<% } %>
</tbody>
</table>
<% } else { %>
<p>... no channels ...</p>
<% } %>
<h1>Channels</h1>
<div class="section">
<%= paginate_ui(channels, 'channels') %>
</div>
<div class="updatable">
<%= format('channels-list', {'channels': channels.items, 'mode': 'standalone'}) %>
</div>
<h1>Cluster name: <b><%= fmt_string(cluster_name.name) %></b></h1>
<p>
The cluster name can be used by clients to identify clusters over
AMQP connections, and is used by the shovel and federation plugins
to identify which clusters a message has been routed through.
</p>
<p>
Note that the cluster name is announced to clients in the AMQP
server properties; i.e. before authentication has taken
place. Therefore it should not be considered secret.
</p>
<p>
The cluster name is generated by default from the name of the first
node in the cluster, but can be changed.
</p>
<div class="section-hidden">
<h2>Change name</h2>
<div class="hider">
<form action="#/cluster-name" method="put">
<table class="form">
<tr>
<th><label>Name:</label></th>
<td><input type="text" name="name" value="<%= fmt_string(cluster_name.name) %>"/><span class="mand">*</span></td>
</tr>
</table>
<input type="submit" value="Change name"/>
</form>
</div>
</div>
<%
var mode = span.attr('for');
%>
<form action="#/column-options" method="put" class="auto-submit">
<input type="hidden" name="mode" value="<%= mode %>"/>
<table class="form" width="100%">
<tr>
<td colspan="2">
<h3>Columns for this table</h3>
</td>
</tr>
<% for (var group in COLUMNS[mode]) {
var options = COLUMNS[mode][group]; %>
<tr>
<th><label><%= group %>:</label></th>
<td>
<% for (var i = 0; i < options.length; i++) { %>
<%= fmt_checkbox(mode + '-' + options[i][0], options[i][1], get_pref('column-' + mode + '-' + options[i][0]) == 'true') %>
<% } %>
</td>
<% } %>
</tr>
</table>
</form>
<h1>Connection <%= fmt_string(connection.name) %> <%= fmt_maybe_vhost(connection.vhost) %></h1>
<div class="section">
<h2>Overview</h2>
<div class="hider updatable">
<%= data_rates('data-rates-conn', connection, 'Data rates') %>
<h3>Details</h3>
<table class="facts facts-l">
<% if (nodes_interesting) { %>
<tr>
<th>Node</th>
<td><%= fmt_node(connection.node) %></td>
</tr>
<% } %>
<% if (connection.client_properties.connection_name) { %>
<tr>
<th>Client-provided name</th>
<td><%= fmt_string(connection.client_properties.connection_name) %></td>
</tr>
<% } %>
<tr>
<th>Username</th>
<td><%= fmt_string(connection.user) %></td>
</tr>
<tr>
<th>Protocol</th>
<td><%= connection.protocol %></td>
</tr>
<tr>
<th>Connected at</th>
<td><%= fmt_timestamp(connection.connected_at) %></td>
</tr>
<% if (connection.ssl) { %>
<tr>
<th>SSL</th>
<td><%= fmt_boolean(connection.ssl) %></td>
</tr>
<% } %>
<% if (connection.auth_mechanism) { %>
<tr>
<th>Authentication</th>
<td><%= connection.auth_mechanism %></td>
</tr>
<% } %>
</table>
<% if (connection.state) { %>
<table class="facts">
<tr>
<th>State</th>
<td><%= fmt_object_state(connection) %></td>
</tr>
<tr>
<th>Heartbeat</th>
<td><%= fmt_time(connection.timeout, 's') %></td>
</tr>
<tr>
<th>Frame max</th>
<td><%= connection.frame_max %> bytes</td>
</tr>
<tr>
<th>Channel limit</th>
<td><%= connection.channel_max %> channels</td>
</tr>
</table>
<% } %>
</div>
</div>
<div class="section">
<h2>Channels</h2>
<div class="hider updatable">
<%= format('channels-list', {'channels': channels, 'mode': 'connection'}) %>
</div>
</div>
<% if (connection.ssl) { %>
<div class="section">
<h2>SSL</h2>
<div class="hider">
<table class="facts">
<tr>
<th>Protocol Version</th>
<td><%= connection.ssl_protocol %></td>
</tr>
<tr>
<th>Key Exchange Algorithm</th>
<td><%= connection.ssl_key_exchange %></td>
</tr>
<tr>
<th>Cipher Algorithm</th>
<td><%= connection.ssl_cipher %></td>
</tr>
<tr>
<th>Hash Algorithm</th>
<td><%= connection.ssl_hash %></td>
</tr>
</table>
<% if (connection.peer_cert_issuer != '') { %>
<table class="facts">
<tr>
<th>Peer Certificate Issuer</th>
<td><%= connection.peer_cert_issuer %></td>
</tr>
<tr>
<th>Peer Certificate Subject</th>
<td><%= connection.peer_cert_subject %></td>
</tr>
<tr>
<th>Peer Certificate Validity</th>
<td><%= connection.peer_cert_validity %></td>
</tr>
</table>
<% } %>
</div>
</div>
<% } %>
<% if (properties_size(connection.client_properties) > 0) { %>
<div class="section-hidden">
<h2>Client properties</h2>
<div class="hider">
<%= fmt_table_long(connection.client_properties) %>
</div>
</div>
<% } %>
<% if(connection.reductions || connection.garbage_collection) { %>
<div class="section-hidden">
<h2>Runtime Metrics (Advanced)</h2>
<div class="hider updatable">
<%= data_reductions('reductions-rates-conn', connection) %>
<table class="facts">
<% if (connection.garbage_collection.min_bin_vheap_size) { %>
<tr>
<th>Minimum binary virtual heap size in words (min_bin_vheap_size)</th>
<td><%= connection.garbage_collection.min_bin_vheap_size %></td>
</tr>
<% } %>
<% if (connection.garbage_collection.min_heap_size) { %>
<tr>
<th>Minimum heap size in words (min_heap_size)</th>
<td><%= connection.garbage_collection.min_heap_size %></td>
</tr>
<% } %>
<% if (connection.garbage_collection.fullsweep_after) { %>
<tr>
<th>Maximum generational collections before fullsweep (fullsweep_after)</th>
<td><%= connection.garbage_collection.fullsweep_after %></td>
</tr>
<% } %>
<% if (connection.garbage_collection.minor_gcs) { %>
<tr>
<th>Number of minor GCs (minor_gcs)</th>
<td><%= connection.garbage_collection.minor_gcs %></td>
</tr>
<% } %>
</table>
</div>
</div>
<% } %>
<div class="section-hidden">
<h2>Close this connection</h2>
<div class="hider">
<form action="#/connections" method="delete" class="confirm">
<input type="hidden" name="name" value="<%= fmt_string(connection.name) %>"/>
<table class="form">
<tr>
<th><label>Reason:</label></th>
<td>
<input type="text" name="reason" value="Closed via management plugin" class="wide"/>
</td>
</tr>
</table>
<input type="submit" value="Force Close"/>
</form>
</div>
</div>
<h1>Connections</h1>
<div class="section">
<%= paginate_ui(connections, 'connections') %>
</div>
<div class="updatable">
<% if (connections.items.length > 0) { %>
<table class="list">
<thead>
<tr>
<%= group_heading('connections', 'Overview', [vhosts_interesting, nodes_interesting, true]) %>
<%= group_heading('connections', 'Details', []) %>
<%= group_heading('connections', 'Network', []) %>
<th class="plus-minus"><span class="popup-options-link" title="Click to change columns" type="columns" for="connections">+/-</span></th>
</tr>
<tr>
<% if (vhosts_interesting) { %>
<th><%= fmt_sort('Virtual host', 'vhost') %></th>
<% } %>
<th><%= fmt_sort('Name', 'client_properties.connection_name') %></th>
<% if (nodes_interesting) { %>
<th><%= fmt_sort('Node', 'node') %></th>
<% } %>
<% if (show_column('connections', 'user')) { %>
<th><%= fmt_sort('User name', 'user') %></th>
<% } %>
<% if (show_column('connections', 'state')) { %>
<th><%= fmt_sort('State', 'state') %></th>
<% } %>
<% if (show_column('connections', 'ssl')) { %>
<th><%= fmt_sort('SSL / TLS', 'ssl') %></th>
<% } %>
<% if (show_column('connections', 'ssl_info')) { %>
<th>SSL Details</th>
<% } %>
<% if (show_column('connections', 'protocol')) { %>
<th><%= fmt_sort('Protocol', 'protocol') %></th>
<% } %>
<% if (show_column('connections', 'channels')) { %>
<th><%= fmt_sort('Channels', 'channels') %></th>
<% } %>
<% if (show_column('connections', 'channel_max')) { %>
<th><%= fmt_sort('Channel max', 'channel_max') %></th>
<% } %>
<% if (show_column('connections', 'frame_max')) { %>
<th><%= fmt_sort('Frame max', 'frame_max') %></th>
<% } %>
<% if (show_column('connections', 'auth_mechanism')) { %>
<th><%= fmt_sort('Auth mechanism', 'auth_mechanism') %></th>
<% } %>
<% if (show_column('connections', 'client')) { %>
<th><%= fmt_sort('Client', 'properties') %></th>
<% } %>
<% if (show_column('connections', 'from_client')) { %>
<th><%= fmt_sort('From client', 'recv_oct_details.rate') %></th>
<% } %>
<% if (show_column('connections', 'to_client')) { %>
<th><%= fmt_sort('To client', 'send_oct_details.rate') %></th>
<% } %>
<% if (show_column('connections', 'heartbeat')) { %>
<th><%= fmt_sort('Heartbeat', 'timeout') %></th>
<% } %>
<% if (show_column('connections', 'connected_at')) { %>
<th><%= fmt_sort('Connected at', 'connected_at') %></th>
<% } %>
</tr>
</thead>
<tbody>
<%
for (var i = 0; i < connections.items.length; i++) {
var connection = connections.items[i];
%>
<tr<%= alt_rows(i)%>>
<% if (vhosts_interesting) { %>
<td><%= fmt_string(connection.vhost) %></td>
<% } %>
<% if(connection.client_properties.connection_name) { %>
<td><%= link_conn(connection.name) %>
<%= fmt_string(short_conn(connection.client_properties.connection_name)) %>
</td>
<% } else { %>
<td><%= link_conn(connection.name) %></td>
<% } %>
<% if (nodes_interesting) { %>
<td><%= fmt_node(connection.node) %></td>
<% } %>
<% if (show_column('connections', 'user')) { %>
<td class="c"><%= fmt_string(connection.user) %></td>
<% } %>
<% if (show_column('connections', 'state')) { %>
<td><%= fmt_object_state(connection) %></td>
<% } %>
<% if (show_column('connections', 'ssl')) { %>
<td class="c"><%= fmt_boolean(connection.ssl, '') %></td>
<% } %>
<% if (show_column('connections', 'ssl_info')) { %>
<td>
<% if (connection.ssl) { %>
<%= connection.ssl_protocol %>
<sub>
<%= connection.ssl_key_exchange %>
<%= connection.ssl_cipher %>
<%= connection.ssl_hash %>
</sub>
<% } %>
</td>
<% } %>
<% if (show_column('connections', 'protocol')) { %>
<td class="c"><%= connection.protocol %></td>
<% } %>
<% if (show_column('connections', 'channels')) { %>
<td class="r"><%= fmt_string(connection.channels, '') %></td>
<% } %>
<% if (show_column('connections', 'channel_max')) { %>
<td class="r"><%= fmt_string(connection.channel_max, '') %></td>
<% } %>
<% if (show_column('connections', 'frame_max')) { %>
<td class="r"><%= fmt_string(connection.frame_max, '') %></td>
<% } %>
<% if (show_column('connections', 'auth_mechanism')) { %>
<td class="c"><%= fmt_string(connection.auth_mechanism, '') %></td>
<% } %>
<% if (show_column('connections', 'client')) { %>
<td><%= fmt_client_name(connection.client_properties) %></td>
<% } %>
<% if (show_column('connections', 'from_client')) { %>
<td><%= fmt_detail_rate_bytes(connection, 'recv_oct') %></td>
<% } %>
<% if (show_column('connections', 'to_client')) { %>
<td><%= fmt_detail_rate_bytes(connection, 'send_oct') %></td>
<% } %>
<% if (show_column('connections', 'heartbeat')) { %>
<td class="r"><%= fmt_time(connection.timeout, 's') %></td>
<% } %>
<% if (show_column('connections', 'connected_at')) { %>
<td><%= fmt_timestamp_mini(connection.connected_at) %></td>
<% } %>
</tr>
<% } %>
</tbody>
</table>
<% } else { %>
<p>... no connections ...</p>
<% } %>
</div>
<% if (consumers.length > 0) { %>
<table class="list">
<thead>
<tr>
<% if (mode == 'queue') { %>
<th>Channel</th>
<th>Consumer tag</th>
<% } else { %>
<th>Consumer tag</th>
<th>Queue</th>
<% } %>
<th>Ack required</th>
<th>Exclusive</th>
<th>Prefetch count</th>
<th>Arguments</th>
</tr>
</thead>
<%
for (var i = 0; i < consumers.length; i++) {
var consumer = consumers[i];
%>
<tr<%= alt_rows(i) %>>
<% if (mode == 'queue') { %>
<td><%= link_channel(consumer.channel_details.name) %></td>
<td><%= fmt_string(consumer.consumer_tag) %></td>
<% } else { %>
<td><%= fmt_string(consumer.consumer_tag) %></td>
<td><%= link_queue(consumer.queue.vhost, consumer.queue.name) %></td>
<% } %>
<td class="c"><%= fmt_boolean(consumer.ack_required) %></td>
<td class="c"><%= fmt_boolean(consumer.exclusive) %></td>
<td class="c"><%= consumer.prefetch_count %></td>
<td class="c"><%= fmt_table_short(consumer.arguments) %></td>
</tr>
<% } %>
</table>
<% } else { %>
<p>... no consumers ...</p>
<% } %>
<h1>Exchange: <b><%= fmt_exchange(highlight_extra_whitespace(exchange.name)) %></b><%= fmt_maybe_vhost(exchange.vhost) %></h1>
<div class="section">
<h2>Overview</h2>
<div class="hider updatable">
<% if (rates_mode != 'none') { %>
<%= message_rates('msg-rates-x', exchange.message_stats) %>
<% } %>
<h3>Details</h3>
<table class="facts">
<tr>
<th>Type</th>
<td class="l"><%= fmt_exchange_type(exchange.type) %></td>
</tr>
<tr>
<th>Features</th>
<td><%= fmt_features(exchange) %></td>
</tr>
<tr>
<th>Policy</th>
<td><%= link_policy(exchange.vhost, exchange.policy) %></td>
</tr>
</table>
</div>
</div>
<% if (rates_mode == 'detailed') { %>
<div class="section-hidden">
<h2>Message rates breakdown</h2>
<div class="hider updatable">
<table class="two-col-layout">
<tr>
<td>
<%= format('msg-detail-publishes',
{'mode': 'exchange-incoming',
'object': exchange.incoming,
'label': 'Incoming <span class="help" id="exchange-rates-incoming"></span>'}) %>
</td>
<td>
<%= format('msg-detail-publishes',
{'mode': 'exchange-outgoing',
'object': exchange.outgoing,
'label': 'Outgoing <span class="help" id="exchange-rates-outgoing"></span>'}) %>
</td>
</tr>
</table>
</div>
</div>
<% } %>
<div class="section-hidden">
<h2>Bindings</h2>
<div class="hider">
<% if (exchange.name == "") { %>
<h3>Default exchange</h3>
<p>
The default exchange is implicitly bound to every queue, with a
routing key equal to the queue name. It is not possible to
explicitly bind to, or unbind from the default exchange. It also
cannot be deleted.
</p>
<% } else { %>
<div class="bindings-wrapper">
<% if (bindings_destination.length > 0) { %>
<%= format('bindings', {'mode': 'exchange_destination', 'bindings': bindings_destination}) %>
<p class="arrow">&dArr;</p>
<% } %>
<p><span class="exchange">This exchange</span></p>
<p class="arrow">&dArr;</p>
<%= format('bindings', {'mode': 'exchange_source', 'bindings': bindings_source}) %>
</div>
<%= format('add-binding', {'mode': 'exchange_source', 'parent': exchange}) %>
<% } %>
</div>
</div>
<% if (!exchange.internal) { %>
<%= format('publish', {'mode': 'exchange', 'exchange': exchange}) %>
<% } %>
<% if (exchange.name != "") { %>
<div class="section-hidden">
<h2>Delete this exchange</h2>
<div class="hider">
<form action="#/exchanges" method="delete" class="confirm">
<input type="hidden" name="vhost" value="<%= fmt_string(exchange.vhost) %>"/>
<input type="hidden" name="name" value="<%= fmt_exchange_url(exchange.name) %>"/>
<input type="submit" value="Delete"/>
</form>
</div>
</div>
<% } %>
<h1>Exchanges</h1>
<div class="section">
<%= paginate_ui(exchanges, 'exchanges') %>
</div>
<div class="updatable">
<% if (exchanges.items.length > 0) { %>
<table class="list">
<thead>
<tr>
<% if (vhosts_interesting) { %>
<th><%= fmt_sort('Virtual host', 'vhost') %></th>
<% } %>
<th><%= fmt_sort('Name', 'name') %></th>
<% if (show_column('exchanges', 'type')) { %>
<th><%= fmt_sort('Type', 'type') %></th>
<% } %>
<% if (show_column('exchanges', 'features')) { %>
<th>Features</th>
<% } %>
<% if (show_column('exchanges', 'features_no_policy')) { %>
<th>Features</th>
<% } %>
<% if (show_column('exchanges', 'policy')) { %>
<th><%= fmt_sort('Policy','policy') %></th>
<% } %>
<% if (rates_mode != 'none') { %>
<% if (show_column('exchanges', 'rate-in')) { %>
<th><%= fmt_sort('Message rate in', 'message_stats.publish_in_details.rate') %></th>
<% } %>
<% if (show_column('exchanges', 'rate-out')) { %>
<th><%= fmt_sort('Message rate out', 'message_stats.publish_out_details.rate') %></th>
<% } %>
<% } %>
<th class="plus-minus"><span class="popup-options-link" title="Click to change columns" type="columns" for="exchanges">+/-</span></th>
</tr>
</thead>
<tbody>
<%
for (var i = 0; i < exchanges.items.length; i++) {
var exchange = exchanges.items[i];
%>
<tr<%= alt_rows(i, exchange.arguments)%>>
<% if (vhosts_interesting) { %>
<td><%= fmt_string(exchange.vhost) %></td>
<% } %>
<td><%= link_exchange(exchange.vhost, exchange.name, exchange.arguments) %></td>
<td class="c"><%= fmt_exchange_type(exchange.type) %></td>
<% if (show_column('exchanges', 'features')) { %>
<td class="c">
<%= fmt_features_short(exchange) %>
<%= fmt_policy_short(exchange) %>
</td>
<% } %>
<% if (show_column('exchanges', 'features_no_policy')) { %>
<td class="c">
<%= fmt_features_short(exchange) %>
</td>
<% } %>
<% if (show_column('exchanges', 'policy')) { %>
<td class="c">
<%= link_policy(exchange.vhost, exchange.policy) %>
</td>
<% } %>
<% if (rates_mode != 'none') { %>
<% if (show_column('exchanges', 'rate-in')) { %>
<td class="r"><%= fmt_detail_rate(exchange.message_stats, 'publish_in') %></td>
<% } %>
<% if (show_column('exchanges', 'rate-out')) { %>
<td class="r"><%= fmt_detail_rate(exchange.message_stats, 'publish_out') %></td>
<% } %>
<% } %>
</tr>
<% } %>
</tbody>
</table>
<% } else { %>
<p>... no exchanges ...</p>
<% } %>
</div>
</div>
</div>
<div class="section-hidden">
<h2>Add a new exchange</h2>
<div class="hider">
<form action="#/exchanges" method="put">
<table class="form">
<% if (vhosts_interesting) { %>
<tr>
<th><label>Virtual host:</label></th>
<td>
<select name="vhost">
<% for (var i = 0; i < vhosts.length; i++) { %>
<option value="<%= fmt_string(vhosts[i].name) %>" <%= (vhosts[i].name === current_vhost) ? 'selected="selected"' : '' %>><%= fmt_string(vhosts[i].name) %></option>
<% } %>
</select>
</td>
</tr>
<% } else { %>
<tr><td><input type="hidden" name="vhost" value="<%= fmt_string(vhosts[0].name) %>"/></td></tr>
<% } %>
<tr>
<th><label>Name:</label></th>
<td><input type="text" name="name"/><span class="mand">*</span></td>
</tr>
<tr>
<th><label>Type:</label></th>
<td>
<select name="type">
<% for (var i = 0; i < exchange_types.length; i++) {
var type = exchange_types[i];
if (type.internal_purpose == undefined) { %>
<option value="<%= fmt_string(type.name) %>"><%= fmt_string(type.name) %></option>
<% }
} %>
</select>
</td>
</tr>
<tr>
<th><label>Durability:</label></th>
<td>
<select name="durable">
<option value="true">Durable</option>
<option value="false">Transient</option>
</select>
</td>
</tr>
<tr>
<th><label>Auto delete: <span class="help" id="exchange-auto-delete"></span></label></th>
<td>
<select name="auto_delete">
<option value="false">No</option>
<option value="true">Yes</option>
</select>
</td>
</tr>
<tr>
<th><label>Internal: <span class="help" id="exchange-internal"></span></label></th>
<td>
<select name="internal">
<option value="false">No</option>
<option value="true">Yes</option>
</select>
</td>
</tr>
<tr>
<th><label>Arguments:</label></th>
<td>
<div class="multifield" id="arguments"></div>
<table class="argument-links">
<tr>
<td>Add</td>
<td>
<span class="argument-link" field="arguments" key="alternate-exchange" type="string">Alternate exchange</span>
<span class="help" id="exchange-alternate"></span>
</td>
</tr>
</table>
</td>
</tr>
</table>
<input type="submit" value="Add exchange"/>
</form>
</div>
</div>
<h1>Feature Flags</h1>
<div class="section">
<h2>All Feature Flags</h2>
<div class="hider">
<%= filter_ui(feature_flags) %>
<div class="updatable">
<% if (feature_flags.length > 0) { %>
<table class="list">
<thead>
<tr>
<th><%= fmt_sort('Name', 'name') %></th>
<th class="c"><%= fmt_sort('State', 'state') %></th>
<th>Description</th>
</tr>
</thead>
<tbody>
<%
for (var i = 0; i < feature_flags.length; i++) {
var feature_flag = feature_flags[i];
var state_color = "grey";
if (feature_flag.state == "enabled") {
state_color = "green";
} else if (feature_flag.state == "disabled") {
state_color = "yellow";
} else if (feature_flag.state == "unsupported") {
state_color = "red";
}
%>
<tr<%= alt_rows(i)%>>
<td><%= fmt_string(feature_flag.name) %></td>
<td class="c">
<% if (feature_flag.state == "disabled") { %>
<form action="#/feature-flags-enable" method="put" style="display: inline-block">
<input type="hidden" name="name" value="<%= fmt_string(feature_flag.name) %>"/>
<input type="submit" value="Enable" class="c"/>
</form>
<% } else { %>
<abbr class="status-<%= fmt_string(state_color) %>"
style="text-transform: capitalize"
title="Feature flag state: <%= fmt_string(feature_flag.state) %>">
<%= fmt_string(feature_flag.state) %>
</abbr>
<% } %>
</td>
<td>
<p><%= fmt_string(feature_flag.desc) %></p>
<% if (feature_flag.doc_url) { %>
<p><a href="<%= fmt_string(feature_flag.doc_url) %>">[Learn more]</a></p>
<% } %>
</td>
</tr>
<% } %>
</tbody>
</table>
<% } else { %>
<p>... no feature_flags ...</p>
<% } %>
</div>
</div>
</div>
<div id="header">
<ul id="topnav">
<li id="interval">
<label for="update-every" id="status"></label>
<select id="update-every">
<option value="5000">Refresh every 5 seconds</option>
<option value="10000">Refresh every 10 seconds</option>
<option value="30000">Refresh every 30 seconds</option>
<option value="">Do not refresh</option>
</select>
</li>
<li id="vhost">
<label for="show-vhost">Virtual host </label>
<select id="show-vhost">
<option value="">All</option>
</select>
</li>
<li id="logout">
<form action="#/logout" method="put">
<input type="submit" value="Log out"/>
</form>
</li>
</ul>
<div id="logo">
<a href="#/"><img src="img/rabbitmqlogo.png" alt="RabbitMQ logo" width="204" height="37"/></a>
<span id="versions"></span>
</div>
<div id="menu">
<ul id="tabs">
</ul>
</div>
</div>
<div id="rhs"></div>
<div id="main"></div>
<div id="footer">
<ul>
<li><a href="api/" target="_blank">HTTP API</a></li>
<li><a href="https://www.rabbitmq.com/admin-guide.html" target="_blank">Server Docs</a></li>
<li><a href="https://www.rabbitmq.com/getstarted.html" target="_blank">Tutorials</a></li>
<li><a href="https://groups.google.com/forum/#!forum/rabbitmq-users" target="_blank">Community Support</a></li>
<li><a href="https://rabbitmq-slack.herokuapp.com/" target="_blank">Community Slack</a></li>
<li><a href="https://www.rabbitmq.com/services.html" target="_blank">Commercial Support</a></li>
<li><a href="https://www.rabbitmq.com/plugins.html" target="_blank">Plugins</a></li>
<li><a href="https://www.rabbitmq.com/github.html" target="_blank">GitHub</a></li>
<li><a href="https://www.rabbitmq.com/changelog.html" target="_blank">Changelog</a></li>
</ul>
</div>
<h1>Limits</h1>
<div class="section">
<h2>Virtual host Limits</h2>
<div class="hider">
<div class="updatable">
<% if (limits.length > 0) { %>
<table class="list">
<thead>
<tr>
<th>Virtual Host</th>
<th>Limit</th>
<th>Value</th>
<th class="administrator-only"></th>
</tr>
</thead>
<tbody>
<% for (var i = 0; i < limits.length; i++) {
var limit = limits[i];
var limit_values = Object.keys(limit.value).sort().map(
function(k) { return {name: k, value: limit.value[k]};});
%>
<% for (var j = 0; j < limit_values.length; j++) {
var limit_value = limit_values[j];
%>
<tr<%= alt_rows(j+1)%>>
<% if(j == 0) { %>
<td rowspan="<%= limit_values.length %>"> <%= fmt_string(limit.vhost) %> </td>
<% } %>
<td><%= limit_value.name %></td>
<td><%= limit_value.value %></td>
<td class="administrator-only">
<form action="#/limits" method="delete" class="confirm">
<input type="hidden" name="name" value="<%= fmt_string(limit_value.name) %>"/>
<input type="hidden" name="vhost" value="<%= fmt_string(limit.vhost) %>"/>
<input type="submit" value="Clear"/>
</form>
</td>
</tr>
<% } %>
<% } %>
</tbody>
</table>
<% } %>
</div>
</div>
</div>
<div class="section administrator-only">
<h2>Set / update a limit</h2>
<div class="hider">
<form action="#/limits" method="put">
<table class="form">
<tr>
<th><label>Virtual host:</label></th>
<td>
<select name="vhost">
<% for (var i = 0; i < vhosts.length; i++) { %>
<option value="<%= fmt_string(vhosts[i].name) %>">
<%= fmt_string(vhosts[i].name) %>
</option>
<% } %>
</select>
</td>
</tr>
<tr>
<th><label>Limit:</label></th>
<td>
<select name="name">
<option value="max-connections">max-connections</option>
<option value="max-queues">max-queues</option>
</select>
</td>
</tr>
<tr>
<th><label>Value:</label></th>
<td>
<input type="text" name="value"/>
<span class="mand">*</span>
</td>
</tr>
</table>
<input type="submit" value="Set / update limit"/>
</form>
</div>
</div>
<select name="exchange">
<% for (var i = 0; i < exchanges.length; i++) { %>
<option value="<%= fmt_string(exchanges[i].name) %>"><%= fmt_exchange(exchanges[i].name) %></option>
<% } %>
</select>
<div id="login">
<p><img src="img/rabbitmqlogo.png" alt="RabbitMQ logo" width="204" height="37"/></p>
<form action="#/login" method="put">
<div id="login-status"></div>
<table class="form">
<tr>
<th><label>Username:</label></th>
<td><input type="text" name="username"/><span class="mand">*</span></td>
</tr>
<tr>
<th><label>Password:</label></th>
<td><input type="password" name="password"/><span class="mand">*</span></td>
</tr>
<tr>
<th>&nbsp;</th>
<td><input type="submit" value="Login"/></td>
</tr>
</table>
</form>
</div>
<div class="memory-bar">
<%
var width = 800;
var pseudo_total = 0
for (var section in sections) {
pseudo_total += memory[section];
}
total_out[0] = pseudo_total;
for (var section in sections) {
if (memory[section] > 0) {
var section_width = Math.round(width * memory[section] / pseudo_total);
%>
<div class="memory-section memory_<%= sections[section][0] %>"
style="width: <%= section_width %>px;"
title="<%= sections[section][1] %> <%= fmt_bytes(memory[section]) %>">
</div>
<%
}
}
%>
</div>
<%
for (var i in key) {
%>
<table class="facts">
<%
for (var j in key[i]) {
var group = key[i][j];
%>
<tr>
<th><div class="colour-key memory_<%= group.colour %>"></div><%= group.name %></th>
<td>
<table class="mini">
<%
for (var k in group.keys) {
var name = group.keys[k][0];
var label = group.keys[k][1];
%>
<tr>
<td class="r"><%= fmt_bytes(memory[name]) %></td>
<td><%= label %></td>
</tr>
<% } %>
</table>
</td>
</tr>
<% } %>
</table>
<% } %>
<%
if (memory == "not_available") {
%>
<p class="warning">
Memory statistics not available.
</p>
<% } else { %>
<%
var sections = {'queue_procs' : ['queue', 'Queues (masters)'],
'queue_slave_procs' : ['queue', 'Queues (mirrors)'],
'binary' : ['binary', 'Binaries'],
'connection_readers' : ['conn', 'Connection readers'],
'connection_writers' : ['conn', 'Connection writers'],
'connection_channels' : ['conn', 'Connection channels'],
'connection_other' : ['conn', 'Connections (other)'],
'mnesia' : ['table', 'Mnesia'],
'msg_index' : ['table', 'Message store index'],
'mgmt_db' : ['table', 'Management database'],
'other_ets' : ['table', 'Other ETS tables'],
'plugins' : ['proc', 'Plugins'],
'other_proc' : ['proc', 'Other process memory'],
'code' : ['system', 'Code'],
'atom' : ['system', 'Atoms'],
'other_system' : ['system', 'Other system'],
'allocated_unused' : ['unused', 'Allocated unused'],
'reserved_unallocated': ['unused', 'Unallocated reserved by the OS']};
%>
<%= format('memory-bar', {sections: sections, memory: memory, total_out: []}) %>
<span class="clear">&nbsp;</span>
<div class="box">
<%
var key = [[{name: 'Queues', colour: 'queue',
keys: [['queue_procs', 'queues'],
['queue_slave_procs', 'mirrors']]},
{name: 'Binaries', colour: 'binary',
keys: [['binary', '']]}],
[{name: 'Connections', colour: 'conn',
keys: [['connection_readers', 'readers'],
['connection_writers', 'writers'],
['connection_channels', 'channels'],
['connection_other', 'other']]}],
[{name: 'Tables', colour: 'table',
keys: [['mnesia', 'internal database tables'],
['msg_index', 'message store index'],
['mgmt_db', 'management database'],
['other_ets', 'other']]}],
[{name: 'Processes', colour: 'proc',
keys: [['plugins', 'plugins'],
['other_proc', 'other']]},
{name: 'System', colour: 'system',
keys: [['code', 'code'],
['atom', 'atoms'],
['other_system', 'other']
]}],
[{name: 'Preallocated memory', colour: 'unused',
keys: [['allocated_unused', 'preallocated by runtime, unused'],
['reserved_unallocated', 'unallocated, reserved by the OS']]}]];
%>
<%= format('memory-table', {key: key, memory: memory}) %>
</div>
<div class="memory-info">
Last updated: <b><%= fmt_date(new Date()) %></b>.<br/>
Memory calculation strategy: <b><%= memory.strategy %></b>. <span class="help" id="memory-calculation-strategy-breakdown"></span><br/><br/>
Amount of memory used vs. allocated during last update: <span class="help" id="memory-use"></span><br/>
<table class="facts">
<tr>
<th>Runtime Used</th>
<td><%= fmt_bytes(memory.total.erlang) %></td>
</tr>
<tr>
<th>Runtime Allocated</th>
<td><%= fmt_bytes(memory.total.allocated) %></td>
</tr>
<tr>
<th>Resident Set Size (RSS) reported by the OS</th>
<td><%= fmt_bytes(memory.total.rss) %></td>
</tr>
</table>
</div>
<% } %>
<%
for (var i = 0; i < msgs.length; i++) {
var msg = msgs[i];
%>
<div class="box">
<h3>Message <%= i+1 %></h3>
<p>The server reported <b><%= msg.message_count %></b> messages remaining.</p>
<table class="facts">
<tr>
<th>Exchange</th>
<td><%= fmt_exchange(msg.exchange) %></td>
</tr>
<tr>
<th>Routing Key</th>
<td><%= fmt_string(msg.routing_key) %></td>
</tr>
<tr>
<th>Redelivered</th>
<td><%= fmt_boolean(msg.redelivered) %></td>
</tr>
<tr>
<th>Properties</th>
<td><%= fmt_table_short(msg.properties) %></td>
</tr>
<tr>
<th>
Payload
<sub><%= msg.payload_bytes %> bytes</sub>
<sub>Encoding: <%= msg.payload_encoding %></sub>
</th>
<td>
<pre class="msg-payload"><%= fmt_maybe_wrap(msg.payload, msg.payload_encoding) %></pre>
</td>
</tr>
</table>
</div>
<%
}
%>
<h3>Deliveries</h3>
<% if (object && object.length > 0) { %>
<table class="list">
<tr>
<% if (mode == 'queue') { %>
<th>Channel</th>
<% } else { %>
<th>Queue</th>
<% } %>
<th>deliver / get</th>
<th>ack</th>
</tr>
<%
for (var i = 0; i < object.length; i++) {
var del = object[i];
%>
<tr<%= alt_rows(i)%>>
<% if (mode == 'queue') { %>
<td><%= link_channel(del.channel_details.name) %></td>
<% } else { %>
<td><%= link_queue(del.queue.vhost, del.queue.name) %></td>
<% } %>
<td class="r"><%= fmt_detail_rate(del.stats, 'deliver_get') %></td>
<td class="r"><%= fmt_detail_rate(del.stats, 'ack') %></td>
</tr>
<% } %>
</table>
<% } else { %>
<p> ... no deliveries ...</p>
<% } %>
<h3><%= label %></h3>
<% if (object && object.length > 0) { %>
<%
var col_confirm = mode != 'exchange-outgoing';
%>
<table class="list">
<tr>
<% if (mode == 'channel') { %>
<th>Exchange</th>
<% } else if (mode == 'exchange-incoming') { %>
<th>Channel</th>
<% } else if (mode == 'exchange-outgoing') { %>
<th>Queue</th>
<% } else { %>
<th>Exchange</th>
<% } %>
<th>publish</th>
<% if (col_confirm) { %>
<th>confirm</th>
<% } %>
</tr>
<%
for (var i = 0; i < object.length; i++) {
var pub = object[i];
%>
<tr<%= alt_rows(i)%>>
<% if (mode == 'channel') { %>
<td><%= link_exchange(pub.exchange.vhost, pub.exchange.name) %></td>
<% } else if (mode == 'exchange-incoming') { %>
<td><%= link_channel(pub.channel_details.name) %></td>
<% } else if (mode == 'exchange-outgoing') { %>
<td><%= link_queue(pub.queue.vhost, pub.queue.name) %></td>
<% } else { %>
<td><%= link_exchange(pub.exchange.vhost, pub.exchange.name) %></td>
<% } %>
<td class="r"><%= fmt_detail_rate(pub.stats, 'publish') %></td>
<% if (col_confirm) { %>
<td class="r"><%= fmt_detail_rate(pub.stats, 'confirm') %></td>
<% } %>
</tr>
<% } %>
</table>
<% } else { %>
<p> ... no publishes ...</p>
<% } %>
<h1>Node <b><%= node.name %></b></h1>
<div class="updatable">
<% if (!node.running) { %>
<p class="warning">Node not running</p>
<% } else if (node.os_pid == undefined) { %>
<p class="warning">Node statistics not available</p>
<% } else { %>
<div class="section">
<h2>Overview</h2>
<div class="hider">
<div class="box">
<table class="facts facts-l">
<tr>
<th>Uptime</th>
<td><%= fmt_uptime(node.uptime) %></td>
</tr>
<% if (rabbit_versions_interesting) { %>
<tr>
<th>RabbitMQ Version</th>
<td><%= fmt_rabbit_version(node.applications) %></td>
</tr>
<% } %>
<tr>
<th>Type</th>
<td>
<% if (node.type == 'disc') { %>
<abbr title="Broker definitions are held on disc.">Disc</abbr>
<% } else { %>
<abbr title="Broker definitions are held in RAM. Messages will still be written to disc if necessary.">RAM</abbr>
<% } %>
</td>
</tr>
<tr>
<th>
<a href="https://www.rabbitmq.com/configure.html" target="_blank">Config file</a>
</th>
<td>
<%
for (var i = 0; i < node.config_files.length; i++) {
var config = fmt_escape_html(node.config_files[i]);
%>
<code><%= config %></code>
<% } %>
</td>
</tr>
<tr>
<th>Database directory</th>
<td>
<code><%= node.db_dir %></code>
</td>
</tr>
<tr>
<th>
<% if (node.log_files.length == 1) { %>
Log file
<% } else { %>
Log files
<% } %>
</th>
<td>
<pre style="margin-top: 0px; margin-bottom: 0px;"><%
for (var i = 0; i < node.log_files.length; i++) {
var config = fmt_escape_html(node.log_files[i]);
%><%= config %>
<% } %></pre>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="section">
<h2>Process statistics</h2>
<div class="hider">
<%= node_stats_prefs() %>
<table class="facts">
<tr>
<th>
File descriptors <span class="help" id="file-descriptors"></span>
</th>
<td>
<% if (node.fd_used != 'install_handle_from_sysinternals') { %>
<%= node_stat_count('fd_used', 'fd_total', node, FD_THRESHOLDS) %>
<% } else { %>
<p class="c">handle.exe missing <span class="help" id="handle-exe"></span><sub><%= node.fd_total %> available</sub></p>
<% } %>
</td>
</tr>
<tr>
<th>
Socket descriptors <span class="help" id="socket-descriptors"></span>
</th>
<td>
<%= node_stat_count('sockets_used', 'sockets_total', node, FD_THRESHOLDS) %>
</td>
</tr>
<tr>
<th>
Erlang processes
</th>
<td>
<%= node_stat_count('proc_used', 'proc_total', node, PROCESS_THRESHOLDS) %>
</td>
</tr>
<tr>
<th>
Memory <span class="help" id="memory-calculation-strategy"></span>
</th>
<td>
<% if (node.mem_limit != 'memory_monitoring_disabled') { %>
<%= node_stat('mem_used', 'Used', 'mem_limit', 'high watermark', node,
fmt_bytes, fmt_bytes_axis,
node.mem_alarm ? 'red' : 'green',
node.mem_alarm ? 'memory-alarm' : null) %>
<% } else { %>
<%= fmt_bytes(node.mem_used) %>
<% } %>
</td>
</tr>
<tr>
<th>
Disk space
</th>
<td>
<% if (node.disk_free_limit != 'disk_free_monitoring_disabled') { %>
<%= node_stat('disk_free', 'Free', 'disk_free_limit', 'low watermark', node,
fmt_bytes, fmt_bytes_axis,
node.disk_free_alarm ? 'red' : 'green',
node.disk_free_alarm ? 'disk_free-alarm' : null,
true) %>
<% } else { %>
(not available)
<% } %>
</td>
</tr>
</table>
</div>
</div>
<div class="section-hidden">
<h2>Persistence statistics</h2>
<div class="hider">
<%= rates_chart_or_text('mnesia-stats-count', node,
[['RAM only', 'mnesia_ram_tx_count'],
['Disk', 'mnesia_disk_tx_count']],
fmt_rate, fmt_rate_axis, true, 'Mnesia transactions', 'mnesia-transactions') %>
<%= rates_chart_or_text('persister-msg-stats-count', node,
[['QI Journal', 'queue_index_journal_write_count'],
['Store Read', 'msg_store_read_count'],
['Store Write', 'msg_store_write_count']],
fmt_rate, fmt_rate_axis, true, 'Persistence operations (messages)', 'persister-operations-msg') %>
<%= rates_chart_or_text('persister-bulk-stats-count', node,
[['QI Read', 'queue_index_read_count'],
['QI Write', 'queue_index_write_count']],
fmt_rate, fmt_rate_axis, true, 'Persistence operations (bulk)', 'persister-operations-bulk') %>
</div>
</div>
<div class="section-hidden">
<h2>I/O statistics</h2>
<div class="hider">
<%= rates_chart_or_text('persister-io-stats-count', node,
[['Read', 'io_read_count'],
['Write', 'io_write_count'],
['Seek', 'io_seek_count'],
['Sync', 'io_sync_count'],
['File handle reopen', 'io_reopen_count'],
['File handle open attempt', 'io_file_handle_open_attempt_count']],
fmt_rate, fmt_rate_axis, true, 'I/O operations', 'io-operations') %>
<%= rates_chart_or_text('persister-io-stats-bytes', node,
[['Read', 'io_read_bytes'],
['Write', 'io_write_bytes']],
fmt_rate_bytes, fmt_rate_bytes_axis, true, 'I/O data rates') %>
<%= rates_chart_or_text('persister-io-stats-time', node,
[['Read', 'io_read_avg_time'],
['Write', 'io_write_avg_time'],
['Seek', 'io_seek_avg_time'],
['Sync', 'io_sync_avg_time'],
['File handle open attempt', 'io_file_handle_open_attempt_avg_time']],
fmt_ms, fmt_ms, false, 'I/O average time per operation') %>
</div>
</div>
<div class="section-hidden">
<h2>Churn statistics</h2>
<div class="hider">
<%= rates_chart_or_text('connection-churn', node,
[['Created', 'connection_created'],
['Closed', 'connection_closed']],
fmt_rate, fmt_rate_axis, true, 'Connection operations', 'connection-operations') %>
<%= rates_chart_or_text('channel-churn', node,
[['Created', 'channel_created'],
['Closed', 'channel_closed']],
fmt_rate, fmt_rate_axis, true, 'Channel operations', 'channel-operations') %>
<%= rates_chart_or_text('queue-churn', node,
[['Declared', 'queue_declared'],
['Created', 'queue_created'],
['Deleted', 'queue_deleted']],
fmt_rate, fmt_rate_axis, true, 'Queue operations', 'queue-operations') %>
</div>
</div>
<div class="section-hidden">
<h2>Cluster links</h2>
<div class="hider">
<% if (node.cluster_links.length > 0) { %>
<table class="list">
<tr>
<th>Remote node</th>
<th>Local address</th>
<th>Local port</th>
<th>Remote address</th>
<th>Remote port</th>
<th class="plain">
<%= chart_h3('cluster-link-data-rates', 'Data rates') %>
</th>
</tr>
<%
for (var i = 0; i < node.cluster_links.length; i++) {
var link = node.cluster_links[i];
%>
<tr<%= alt_rows(i)%>>
<td><%= link_node(link.name) %></td>
<td><%= fmt_string(link.sock_addr) %></td>
<td><%= fmt_string(link.sock_port) %></td>
<td><%= fmt_string(link.peer_addr) %></td>
<td><%= fmt_string(link.peer_port) %></td>
<td class="plain">
<%= rates_chart_or_text_no_heading(
'cluster-link-data-rates', 'cluster-link-data-rates' + link.name,
link.stats,
[['Recv', 'recv_bytes'],
['Send', 'send_bytes']],
fmt_rate_bytes, fmt_rate_bytes_axis, true) %>
</td>
</tr>
<% } %>
</table>
<% } else { %>
<p>... no cluster links ...</p>
<% } %>
</div>
</div>
<% } %>
</div>
<!--
The next two need to be non-updatable or we will wipe the memory details
as soon as we have drawn it.
-->
<% if (node.running && node.os_pid != undefined) { %>
<div class="section">
<h2>Memory details</h2>
<div class="hider">
<div id="memory-details"></div>
<button class="update-manual memory-button" for="memory-details" query="memory">Update</button>
</div>
</div>
<div class="section-hidden">
<h2>Binary references</h2>
<div class="hider">
<p>
<b>Warning:</b> Calculating binary memory use can be expensive if
there are many small binaries in the system.
</p>
<div id="binary-details"></div>
<button class="update-manual memory-button" for="binary-details" query="binary">Update</button>
</div>
</div>
<% } %>
<div class="updatable">
<% if (node.running && node.os_pid != undefined) { %>
<div class="section-hidden">
<h2>Advanced</h2>
<div class="hider">
<div class="box">
<h3>VM</h3>
<table class="facts">
<tr>
<th>OS pid</th>
<td><%= node.os_pid %></td>
</tr>
<tr>
<th>Rates mode</th>
<td><%= node.rates_mode %></td>
</tr>
<tr>
<th>Net ticktime</th>
<td><%= node.net_ticktime %>s</td>
</tr>
</table>
<table class="facts">
<tr>
<th>Run queue</th>
<td><%= node.run_queue %></td>
</tr>
<tr>
<th>Processors</th>
<td><%= node.processors %></td>
</tr>
</table>
</div>
<%= rates_chart_or_text('advanced-gc-stats-count', node,
[['GC', 'gc_num']],
fmt_rate, fmt_rate_axis, true, 'GC operations', 'gc-operations') %>
<%= rates_chart_or_text('advanced-gc-bytes-stats-count', node,
[['GC bytes reclaimed', 'gc_bytes_reclaimed']],
fmt_rate, fmt_rate_axis, true, 'GC bytes reclaimed', 'gc-bytes') %>
<%= rates_chart_or_text('advanced-context-switches-stats-count', node,
[['Context switches', 'context_switches']],
fmt_rate, fmt_rate_axis, true, 'Context switch operations', 'context-switches-operations') %>
<div class="box">
<h3>Management GC queue length</h3>
<table class="facts">
<% for(var k in node.metrics_gc_queue_length) {
if(node.metrics_gc_queue_length.hasOwnProperty(k)) { %>
<tr>
<th><%= k %></th>
<td><%= node.metrics_gc_queue_length[k] %></td>
</tr>
<% } } %>
</table>
</div>
<h3>Plugins <span class="help" id="plugins"></span></h3>
<table class="list">
<tr>
<th>Name</th>
<th>Version</th>
<th>Description</th>
</tr>
<%
var plugins = get_plugins_list(node);
for (var j = 0; j < plugins.length; j++) {
var application = plugins[j];
%>
<tr<%= alt_rows(j)%>>
<td><%= application.name %></td>
<td><%= application.version %></td>
<td><%= application.description %></td>
</tr>
<% } %>
</table>
<h3>All applications</h3>
<table class="list">
<tr>
<th>Name</th>
<th>Version</th>
<th>Description</th>
</tr>
<%
for (var j = 0; j < node.applications.length; j++) {
var application = node.applications[j];
%>
<tr<%= alt_rows(j)%>>
<td><%= application.name %></td>
<td><%= application.version %></td>
<td><%= application.description %></td>
</tr>
<% } %>
</table>
<h3>Exchange types</h3>
<%= format('registry', {'list': node.exchange_types, 'node': node, 'show_enabled': false} ) %>
<h3>Authentication mechanisms</h3>
<%= format('registry', {'list': node.auth_mechanisms, 'node': node, 'show_enabled': true} ) %>
</div>
</div>
<% } %>
</div>
<h1>Overview</h1>
<% if (user_monitor) { %>
<%= format('partition', {'nodes': nodes}) %>
<% } %>
<div class="updatable">
<% if (overview.statistics_db_event_queue > 1000) { %>
<p class="warning">
The management statistics database currently has a queue
of <b><%= overview.statistics_db_event_queue %></b> events to
process. If this number keeps increasing, so will the memory used by
the management plugin.
<% if (overview.rates_mode != 'none') { %>
You may find it useful to set the <code>rates_mode</code> config item
to <code>none</code>.
<% } %>
</p>
<% } %>
<% for (i = 0; i < vhosts.length; i++)
{
for (var vhost_status_node in vhosts[i].cluster_state) {
if (vhosts[i].cluster_state[vhost_status_node] != 'running') {
%>
<p class="warning">
Virtual host <b><%= vhosts[i].name %></b> experienced an error on node <b><%= vhost_status_node %></b> and may be inaccessible
</p>
<% }}} %>
</div>
<div class="section">
<h2>Totals</h2>
<div class="hider updatable">
<%= queue_lengths('lengths-over', overview.queue_totals) %>
<% if (rates_mode != 'none') { %>
<%= message_rates('msg-rates-over', overview.message_stats) %>
<% } %>
<% if (overview.object_totals) { %>
<h3>Global counts <span class="help" id="resource-counts"></span></h3>
<ul id="global-counts">
<li>
<a href="#/connections" class="button">Connections: <strong><%= overview.object_totals.connections %></strong></a>
</li>
<li>
<a href="#/channels" class="button">Channels: <strong><%= overview.object_totals.channels %></strong></a>
</li>
<li>
<a href="#/exchanges" class="button">Exchanges: <strong><%= overview.object_totals.exchanges %></strong></a>
</li>
<li>
<a href="#/queues" class="button">Queues: <strong><%= overview.object_totals.queues %></strong></a>
</li>
<% if (overview.object_totals['consumers'] != undefined) { %>
<li>
<a href="#" class="button disabled">Consumers: <strong><%= overview.object_totals.consumers %></strong></a>
</li>
<% } %>
</ul>
<% } %>
</div>
</div>
<% if (user_monitor) { %>
<div class="section">
<h2>Nodes</h2>
<div class="hider updatable">
<table class="list">
<tr>
<th>Name</th>
<% if (show_column('overview', 'file_descriptors')) { %>
<th>File descriptors <span class="help" id="file-descriptors"></span></th>
<% } %>
<% if (show_column('overview', 'socket_descriptors')) { %>
<th>Socket descriptors <span class="help" id="socket-descriptors"></span></th>
<% } %>
<% if (show_column('overview', 'erlang_processes')) { %>
<th>Erlang processes</th>
<% } %>
<% if (show_column('overview', 'memory')) { %>
<th>Memory <span class="help" id="memory-calculation-strategy"></span></th>
<% } %>
<% if (show_column('overview', 'disk_space')) { %>
<th>Disk space</th>
<% } %>
<% if (show_column('overview', 'uptime')) { %>
<th>Uptime</th>
<% } %>
<% if (show_column('overview', 'info')) { %>
<th>Info</th>
<% } %>
<% if (user_administrator && show_column('overview', 'reset_stats')) { %>
<th>Reset stats</th>
<% } %>
<th class="plus-minus"><span class="popup-options-link" title="Click to change columns" type="columns" for="overview">+/-</span></th>
</tr>
<%
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
var colspan = group_count('overview', 'Statistics', []) +
group_count('overview', 'General', []);
%>
<tr<%= alt_rows(i)%>>
<td>
<a href="#/nodes/<%= esc(node.name) %>" class="button"><%= fmt_node(node.name) %></a>
<% if (rabbit_versions_interesting) { %>
<sub>RabbitMQ <%= fmt_rabbit_version(node.applications) %></sub>
<% } %>
</td>
<% if (!node.running) { %>
<td colspan="<%= colspan %>">
<div class="status-red">
Node not running
</div>
</td>
<% } else if (node.os_pid == undefined) { %>
<td colspan="<%= colspan %>">
<div class="status-yellow">
<abbr title="The rabbitmq_management_agent plugin should be enabled on this node. If it is not, various statistics will be inaccurate.">
Node statistics not available</abbr>
</div>
</td>
<% } else { %>
<% if (show_column('overview', 'file_descriptors')) { %>
<td>
<% if (node.fd_used != 'install_handle_from_sysinternals') { %>
<%= node_stat_count_bar('fd_used', 'fd_total', node, FD_THRESHOLDS) %>
<% } else { %>
<p class="c">handle.exe missing <span class="help" id="handle-exe"></span><sub><%= node.fd_total %> available</sub></p>
<% } %>
</td>
<% } %>
<% if (show_column('overview', 'socket_descriptors')) { %>
<td>
<%= node_stat_count_bar('sockets_used', 'sockets_total', node, FD_THRESHOLDS) %>
</td>
<% } %>
<% if (show_column('overview', 'erlang_processes')) { %>
<td>
<%= node_stat_count_bar('proc_used', 'proc_total', node, PROCESS_THRESHOLDS) %>
</td>
<% } %>
<% if (show_column('overview', 'memory')) { %>
<td>
<% if (node.mem_limit != 'memory_monitoring_disabled') { %>
<%= node_stat_bar('mem_used', 'mem_limit', 'high watermark', node, fmt_bytes_axis,
node.mem_alarm ? 'red' : 'green',
node.mem_alarm ? 'memory-alarm' : null) %>
<% } else { %>
<%= fmt_bytes(node.mem_used) %>
<% } %>
</td>
<% } %>
<% if (show_column('overview', 'disk_space')) { %>
<td>
<% if (node.disk_free_limit != 'disk_free_monitoring_disabled') { %>
<%= node_stat_bar('disk_free', 'disk_free_limit', 'low watermark', node, fmt_bytes_axis,
node.disk_free_alarm ? 'red' : 'green',
node.disk_free_alarm ? 'disk_free-alarm' : null, true) %>
<% } else { %>
(not available)
<% } %>
</td>
<% } %>
<% if (show_column('overview', 'uptime')) { %>
<td><span><%= fmt_uptime(node.uptime) %></span></td>
<% } %>
<% if (show_column('overview', 'info')) { %>
<td>
<abbr title="Message rates"><%= fmt_string(node.rates_mode) %></abbr>
<% if (node.type == 'disc') { %>
<abbr title="Broker definitions are held on disc.">disc</abbr>
<% } else { %>
<abbr title="Broker definitions are held in RAM. Messages will still be written to disc if necessary.">RAM</abbr>
<% } %>
<%= fmt_plugins_small(node) %>
<abbr title="Memory calculation strategy"><%= fmt_string(node.mem_calculation_strategy) %></abbr>
</td>
<% } %>
<% if(user_administrator && show_column('overview', 'reset_stats')) { %>
<td>
<form action="#/reset_node" method="delete" class="confirm inline-form">
<input type="hidden" name="node" value="<%= node.name %>"/>
<input type="submit" value="This node"/>
</form>
<form action="#/reset" method="delete" class="confirm inline-form-right">
<input type="submit" value="All nodes"/>
</form>
<% } %>
<% } %>
</tr>
<% } %>
</table>
</div>
</div>
<div class="section-hidden">
<h2>Churn statistics</h2>
<div class="hider updatable">
<%= rates_chart_or_text('connection-churn', overview.churn_rates,
[['Created', 'connection_created'],
['Closed', 'connection_closed']],
fmt_rate, fmt_rate_axis, true, 'Connection operations', 'connection-operations') %>
<%= rates_chart_or_text('channel-churn', overview.churn_rates,
[['Created', 'channel_created'],
['Closed', 'channel_closed']],
fmt_rate, fmt_rate_axis, true, 'Channel operations', 'channel-operations') %>
<%= rates_chart_or_text('queue-churn', overview.churn_rates,
[['Declared', 'queue_declared'],
['Created', 'queue_created'],
['Deleted', 'queue_deleted']],
fmt_rate, fmt_rate_axis, true, 'Queue operations', 'queue-operations') %>
</div>
</div>
<div class="section-hidden">
<h2>Ports and contexts</h2>
<div class="hider updatable">
<h3>Listening ports</h3>
<table class="list">
<tr>
<th>Protocol</th>
<% if (nodes_interesting) { %>
<th>Node</th>
<% } %>
<th>Bound to</th>
<th>Port</th>
</tr>
<%
for (var i = 0; i < overview.listeners.length; i++) {
var listener = overview.listeners[i];
%>
<tr<%= alt_rows(i)%>>
<td><%= listener.protocol %></td>
<% if (nodes_interesting) { %>
<td><%= fmt_node(listener.node) %></td>
<% } %>
<td><%= listener.ip_address %></td>
<td><%= listener.port %></td>
</tr>
<% } %>
</table>
<h3>Web contexts</h3>
<table class="list">
<tr>
<th>Context</th>
<% if (nodes_interesting) { %>
<th>Node</th>
<% } %>
<th>Bound to</th>
<th>Port</th>
<th>SSL</th>
<th>Path</th>
</tr>
<%
for (var i = 0; i < overview.contexts.length; i++) {
var context = overview.contexts[i];
%>
<tr<%= alt_rows(i)%>>
<td><%= context.description %></td>
<% if (nodes_interesting) { %>
<td><%= fmt_node(context.node) %></td>
<% } %>
<td><%= (context.ip != undefined) ? context.ip : "0.0.0.0" %></td>
<td><%= context.port %></td>
<td class="c"><%= fmt_boolean(context.ssl || false) %></td>
<td><%= context.path %></td>
</tr>
<% } %>
</table>
</div>
</div>
<div class="section-hidden administrator-only">
<h2>Export definitions</h2>
<div class="hider">
<table class="two-col-layout">
<tr>
<td>
<p>
<label for="download-filename">Filename for download:</label><br/>
<input type="text" id="download-filename" value="<%= fmt_download_filename(overview.node) %>" class="wide" />
</p>
</td>
<td>
<p>
<button id="download-definitions">Download broker definitions</button>
<span class="help" id="export-definitions"></span>
</p>
</td>
</tr>
<tr>
<td>
<% if (vhosts_interesting) { %>
<label>Virtual host:</label>
<select name="vhost-download">
<option value="all">All</option>
<% for (var i = 0; i < vhosts.length; i++) { %>
<option value="<%= fmt_string(vhosts[i].name) %>"><%= fmt_string(vhosts[i].name) %></option>
<% } %>
</select> <span class="help" id="export-definitions-vhost"></span>
<% } else { %>
<input type="hidden" name="vhost" value="all"/>
<% } %>
</td>
</tr>
</table>
</div>
</div>
<div class="section-hidden administrator-only">
<h2>Import definitions</h2>
<div class="hider">
<form method="post" enctype="multipart/form-data">
<table class="two-col-layout">
<tr>
<td>
<p>
<label>Definitions file:</label><br/>
<input type="file" name="file"/>
</p>
</td>
<td>
<p>
<input type="submit" value="Upload broker definitions" onclick="submit_import($(this).closest('form')[0]); return false"/>
<span class="help" id="import-definitions"></span>
</p>
</td>
</tr>
<tr>
<td>
<% if (vhosts_interesting) { %>
<label>Virtual host:</label>
<select name="vhost-upload">
<option value="all">All</option>
<% for (var i = 0; i < vhosts.length; i++) { %>
<option value="<%= fmt_string(vhosts[i].name) %>"><%= fmt_string(vhosts[i].name) %></option>
<% } %>
</select> <span class="help" id="import-definitions-vhost"></span>
<% } else { %>
<input type="hidden" name="vhost" value="all"/>
<% } %>
</td>
</tr>
</table>
</form>
</div>
</div>
<% if (overview.rates_mode == 'none') { %>
<div class="section-hidden">
<h2>Message rates disabled</h2>
<div class="hider">
<p>
Message rates are currently disabled.
</p>
<p>
To re-enable message rates, edit your configuration file and
set <code>rates_mode</code> to <code>basic</code>
or <code>detailed</code> in the <code>rabbitmq_management</code>
application
</p>
</div>
</div>
<% } %>
<% } %>
<div class="updatable">
<%
var partitions = [];
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
if (node.partitions != undefined && node.partitions.length != 0) {
partitions.push({'node': node.name,
'others': node.partitions});
}
}
if (partitions.length > 0) {
%>
<p class="status-error">
Network partition detected<br/><br/>
Mnesia reports that this RabbitMQ cluster has experienced a
network partition. There is a risk of losing data. Please read
<a href="https://www.rabbitmq.com/partitions.html">RabbitMQ
documentation about network partitions and the possible solutions</a>.
</p>
<p>
The nature of the partition is as follows:
</p>
<table class="list">
<tr>
<th>Node</th><th>Was partitioned from</th>
</tr>
<%
for (var i = 0; i < partitions.length; i++) {
var partition = partitions[i];
%>
<tr<%= alt_rows(i)%>>
<td><%= fmt_node(partition.node) %></td>
<td>
<%
for (var j = 0; j < partition.others.length; j++) {
var other = partition.others[j];
%>
<%= other %><br/>
<% } %>
</td>
</tr>
<% } %>
</table>
<p>
While running in this partitioned state, changes (such as queue or
exchange declaration and binding) which take place in one partition
will not be visible to other partition(s). Other behaviour is not
guaranteed.
</p>
<p>
<a target="_blank"
href="https://www.rabbitmq.com/partitions.html">More information on
network partitions.</a>
</p>
<% } %>
<%
var ticktime = null;
var ticktimes_unequal = false;
for (var i = 0; i < nodes.length; i++) {
var node_ticktime = nodes[i].net_ticktime;
if (node_ticktime != undefined) {
if (ticktime != null && node_ticktime != ticktime) {
ticktimes_unequal = true;
}
ticktime = nodes[i].net_ticktime;
}
}
if (ticktimes_unequal) {
%>
<p class="status-error">
The <code>kernel</code> <code>net_ticktime</code> values are set
differently for different nodes in this cluster.
</p>
<p>
The values are:
</p>
<table class="list">
<tr><th>Node</th><th>net_ticktime</th></tr>
<%
for (var i = 0; i < nodes.length; i++) {
%>
<tr<%= alt_rows(i)%>>
<td><%= nodes[i].name %></td>
<td><%= nodes[i].net_ticktime %></td>
</tr>
<%
}
%>
</table>
<p>
This is a dangerous configuration; use of substantially
unequal <code>net_timetime</code> values can lead to partitions
being falsely detected.
</p>
<p>
<a target="_blank"
href="https://www.rabbitmq.com/nettick.html">More information on
<code>net_ticktime</code>.</a>
</p>
<%
}
%>
</div>
<div class="section">
<h2>Permissions</h2>
<div class="hider">
<h3>Current permissions</h3>
<% if (permissions.length > 0) { %>
<table class="list">
<thead>
<tr>
<% if (mode == 'vhost') { %>
<th>User</th>
<% } else { %>
<th>Virtual host</th>
<% } %>
<th>Configure regexp</th>
<th>Write regexp</th>
<th>Read regexp</th>
<th></th>
</tr>
</thead>
<tbody>
<%
for (var i = 0; i < permissions.length; i++) {
var permission = permissions[i];
%>
<tr<%= alt_rows(i)%>>
<% if (mode == 'vhost') { %>
<td><%= link_user(permission.user) %></td>
<% } else { %>
<td><%= link_vhost(permission.vhost) %></td>
<% } %>
<td><%= fmt_string(permission.configure) %></td>
<td><%= fmt_string(permission.write) %></td>
<td><%= fmt_string(permission.read) %></td>
<td class="c">
<form action="#/permissions" method="delete" class="confirm">
<input type="hidden" name="username" value="<%= fmt_string(permission.user) %>"/>
<input type="hidden" name="vhost" value="<%= fmt_string(permission.vhost) %>"/>
<input type="submit" value="Clear"/>
</form>
</td>
</tr>
<% } %>
</tbody>
</table>
<% } else { %>
<p>... no permissions ...</p>
<% } %>
<h3>Set permission</h3>
<form action="#/permissions" method="put">
<table class="form">
<tr>
<% if (mode == 'vhost') { %>
<th>User</th>
<td>
<input type="hidden" name="vhost" value="<%= fmt_string(parent.name) %>"/>
<select name="username">
<% for (var i = 0; i < users.length; i++) { %>
<option value="<%= fmt_string(users[i].name) %>"><%= fmt_string(users[i].name) %></option>
<% } %>
</select>
</td>
<% } else { %>
<th><label>Virtual Host:</label></th>
<td>
<input type="hidden" name="username" value="<%= fmt_string(parent.name) %>"/>
<select name="vhost">
<% for (var i = 0; i < vhosts.length; i++) { %>
<option value="<%= fmt_string(vhosts[i].name) %>"><%= fmt_string(vhosts[i].name) %></option>
<% } %>
</select>
</td>
<% } %>
</tr>
<tr>
<th><label>Configure regexp:</label></th>
<td><input type="text" name="configure" value=".*"/></td>
</tr>
<tr>
<th><label>Write regexp:</label></th>
<td><input type="text" name="write" value=".*"/></td>
</tr>
<tr>
<th><label>Read regexp:</label></th>
<td><input type="text" name="read" value=".*"/></td>
</tr>
</table>
<input type="submit" value="Set permission"/>
</form>
</div>
</div>
<h1>Policies</h1>
<div class="section">
<h2>User policies</h2>
<div class="hider">
<%= filter_ui(policies) %>
<div class="updatable">
<% if (policies.length > 0) { %>
<table class="list">
<thead>
<tr>
<% if (vhosts_interesting) { %>
<th>Virtual Host</th>
<% } %>
<th>Name</th>
<th>Pattern</th>
<th>Apply to</th>
<th>Definition</th>
<th>Priority</th>
</tr>
</thead>
<tbody>
<%
for (var i = 0; i < policies.length; i++) {
var policy = policies[i];
%>
<tr<%= alt_rows(i)%>>
<% if (vhosts_interesting) { %>
<td><%= fmt_string(policy.vhost) %></td>
<% } %>
<% if (is_user_policymaker) { %>
<td><%= link_policy(policy.vhost, policy.name) %></td>
<% } else { %>
<td><%= fmt_string(policy.name) %></td>
<% } %>
<td><%= fmt_string(policy.pattern) %></td>
<td><%= fmt_string(policy['apply-to']) %></td>
<td><%= fmt_table_short(policy.definition) %></td>
<td><%= fmt_string(policy.priority) %></td>
</tr>
<% } %>
</tbody>
</table>
<% } else { %>
<p>... no policies ...</p>
<% } %>
</div>
</div>
</div>
<% if (is_user_policymaker && vhosts.length > 0) { %>
<div class="section-hidden">
<h2>Add / update a policy</h2>
<div class="hider">
<form action="#/policies" method="put">
<table class="form">
<% if (vhosts_interesting) { %>
<tr>
<th><label>Virtual host:</label></th>
<td>
<select name="vhost">
<% for (var i = 0; i < vhosts.length; i++) { %>
<option value="<%= fmt_string(vhosts[i].name) %>" <%= (vhosts[i].name === current_vhost) ? 'selected="selected"' : '' %>><%= fmt_string(vhosts[i].name) %></option>
<% } %>
</select>
</td>
</tr>
<% } else { %>
<tr><td><input type="hidden" name="vhost" value="<%= fmt_string(vhosts[0].name) %>"/></td></tr>
<% } %>
<tr>
<th><label>Name:</label></th>
<td><input type="text" name="name"/><span class="mand">*</span></td>
</tr>
<tr>
<th><label>Pattern:</label></th>
<td><input type="text" name="pattern"/><span class="mand">*</span></td>
</tr>
<tr>
<th><label>Apply to:</label></th>
<td>
<select name="apply-to">
<option value="all">Exchanges and queues</option>
<option value="exchanges">Exchanges</option>
<option value="queues">Queues</option>
</select>
</td>
</tr>
<tr>
<th><label>Priority:</label></th>
<td><input type="text" name="priority"/></td>
</tr>
<tr>
<th><label>Definition:</label></th>
<td>
<div class="multifield" id="definition"></div>
<table class="argument-links">
<tr>
<td>HA</td>
<td>
<span class="argument-link" field="definition" key="ha-mode" type="string">HA mode</span> <span class="help" id="policy-ha-mode"></span> |
<span class="argument-link" field="definition" key="ha-params" type="number">HA params</span> <span class="help" id="policy-ha-params"></span> |
<span class="argument-link" field="definition" key="ha-sync-mode" type="string">HA sync mode</span> <span class="help" id="policy-ha-sync-mode"></span> |
<span class="argument-link" field="definition" key="ha-promote-on-shutdown" type="string" value="">HA mirror promotion on shutdown</span> <span class="help" id="policy-ha-promote-on-shutdown"></span>
<span class="argument-link" field="definition" key="ha-promote-on-failure" type="string" value="">HA mirror promotion on failure</span> <span class="help" id="policy-ha-promote-on-failure"></span>
</td>
</tr>
<tr>
<td>Federation</td>
<td>
<span class="argument-link" field="definition" key="federation-upstream-set" type="string">Federation upstream set</span> <span class="help" id="policy-federation-upstream-set"></span> |
<span class="argument-link" field="definition" key="federation-upstream" type="string">Federation upstream</span> <span class="help" id="policy-federation-upstream"></span>
</td>
</tr>
<tr>
<td>Queues</td>
<td>
<span class="argument-link" field="definition" key="message-ttl" type="number">Message TTL</span> |
<span class="argument-link" field="definition" key="expires" type="number">Auto expire</span> |
<span class="argument-link" field="definition" key="max-length" type="number">Max length</span> |
<span class="argument-link" field="definition" key="max-length-bytes" type="number">Max length bytes</span> |
<span class="argument-link" field="definition" key="overflow" type="string">Overflow behaviour</span><br/>
<span class="argument-link" field="definition" key="dead-letter-exchange" type="string">Dead letter exchange</span> |
<span class="argument-link" field="definition" key="dead-letter-routing-key" type="string">Dead letter routing key</span><br />
<span class="argument-link" field="definition" key="queue-mode" type="string" value="lazy">Lazy mode</span> |
<span class="argument-link" field="definition" key="queue-master-locator" type="string">Master Locator</span>
</td>
</tr>
<tr>
<td>Exchanges</td>
<td>
<span class="argument-link" field="definition" key="alternate-exchange" type="string">Alternate exchange</span>
<span class="help" id="exchange-alternate"></span>
</td>
</tr>
</table>
</td>
<td class="t"><span class="mand">*</span></td>
</tr>
</table>
<input type="submit" value="Add / update policy"/>
</form>
</div>
</div>
<% } %>
<div class="section">
<h2>Operator policies</h2>
<div class="hider">
<%= filter_ui(operator_policies) %>
<div class="updatable">
<% if (operator_policies.length > 0) { %>
<table class="list">
<thead>
<tr>
<% if (vhosts_interesting) { %>
<th>Virtual Host</th>
<% } %>
<th>Name</th>
<th>Pattern</th>
<th>Apply to</th>
<th>Definition</th>
<th>Priority</th>
<th class="administrator-only">Clear</th>
</tr>
</thead>
<tbody>
<%
for (var i = 0; i < operator_policies.length; i++) {
var policy = operator_policies[i];
%>
<tr<%= alt_rows(i)%>>
<% if (vhosts_interesting) { %>
<td><%= fmt_string(policy.vhost) %></td>
<% } %>
<td><%= fmt_string(policy.name) %></td>
<td><%= fmt_string(policy.pattern) %></td>
<td><%= fmt_string(policy['apply-to']) %></td>
<td><%= fmt_table_short(policy.definition) %></td>
<td><%= fmt_string(policy.priority) %></td>
<td class="administrator-only">
<form action="#/operator_policies" method="delete" class="confirm">
<input type="hidden" name="name" value="<%= fmt_string(policy.name) %>"/>
<input type="hidden" name="vhost" value="<%= fmt_string(policy.vhost) %>"/>
<input type="submit" value="Clear"/>
</form>
</td>
</tr>
<% } %>
</tbody>
</table>
<% } else { %>
<p>... no policies ...</p>
<% } %>
</div>
</div>
</div>
<% if (user_administrator && vhosts.length > 0) { %>
<div class="section-hidden">
<h2>Add / update an operator policy</h2>
<div class="hider">
<form action="#/operator_policies" method="put">
<table class="form">
<% if (vhosts_interesting) { %>
<tr>
<th><label>Virtual host:</label></th>
<td>
<select name="vhost">
<% for (var i = 0; i < vhosts.length; i++) { %>
<option value="<%= fmt_string(vhosts[i].name) %>"><%= fmt_string(vhosts[i].name) %></option>
<% } %>
</select>
</td>
</tr>
<% } else { %>
<tr><td><input type="hidden" name="vhost" value="<%= fmt_string(vhosts[0].name) %>"/></td></tr>
<% } %>
<tr>
<th><label>Name:</label></th>
<td><input type="text" name="name"/><span class="mand">*</span></td>
</tr>
<tr>
<th><label>Pattern:</label></th>
<td><input type="text" name="pattern"/><span class="mand">*</span></td>
</tr>
<tr>
<th><label>Apply to:</label></th>
<td>
<select name="apply-to">
<option value="queues">Queues</option>
</select>
</td>
</tr>
<tr>
<th><label>Priority:</label></th>
<td><input type="text" name="priority"/></td>
</tr>
<tr>
<th><label>Definition:</label></th>
<td>
<div class="multifield" id="definitionop"></div>
<table class="argument-links">
<tr>
<td>Queues</td>
<td>
<span class="argument-link" field="definitionop" key="message-ttl" type="number">Message TTL</span> |
<span class="argument-link" field="definitionop" key="expires" type="number">Auto expire</span> |
<span class="argument-link" field="definitionop" key="max-length" type="number">Max length</span> |
<span class="argument-link" field="definitionop" key="max-length-bytes" type="number">Max length bytes</span> |
<span class="argument-link" field="definitionop" key="overflow" type="string">Overflow behaviour</span><br/>
</td>
</tr>
</table>
</td>
<td class="t"><span class="mand">*</span></td>
</tr>
</table>
<input type="submit" value="Add / update operator policy"/>
</form>
</div>
</div>
<% } %>
<h1>Policy: <b><%= fmt_string(policy.name) %></b><%= fmt_maybe_vhost(policy.vhost) %></h1>
<div class="section">
<h2>Overview</h2>
<div class="hider">
<table class="facts">
<tr>
<th>Pattern</th>
<td><%= fmt_string(policy.pattern) %></td>
</tr>
<tr>
<th>Apply to</th>
<td><%= fmt_string(policy['apply-to']) %></td>
</tr>
<tr>
<th>Definition</th>
<td><%= fmt_table_short(policy.definition) %></td>
</tr>
<tr>
<th>Priority</th>
<td><%= fmt_string(policy.priority) %></td>
</tr>
</table>
</div>
</div>
<div class="section-hidden">
<h2>Delete this policy</h2>
<div class="hider">
<form action="#/policies" method="delete" class="confirm">
<input type="hidden" name="component" value="policy"/>
<input type="hidden" name="vhost" value="<%= fmt_string(policy.vhost) %>"/>
<input type="hidden" name="name" value="<%= fmt_string(policy.name) %>"/>
<input type="submit" value="Delete this policy"/>
</form>
</div>
</div>
<div class="section-hidden">
<h2>Publish message</h2>
<div class="hider">
<form action="#/exchanges/publish" method="post">
<% if (mode == 'queue') { %>
<input type="hidden" name="vhost" value="<%= fmt_string(queue.vhost) %>"/>
<input type="hidden" name="name" value="amq.default"/>
<% } else { %>
<input type="hidden" name="vhost" value="<%= fmt_string(exchange.vhost) %>"/>
<input type="hidden" name="name" value="<%= fmt_exchange_url(exchange.name) %>"/>
<% } %>
<input type="hidden" name="properties" value=""/>
<table class="form">
<% if (mode == 'queue') { %>
<tr>
<td colspan="2"><input type="hidden" name="routing_key" value="<%= fmt_string(queue.name) %>"/> Message will be published to the default exchange with routing key <strong><%= fmt_string(queue.name) %></strong>, routing it to this queue.</td>
</tr>
<% } else { %>
<tr>
<th><label>Routing key:</label></th>
<td><input type="text" name="routing_key" value=""/></td>
</tr>
<% } %>
<tr>
<th><label>Delivery mode:</label></th>
<td>
<select name="delivery_mode">
<option value="1">1 - Non-persistent</option>
<option value="2">2 - Persistent</option>
</select>
</td>
</tr>
<tr>
<th>
<label>
Headers:
<span class="help" id="message-publish-headers"></span>
</label>
</th>
<td>
<div class="multifield" id="headers"></div>
</td>
</tr>
<tr>
<th>
<label>
Properties:
<span class="help" id="message-publish-properties"></span>
</label>
</th>
<td>
<div class="multifield string-only" id="props"></div>
</td>
</tr>
<tr>
<th><label>Payload:</label></th>
<td><textarea name="payload"></textarea></td>
</tr>
</table>
<input type="submit" value="Publish message" />
</form>
</div>
</div>
<h1>Queue <b><%= fmt_string(highlight_extra_whitespace(queue.name)) %></b><%= fmt_maybe_vhost(queue.vhost) %></h1>
<div class="section">
<h2>Overview</h2>
<div class="hider updatable">
<%= queue_lengths('lengths-q', queue) %>
<% if (rates_mode != 'none') { %>
<%= message_rates('msg-rates-q', queue.message_stats) %>
<% } %>
<h3>Details</h3>
<table class="facts facts-l">
<tr>
<th>Features</th>
<td><%= fmt_features(queue) %></td>
</tr>
<tr>
<th>Policy</th>
<td><%= link_policy(queue.vhost, queue.policy) %></td>
</tr>
<tr>
<th>Operator policy</th>
<td><%= fmt_string(queue.operator_policy, '') %></td>
</tr>
<% if (queue.owner_pid_details != undefined) { %>
<tr>
<th>Exclusive owner</th>
<td><%= link_conn(queue.owner_pid_details.name) %></td>
</tr>
<% } %>
<tr>
<th>Effective policy definition</th>
<td><%= fmt_table_short(queue.effective_policy_definition) %></td>
</tr>
<% if (nodes_interesting) { %>
<tr>
<th>Node</th>
<td><%= fmt_node(queue.node) %></td>
</tr>
<% if (!queue.exclusive) { %>
<tr>
<th>Mirrors</th>
<td>
<%
var has_unsynced_node = false;
for (var i in queue.slave_nodes) {
var node = queue.slave_nodes[i];
%>
<%
if (jQuery.inArray(node, queue.synchronised_slave_nodes) == -1) {
has_unsynced_node = true;
%>
<%= fmt_node(node) %> <b>(unsynchronised)</b>
<% } else { %>
<%= fmt_node(node) %>
<% } %>
<br/>
<% } %>
<% if (queue.state == 'syncing') { %>
<table>
<tr>
<td>
<%= fmt_sync_state(queue) %>
</td>
<td>
<form action="#/queues/actions" method="post">
<input type="hidden" name="vhost" value="<%= fmt_string(queue.vhost) %>"/>
<input type="hidden" name="name" value="<%= fmt_string(queue.name) %>"/>
<input type="hidden" name="action" value="cancel_sync"/>
<input type="submit" value="Cancel" id="action-button" />
</form>
</td>
</tr>
</table>
<% } else if (has_unsynced_node) { %>
<form action="#/queues/actions" method="post">
<input type="hidden" name="vhost" value="<%= fmt_string(queue.vhost) %>"/>
<input type="hidden" name="name" value="<%= fmt_string(queue.name) %>"/>
<input type="hidden" name="action" value="sync"/>
<input type="submit" value="Synchronise" id="action-button" />
</form>
<% } %>
</td>
</tr>
<% } %>
<% } %>
</table>
<table class="facts facts-l">
<tr>
<th>State</th>
<td><%= fmt_object_state(queue) %></td>
</tr>
<tr>
<th>Consumers</th>
<td><%= fmt_string(queue.consumers) %></td>
</tr>
<tr>
<th>Consumer utilisation <span class="help" id="queue-consumer-utilisation"></th>
<td><%= fmt_percent(queue.consumer_utilisation) %></td>
</tr>
</table>
<table class="facts">
<tr>
<td></td>
<th class="horizontal">Total</th>
<th class="horizontal">Ready</th>
<th class="horizontal">Unacked</th>
<th class="horizontal">In memory</th>
<th class="horizontal">Persistent</th>
<th class="horizontal">Transient, Paged Out</th>
</tr>
<tr>
<th>
Messages
<span class="help" id="queue-messages"></span>
</th>
<td class="r">
<%= fmt_num_thousands(queue.messages) %>
</td>
<td class="r">
<%= fmt_num_thousands(queue.messages_ready) %>
</td>
<td class="r">
<%= fmt_num_thousands(queue.messages_unacknowledged) %>
</td>
<td class="r">
<%= fmt_num_thousands(queue.messages_ram) %>
</td>
<td class="r">
<%= fmt_num_thousands(queue.messages_persistent) %>
</td>
<td class="r">
<%= fmt_num_thousands(queue.messages_paged_out) %>
</td>
</tr>
<tr>
<th>
Message body bytes
<span class="help" id="queue-message-body-bytes"></span>
</th>
<td class="r">
<%= fmt_bytes(queue.message_bytes) %>
</td>
<td class="r">
<%= fmt_bytes(queue.message_bytes_ready) %>
</td>
<td class="r">
<%= fmt_bytes(queue.message_bytes_unacknowledged) %>
</td>
<td class="r">
<%= fmt_bytes(queue.message_bytes_ram) %>
</td>
<td class="r">
<%= fmt_bytes(queue.message_bytes_persistent) %>
</td>
<td class="r">
<%= fmt_bytes(queue.message_bytes_paged_out) %>
</td>
</tr>
<tr>
<th>
Process memory
<span class="help" id="queue-process-memory"></span>
</th>
<td class="r"><%= fmt_bytes(queue.memory) %></td>
</tr>
</table>
</div>
</div>
<% if (rates_mode == 'detailed') { %>
<div class="section-hidden">
<h2>Message rates breakdown</h2>
<div class="hider updatable">
<table class="two-col-layout">
<tr>
<td>
<%= format('msg-detail-publishes',
{'mode': 'queue',
'object': queue.incoming,
'label': 'Incoming'}) %>
</td>
<td>
<%= format('msg-detail-deliveries',
{'mode': 'queue',
'object': queue.deliveries}) %>
</td>
</tr>
</table>
</div>
</div>
<% } %>
<div class="section-hidden">
<h2>Consumers</h2>
<div class="hider updatable">
<%= format('consumers', {'mode': 'queue', 'consumers': queue.consumer_details}) %>
</div>
</div>
<div class="section-hidden">
<h2>Bindings</h2>
<div class="hider">
<div class="bindings-wrapper">
<%= format('bindings', {'mode': 'queue', 'bindings': bindings}) %>
<p class="arrow">&dArr;</p>
<p><span class="queue">This queue</span></p>
<%= format('add-binding', {'mode': 'queue', 'parent': queue}) %>
</div>
</div>
</div>
<%= format('publish', {'mode': 'queue', 'queue': queue}) %>
<div class="section-hidden">
<h2>Get messages</h2>
<div class="hider">
<p>
Warning: getting messages from a queue is a destructive action.
<span class="help" id="message-get-requeue"></span>
</p>
<form action="#/queues/get" method="post">
<input type="hidden" name="vhost" value="<%= fmt_string(queue.vhost) %>"/>
<input type="hidden" name="name" value="<%= fmt_string(queue.name) %>"/>
<input type="hidden" name="truncate" value="50000"/>
<table class="form">
<tr>
<th><label>Ack Mode:</label></th>
<td>
<select name="ackmode">
<option value="ack_requeue_true" selected>Nack message requeue true</option>
<option value="ack_requeue_false">Ack message requeue false</option>
<option value="reject_requeue_true">Reject requeue true</option>
<option value="reject_requeue_false">Reject requeue false</option>
</select>
</td>
</tr>
<tr>
<th><label>Encoding:</label></th>
<td>
<select name="encoding">
<option value="auto">Auto string / base64</option>
<option value="base64">base64</option>
</select>
<span class="help" id="string-base64"></span>
</td>
</tr>
<tr>
<th><label>Messages:</label></th>
<td><input type="text" name="count" value="1"/></td>
</tr>
</table>
<input type="submit" value="Get Message(s)" />
</form>
<div id="msg-wrapper"></div>
</div>
</div>
<% if (is_user_policymaker) { %>
<div class="section-hidden">
<h2>Move messages</h2>
<div class="hider">
<% if (NAVIGATION['Admin'][0]['Shovel Management'] == undefined) { %>
<p>To move messages, the shovel plugin must be enabled, try:</p>
<pre>$ rabbitmq-plugins enable rabbitmq_shovel rabbitmq_shovel_management</pre>
<% } else { %>
<p>
The shovel plugin can be used to move messages from this queue
to another one. The form below will create a temporary shovel to
move messages to another queue on the same virtual host, with
default settings.
</p>
<p>
For more options <a href="#/dynamic-shovels">see the shovel
interface</a>.
</p>
<form action="#/shovel-parameters-move-messages" method="put">
<input type="hidden" name="component" value="shovel"/>
<input type="hidden" name="vhost" value="<%= fmt_string(queue.vhost) %>"/>
<input type="hidden" name="name" value="Move from <%= fmt_string(queue.name) %>"/>
<input type="hidden" name="src-uri" value="amqp:///<%= esc(queue.vhost) %>"/>
<input type="hidden" name="src-queue" value="<%= fmt_string(queue.name) %>"/>
<input type="hidden" name="src-protocol" value="amqp091"/>
<input type="hidden" name="src-prefetch-count" value="1000"/>
<input type="hidden" name="src-delete-after" value="queue-length"/>
<input type="hidden" name="dest-protocol" value="amqp091"/>
<input type="hidden" name="dest-uri" value="amqp:///<%= esc(queue.vhost) %>"/>
<input type="hidden" name="dest-add-forward-headers" value="false"/>
<input type="hidden" name="ack-mode" value="on-confirm"/>
<input type="hidden" name="redirect" value="#/queues"/>
<table class="form">
<tr>
<th>Destination queue:</th>
<td><input type="text" name="dest-queue"/></td>
</tr>
</table>
<input type="submit" value="Move messages"/>
</form>
<% } %>
</div>
</div>
<% } %>
<div class="section-hidden">
<h2>Delete</h2>
<div class="hider">
<form action="#/queues" method="delete" class="confirm-queue inline-form">
<input type="hidden" name="vhost" value="<%= fmt_string(queue.vhost) %>"/>
<input type="hidden" name="name" value="<%= fmt_string(queue.name) %>"/>
<input type="hidden" name="mode" value="delete"/>
<input type="submit" value="Delete Queue" />
</form>
</div>
</div>
<div class="section-hidden">
<h2>Purge</h2>
<div class="hider">
<form action="#/queues" method="delete" class="confirm-purge-queue inline-form">
<input type="hidden" name="vhost" value="<%= fmt_string(queue.vhost) %>"/>
<input type="hidden" name="name" value="<%= fmt_string(queue.name) %>"/>
<input type="hidden" name="mode" value="purge"/>
<input type="submit" value="Purge Messages" />
</form>
</div>
</div>
<% if(queue.reductions || queue.garbage_collection) { %>
<div class="section-hidden">
<h2>Runtime Metrics (Advanced)</h2>
<div class="hider updatable">
<%= data_reductions('reductions-rates-queue', queue) %>
<table class="facts">
<% if (queue.garbage_collection.min_bin_vheap_size) { %>
<tr>
<th>Minimum binary virtual heap size in words (min_bin_vheap_size)</th>
<td><%= queue.garbage_collection.min_bin_vheap_size %></td>
</tr>
<% } %>
<% if (queue.garbage_collection.min_heap_size) { %>
<tr>
<th>Minimum heap size in words (min_heap_size)</th>
<td><%= queue.garbage_collection.min_heap_size %></td>
</tr>
<% } %>
<% if (queue.garbage_collection.fullsweep_after) { %>
<tr>
<th>Maximum generational collections before fullsweep (fullsweep_after)</th>
<td><%= queue.garbage_collection.fullsweep_after %></td>
</tr>
<% } %>
<% if (queue.garbage_collection.minor_gcs) { %>
<tr>
<th>Number of minor GCs (minor_gcs)</th>
<td><%= queue.garbage_collection.minor_gcs %></td>
</tr>
<% } %>
</table>
</div>
</div>
<% } %>
<h1>Queues</h1>
<div class="section">
<%= paginate_ui(queues, 'queues') %>
</div>
<div class="updatable">
<% if (queues.items.length > 0) { %>
<table class="list">
<thead>
<tr>
<%= group_heading('queues', 'Overview', [vhosts_interesting, nodes_interesting, true]) %>
<%= group_heading('queues', 'Messages', []) %>
<%= group_heading('queues', 'Message bytes', []) %>
<% if (rates_mode != 'none') { %>
<%= group_heading('queues', 'Message rates', []) %>
<% } %>
<th class="plus-minus"><span class="popup-options-link" title="Click to change columns" type="columns" for="queues">+/-</span></th>
</tr>
<tr>
<% if (vhosts_interesting) { %>
<th><%= fmt_sort('Virtual host', 'vhost') %></th>
<% } %>
<th><%= fmt_sort('Name', 'name') %></th>
<% if (nodes_interesting) { %>
<th><%= fmt_sort('Node', 'node') %></th>
<% } %>
<% if (show_column('queues', 'features')) { %>
<th>Features</th>
<% } %>
<% if (show_column('queues', 'features_no_policy')) { %>
<th>Features</th>
<% } %>
<% if (show_column('queues', 'policy')) { %>
<th><%= fmt_sort('Policy','policy') %></th>
<% } %>
<% if (show_column('queues', 'consumers')) { %>
<th><%= fmt_sort('Consumers', 'consumers') %></th>
<% } %>
<% if (show_column('queues', 'consumer_utilisation')) { %>
<th><%= fmt_sort('Consumer utilisation', 'consumer_utilisation') %></th>
<% } %>
<% if (show_column('queues', 'state')) { %>
<th><%= fmt_sort('State', 'state') %></th>
<% } %>
<% if (show_column('queues', 'msgs-ready')) { %>
<th><%= fmt_sort('Ready', 'messages_ready') %></th>
<% } %>
<% if (show_column('queues', 'msgs-unacked')) { %>
<th><%= fmt_sort('Unacked', 'messages_unacknowledged') %></th>
<% } %>
<% if (show_column('queues', 'msgs-ram')) { %>
<th><%= fmt_sort('In Memory', 'messages_ram') %></th>
<% } %>
<% if (show_column('queues', 'msgs-persistent')) { %>
<th><%= fmt_sort('Persistent', 'messages_persistent') %></th>
<% } %>
<% if (show_column('queues', 'msgs-total')) { %>
<th><%= fmt_sort('Total', 'messages') %></th>
<% } %>
<% if (show_column('queues', 'msg-bytes-ready')) { %>
<th><%= fmt_sort('Ready', 'message_bytes_ready') %></th>
<% } %>
<% if (show_column('queues', 'msg-bytes-unacked')) { %>
<th><%= fmt_sort('Unacked', 'message_bytes_unacknowledged') %></th>
<% } %>
<% if (show_column('queues', 'msg-bytes-ram')) { %>
<th><%= fmt_sort('In Memory', 'message_bytes_ram') %></th>
<% } %>
<% if (show_column('queues', 'msg-bytes-persistent')) { %>
<th><%= fmt_sort('Persistent', 'message_bytes_persistent') %></th>
<% } %>
<% if (show_column('queues', 'msg-bytes-total')) { %>
<th><%= fmt_sort('Total', 'message_bytes') %></th>
<% } %>
<% if (rates_mode != 'none') { %>
<% if (show_column('queues', 'rate-incoming')) { %>
<th><%= fmt_sort('incoming', 'message_stats.publish_details.rate') %></th>
<% } %>
<% if (show_column('queues', 'rate-deliver')) { %>
<th><%= fmt_sort('deliver / get', 'message_stats.deliver_get_details.rate') %></th>
<% } %>
<% if (show_column('queues', 'rate-redeliver')) { %>
<th><%= fmt_sort('redelivered', 'message_stats.redeliver_details.rate') %></th>
<% } %>
<% if (show_column('queues', 'rate-ack')) { %>
<th><%= fmt_sort('ack', 'message_stats.ack_details.rate') %></th>
<% } %>
<% } %>
</tr>
</thead>
<tbody>
<%
for (var i = 0; i < queues.items.length; i++) {
var queue = queues.items[i];
%>
<tr<%= alt_rows(i, queue.arguments) %>>
<% if (vhosts_interesting) { %>
<td><%= fmt_string(queue.vhost) %></td>
<% } %>
<td><%= link_queue(queue.vhost, queue.name, queue.arguments) %></td>
<% if (nodes_interesting) { %>
<td>
<%= fmt_node(queue.node) %>
<%= fmt_mirrors(queue) %>
<% if (queue.state == 'syncing') { %>
<%= fmt_sync_state(queue) %>
<% } %>
</td>
<% } %>
<% if (show_column('queues', 'features')) { %>
<td class="c">
<%= fmt_features_short(queue) %>
<%= fmt_policy_short(queue) %>
<%= fmt_op_policy_short(queue) %>
</td>
<% } %>
<% if (show_column('queues', 'features_no_policy')) { %>
<td class="c"><%= fmt_features_short(queue) %></td>
<% } %>
<% if (show_column('queues', 'policy')) { %>
<td class="c"><%= link_policy(queue.vhost, queue.policy) %>
<%= fmt_string(queue.operator_policy) %></td>
<% } %>
<% if (show_column('queues', 'consumers')) { %>
<td class="c"><%= fmt_string(queue.consumers) %></td>
<% } %>
<% if (show_column('queues', 'consumer_utilisation')) { %>
<td class="c"><%= fmt_percent(queue.consumer_utilisation) %></td>
<% } %>
<% if (show_column('queues', 'state')) { %>
<td class="c"><%= fmt_object_state(queue) %></td>
<% } %>
<% if (show_column('queues', 'msgs-ready')) { %>
<td class="r"><%= fmt_num_thousands(queue.messages_ready) %></td>
<% } %>
<% if (show_column('queues', 'msgs-unacked')) { %>
<td class="r"><%= fmt_num_thousands(queue.messages_unacknowledged) %></td>
<% } %>
<% if (show_column('queues', 'msgs-ram')) { %>
<td class="r"><%= fmt_num_thousands(queue.messages_ram) %></td>
<% } %>
<% if (show_column('queues', 'msgs-persistent')) { %>
<td class="r"><%= fmt_num_thousands(queue.messages_persistent) %></td>
<% } %>
<% if (show_column('queues', 'msgs-total')) { %>
<td class="r"><%= fmt_num_thousands(queue.messages) %></td>
<% } %>
<% if (show_column('queues', 'msg-bytes-ready')) { %>
<td class="r"><%= fmt_bytes(queue.message_bytes_ready) %></td>
<% } %>
<% if (show_column('queues', 'msg-bytes-unacked')) { %>
<td class="r"><%= fmt_bytes(queue.message_bytes_unacknowledged) %></td>
<% } %>
<% if (show_column('queues', 'msg-bytes-ram')) { %>
<td class="r"><%= fmt_bytes(queue.message_bytes_ram) %></td>
<% } %>
<% if (show_column('queues', 'msg-bytes-persistent')) { %>
<td class="r"><%= fmt_bytes(queue.message_bytes_persistent) %></td>
<% } %>
<% if (show_column('queues', 'msg-bytes-total')) { %>
<td class="r"><%= fmt_bytes(queue.message_bytes) %></td>
<% } %>
<% if (rates_mode != 'none') { %>
<% if (show_column('queues', 'rate-incoming')) { %>
<td class="r"><%= fmt_detail_rate(queue.message_stats, 'publish') %></td>
<% } %>
<% if (show_column('queues', 'rate-deliver')) { %>
<td class="r"><%= fmt_detail_rate(queue.message_stats, 'deliver_get') %></td>
<% } %>
<% if (show_column('queues', 'rate-redeliver')) { %>
<td class="r"><%= fmt_detail_rate(queue.message_stats, 'redeliver') %></td>
<% } %>
<% if (show_column('queues', 'rate-ack')) { %>
<td class="r"><%= fmt_detail_rate(queue.message_stats, 'ack') %></td>
<% } %>
<% } %>
</tr>
<% } %>
</tbody>
</table>
<% } else { %>
<p>... no queues ...</p>
<% } %>
</div>
</div>
</div>
<div class="section-hidden">
<h2>Add a new queue</h2>
<div class="hider">
<form action="#/queues" method="put">
<table class="form">
<% if (vhosts_interesting) { %>
<tr>
<th><label>Virtual host:</label></th>
<td>
<select name="vhost">
<% for (var i = 0; i < vhosts.length; i++) { %>
<option value="<%= fmt_string(vhosts[i].name) %>" <%= (vhosts[i].name === current_vhost) ? 'selected="selected"' : '' %>><%= fmt_string(vhosts[i].name) %></option>
<% } %>
</select>
</td>
</tr>
<% } else { %>
<tr><td><input type="hidden" name="vhost" value="<%= fmt_string(vhosts[0].name) %>"/></td></tr>
<% } %>
<tr>
<th><label>Name:</label></th>
<td><input type="text" name="name"/><span class="mand">*</span></td>
</tr>
<tr>
<th><label>Durability:</label></th>
<td>
<select name="durable">
<option value="true">Durable</option>
<option value="false">Transient</option>
</select>
</td>
</tr>
<%
if (nodes_interesting) {
var nodes = JSON.parse(sync_get('/nodes'));
%>
<tr>
<th><label>Node:</label></th>
<td>
<select name="node">
<% for (var i = 0; i < nodes.length; i++) { %>
<option value="<%= fmt_string(nodes[i].name) %>"><%= fmt_node(nodes[i].name) %></option>
<% } %>
</select>
</td>
</tr>
<% } %>
<tr>
<th><label>Auto delete: <span class="help" id="queue-auto-delete"></span></label></th>
<td>
<select name="auto_delete">
<option value="false">No</option>
<option value="true">Yes</option>
</select>
</td>
</tr>
<tr>
<th><label>Arguments:</label></th>
<td>
<div class="multifield" id="arguments"></div>
<table class="argument-links">
<tr>
<td>Add</td>
<td>
<span class="argument-link" field="arguments" key="x-message-ttl" type="number">Message TTL</span> <span class="help" id="queue-message-ttl"></span> |
<span class="argument-link" field="arguments" key="x-expires" type="number">Auto expire</span> <span class="help" id="queue-expires"></span> |
<span class="argument-link" field="arguments" key="x-max-length" type="number">Max length</span> <span class="help" id="queue-max-length"></span> |
<span class="argument-link" field="arguments" key="x-max-length-bytes" type="number">Max length bytes</span> <span class="help" id="queue-max-length-bytes"></span> |
<span class="argument-link" field="arguments" key="x-overflow" type="string">Overflow behaviour</span> <span class="help" id="queue-overflow"></span><br/>
<span class="argument-link" field="arguments" key="x-dead-letter-exchange" type="string">Dead letter exchange</span> <span class="help" id="queue-dead-letter-exchange"></span> |
<span class="argument-link" field="arguments" key="x-dead-letter-routing-key" type="string">Dead letter routing key</span> <span class="help" id="queue-dead-letter-routing-key"></span> |
<span class="argument-link" field="arguments" key="x-max-priority" type="number">Maximum priority</span> <span class="help" id="queue-max-priority"></span><br/>
<span class="argument-link" field="arguments" key="x-queue-mode" type="string" value="lazy">Lazy mode</span> <span class="help" id="queue-lazy"></span>
<span class="argument-link" field="arguments" key="x-queue-master-locator" type="string" value="">Master locator</span> <span class="help" id="queue-master-locator"></span>
</td>
</tr>
</table>
</td>
</tr>
</table>
<input type="submit" value="Add queue"/>
</form>
</div>
</div>
<%
var id = span.attr('for');
var mode = get_pref('rate-mode-' + id);
var size = get_pref('chart-size-' + id);
var range_pref = get_pref('chart-range');
%>
<form action="#/rate-options" method="put" class="auto-submit">
<input type="hidden" name="id" value="<%= id %>"/>
<table class="form" width="100%">
<tr>
<td colspan="2">
<h3>This time series</h3>
</td>
</tr>
<tr>
<th><label>Display:</label></th>
<td>
<%= fmt_radio('mode', 'Chart', 'chart', mode) %>
<%= fmt_radio('mode', 'Current value', 'curr', mode) %>
<% if (id != 'node-stats') { %>
<%= fmt_radio('mode', 'Moving average', 'avg', mode) %>
<% } %>
</td>
</tr>
<tr>
<th><label>Chart size:</label></th>
<td>
<%= fmt_radio('size', 'Small', 'small', size) %>
<%= fmt_radio('size', 'Medium', 'medium', size) %>
<%= fmt_radio('size', 'Large', 'large', size) %>
</td>
</tr>
<tr>
<td colspan="2">
<h3>All time series</h3>
</td>
</tr>
<tr>
<th><label>Chart range:</label></th>
<td>
<%
var range_type = get_chart_range_type(id);
for (var i = 0; i < CHART_RANGES[range_type].length; ++i) {
var data = CHART_RANGES[range_type][i];
var range = data[0];
var desc = data[1];
%>
<%= fmt_radio('range', desc, range, range_pref) %>
<%
}
%>
</td>
</tr>
</table>
</form>
<% if (node.running) { %>
<table class="list">
<tr>
<th>Name</th>
<th>Description</th>
<% if (show_enabled) { %>
<th>Enabled</th>
<% } %>
</tr>
<%
for (var i = 0; i < list.length; i++) {
var item = list[i];
%>
<tr<%= alt_rows(i)%>>
<td><%= fmt_string(item.name) %></td>
<td><%= fmt_string(item.description) %></td>
<% if (show_enabled) { %>
<td class="c"><%= fmt_boolean(item.enabled) %></td>
<% } %>
</tr>
<% } %>
</table>
<% } else {%>
<p>...node not running...</p>
<% } %>
<div class="section">
<h2>Topic permissions</h2>
<div class="hider">
<h3>Current topic permissions</h3>
<% if (topic_permissions.length > 0) { %>
<table class="list">
<thead>
<tr>
<% if (mode == 'vhost') { %>
<th>User</th>
<% } else { %>
<th>Virtual host</th>
<% } %>
<th>Exchange</th>
<th>Write regexp</th>
<th>Read regexp</th>
<th></th>
</tr>
</thead>
<tbody>
<%
for (var i = 0; i < topic_permissions.length; i++) {
var permission = topic_permissions[i];
%>
<tr<%= alt_rows(i)%>>
<% if (mode == 'vhost') { %>
<td><%= link_user(permission.user) %></td>
<% } else { %>
<td><%= link_vhost(permission.vhost) %></td>
<% } %>
<td><%= fmt_exchange(permission.exchange) %></td>
<td><%= fmt_string(permission.write) %></td>
<td><%= fmt_string(permission.read) %></td>
<td class="c">
<form action="#/topic-permissions" method="delete" class="confirm">
<input type="hidden" name="username" value="<%= fmt_string(permission.user) %>"/>
<input type="hidden" name="vhost" value="<%= fmt_string(permission.vhost) %>"/>
<input type="hidden" name="exchange" value="<%= fmt_exchange_url(permission.exchange) %>"/>
<input type="submit" value="Clear"/>
</form>
</td>
</tr>
<% } %>
</tbody>
</table>
<% } else { %>
<p>... no topic permissions ...</p>
<% } %>
<h3>Set topic permission</h3>
<form action="#/topic-permissions" method="put">
<table class="form">
<tr>
<% if (mode == 'vhost') { %>
<th>User</th>
<td>
<input type="hidden" name="vhost" value="<%= fmt_string(parent.name) %>"/>
<select name="username">
<% for (var i = 0; i < users.length; i++) { %>
<option value="<%= fmt_string(users[i].name) %>"><%= fmt_string(users[i].name) %></option>
<% } %>
</select>
</td>
<% } else { %>
<th><label>Virtual Host:</label></th>
<td>
<input type="hidden" name="username" value="<%= fmt_string(parent.name) %>"/>
<select name="vhost" class="list-exchanges">
<% for (var i = 0; i < vhosts.length; i++) { %>
<option value="<%= fmt_string(vhosts[i].name) %>"><%= fmt_string(vhosts[i].name) %></option>
<% } %>
</select>
</td>
<% } %>
</tr>
<tr>
<th><label>Exchange:</label></th>
<td>
<div id='list-exchanges'>
<%= format('list-exchanges', {'exchanges': exchanges}) %>
</div>
</td>
</tr>
<tr>
<th><label>Write regexp:</label></th>
<td><input type="text" name="write" value=".*"/></td>
</tr>
<tr>
<th><label>Read regexp:</label></th>
<td><input type="text" name="read" value=".*"/></td>
</tr>
</table>
<input type="submit" value="Set topic permission"/>
</form>
</div>
</div>
<h1>User: <b><%= fmt_string(user.name) %></b></h1>
<% if (permissions.length == 0) { %>
<p class="warning">
This user does not have permission to access any virtual hosts.<br/>
Use "Set Permission" below to grant permission to access virtual hosts.
</p>
<% } %>
<div class="section">
<h2>Overview</h2>
<div class="hider">
<table class="facts">
<tr>
<th>Tags</th>
<td><%= fmt_string(user.tags) %></td>
</tr>
<tr>
<th>Can log in with password</th>
<td><%= fmt_boolean(user.password_hash.length > 0) %></td>
</tr>
</table>
</div>
</div>
<%= format('permissions', {'mode': 'user', 'permissions': permissions, 'vhosts': vhosts, 'parent': user}) %>
<%= format('topic-permissions', {'mode': 'user', 'topic_permissions': topic_permissions, 'vhosts': vhosts, 'parent': user, 'exchanges': exchanges}) %>
<div class="section-hidden">
<h2>Update this user</h2>
<div class="hider">
<form action="#/users-modify" method="put">
<input type="hidden" name="username" value="<%= fmt_string(user.name) %>"/>
<table class="form">
<tr>
<th>
<label>
<select name="has-password" class="narrow controls-appearance">
<% if (user.password_hash.length > 0) { %>
<option value="password" selected="selected">Password:</option>
<option value="no-password">No password</option>
<% } else { %>
<option value="password">Password:</option>
<option value="no-password" selected="selected">No password</option>
<% } %>
</select>
</label>
</th>
<td>
<% if (user.password_hash.length > 0) { %>
<div id="password-div">
<% } else { %>
<div id="password-div" style="display: none;">
<% } %>
<input type="password" name="password" />
<span class="mand">*</span><br/>
<input type="password" name="password_confirm" />
<span class="mand">*</span>
(confirm)
</div>
<% if (user.password_hash.length > 0) { %>
<div id="no-password-div" style="display: none;">
<% } else { %>
<div id="no-password-div">
<% } %>
User cannot log in using password.
</div>
</td>
</tr>
<tr>
<th><label>Tags:</label></th>
<td>
<input type="text" name="tags" id="tags" value="<%= fmt_string(user.tags) %>" />
<span class="help" id="user-tags"/>
<sub>
[<span class="tag-link" tag="administrator">Admin</span>]
[<span class="tag-link" tag="monitoring">Monitoring</span>]
[<span class="tag-link" tag="policymaker">Policymaker</span>]
[<span class="tag-link" tag="management">Management</span>]
[<span class="tag-link" tag="impersonator">Impersonator</span>]
[<span class="tag-link" tag="">None</span>]
</sub>
</td>
</tr>
</table>
<input type="submit" value="Update user"/>
</form>
</div>
</div>
<div class="section-hidden">
<h2>Delete this user</h2>
<div class="hider">
<form action="#/users" method="delete" class="confirm">
<input type="hidden" name="username" value="<%= fmt_string(user.name) %>"/>
<input type="submit" value="Delete"/>
</form>
</div>
</div>
<h1>Users</h1>
<div class="section">
<h2>All users</h2>
<div class="hider">
<%= filter_ui(users) %>
<div class="updatable">
<% if (users.length > 0) { %>
<table class="list">
<thead>
<tr>
<th><%= fmt_sort('Name', 'name') %></th>
<th><%= fmt_sort('Tags', 'tags') %></th>
<th>Can access virtual hosts</th>
<th>Has password</th>
</tr>
</thead>
<tbody>
<%
for (var i = 0; i < users.length; i++) {
var user = users[i];
%>
<tr<%= alt_rows(i)%>>
<td><%= link_user(user.name) %></td>
<td class="c"><%= fmt_string(user.tags) %></td>
<td class="c"><%= fmt_permissions(user, permissions, 'user', 'vhost',
'<p class="warning">No access</p>') %></td>
<td class="c"><%= fmt_boolean(user.password_hash.length > 0) %></td>
</tr>
<% } %>
</tbody>
</table>
<% } else { %>
<p>... no users ...</p>
<% } %>
<p><span class="help" id="internal-users-only"></span></p>
</div>
</div>
</div>
<div class="section-hidden">
<h2>Add a user</h2>
<div class="hider">
<form action="#/users-add" method="put">
<table class="form">
<tr>
<th><label>Username:</label></th>
<td>
<input type="text" name="username"/>
<span class="mand">*</span>
</td>
</tr>
<tr>
<th>
<label>
<select name="has-password" class="narrow controls-appearance">
<option value="password">Password:</option>
<option value="no-password">No password</option>
</select>
</label>
</th>
<td>
<div id="password-div">
<input type="password" name="password" />
<span class="mand">*</span><br/>
<input type="password" name="password_confirm" />
<span class="mand">*</span>
(confirm)
</div>
<div id="no-password-div" style="display: none;">
User cannot log in using password.
</div>
</td>
</tr>
<tr>
<th><label>Tags:</label></th>
<td>
<input type="text" name="tags" id="tags" />
<span class="help" id="user-tags"/>
<table class="argument-links">
<tr>
<td>Set</td>
<td>
<span class="tag-link" tag="administrator">Admin</span> |
<span class="tag-link" tag="monitoring">Monitoring</span> |
<span class="tag-link" tag="policymaker">Policymaker</span><br />
<span class="tag-link" tag="management">Management</span> |
<span class="tag-link" tag="impersonator">Impersonator</span> |
<span class="tag-link" tag="">None</span>
</td>
</tr>
</table>
</td>
</tr>
</table>
<input type="submit" value="Add user"/>
</form>
</div>
</div>
<h1>Virtual Host: <b><%= fmt_string(vhost.name) %></b></h1>
<% if (permissions.length == 0) { %>
<p class="warning">
No users have permission to access this virtual host.<br/>
Use "Set Permission" below to grant users permission to access this virtual host.
</p>
<% } %>
<div class="section">
<h2>Overview</h2>
<div class="hider updatable">
<%= queue_lengths('lengths-vhost', vhost) %>
<% if (rates_mode != 'none') { %>
<%= message_rates('msg-rates-vhost', vhost.message_stats) %>
<% } %>
<%= data_rates('data-rates-vhost', vhost, 'Data rates') %>
<h3>Details</h3>
<table class="facts">
<tr>
<th>Tracing enabled:</th>
<td><%= fmt_boolean(vhost.tracing) %></td>
<tr>
<th>State:</th>
<td>
<table class="mini">
<% for (var node in vhost.cluster_state) { %>
<tr>
<th><%= fmt_escape_html(node) %> :</th>
<td><%= vhost.cluster_state[node] %>
<% if (vhost.cluster_state[node] == "stopped"){ %>
<form action="#/restart_vhost" method="post">
<input type="hidden" name="node" value="<%= node %>"/>
<input type="hidden" name="vhost" value="<%= vhost.name %>"/>
<input type="submit" value="Restart"/>
</form>
<% } %>
</td>
</tr>
<% } %>
</table>
</td>
</tr>
</table>
</div>
</div>
<%= format('permissions', {'mode': 'vhost', 'permissions': permissions, 'users': users, 'parent': vhost}) %>
<%= format('topic-permissions', {'mode': 'vhost', 'topic_permissions': topic_permissions, 'users':users, 'parent': vhost, 'exchanges': exchanges}) %>
<div class="section-hidden">
<h2>Delete this vhost</h2>
<div class="hider">
<form action="#/vhosts" method="delete" class="confirm">
<input type="hidden" name="name" value="<%= fmt_string(vhost.name) %>"/>
<input type="submit" value="Delete this virtual host"/>
</form>
</div>
</div>
<h1>Virtual Hosts</h1>
<div class="section">
<h2>All virtual hosts</h2>
<div class="hider">
<%= filter_ui(vhosts) %>
<div class="updatable">
<% if (vhosts.length > 0) { %>
<table class="list">
<thead>
<tr>
<%= group_heading('vhosts', 'Overview', [true, true, true]) %>
<%= group_heading('vhosts', 'Messages', []) %>
<%= group_heading('vhosts', 'Network', []) %>
<% if (rates_mode != 'none') { %>
<%= group_heading('vhosts', 'Message rates', []) %>
<% } %>
<th class="plus-minus"><span class="popup-options-link" title="Click to change columns" type="columns" for="vhosts">+/-</span></th>
</tr>
<tr>
<th><%= fmt_sort('Name', 'name') %></th>
<th>Users <span class="help" id="internal-users-only"></span></th>
<th>State</th>
<% if (show_column('vhosts', 'cluster-state')) { %>
<th>Cluster state</th>
<% } %>
<% if (show_column('vhosts', 'msgs-ready')) { %>
<th><%= fmt_sort('Ready', 'messages_ready') %></th>
<% } %>
<% if (show_column('vhosts', 'msgs-unacked')) { %>
<th><%= fmt_sort('Unacked', 'messages_unacknowledged') %></th>
<% } %>
<% if (show_column('vhosts', 'msgs-total')) { %>
<th><%= fmt_sort('Total', 'messages') %></th>
<% } %>
<% if (show_column('vhosts', 'from_client')) { %>
<th><%= fmt_sort('From client', 'recv_oct_details.rate') %></th>
<% } %>
<% if (show_column('vhosts', 'to_client')) { %>
<th><%= fmt_sort('To client', 'send_oct_details.rate') %></th>
<% } %>
<% if (rates_mode != 'none') { %>
<% if (show_column('vhosts', 'rate-publish')) { %>
<th><%= fmt_sort('publish', 'message_stats.publish_details.rate') %></th>
<% } %>
<% if (show_column('vhosts', 'rate-deliver')) { %>
<th><%= fmt_sort('deliver / get','message_stats.deliver_get_details.rate') %></th>
<% } %>
<% } %>
</tr>
</thead>
<tbody>
<%
for (var i = 0; i < vhosts.length; i++) {
var vhost = vhosts[i];
%>
<tr<%= alt_rows(i)%>>
<td><%= link_vhost(vhost.name) %></td>
<td class="c"><%= fmt_permissions(vhost, permissions, 'vhost', 'user',
'<p class="warning">No users</p>') %></td>
<td><%= fmt_vhost_state(vhost) %></td>
<% if (show_column('vhosts', 'cluster-state')) { %>
<td>
<table>
<tbody>
<%
for (var node in vhost.cluster_state) {
var state = vhost.cluster_state[node];
%>
<tr>
<td><%= node %></td>
<td>
<%= state %>
<% if (state == "stopped"){ %>
<form action="#/restart_vhost" method="post" class="confirm">
<input type="hidden" name="node" value="<%= node %>"/>
<input type="hidden" name="vhost" value="<%= vhost.name %>"/>
<input type="submit" value="Restart"/>
</form>
<% } %>
</td>
</tr>
<%
}
%>
</tbody>
</table>
</td>
<% } %>
<% if (show_column('vhosts', 'msgs-ready')) { %>
<td class="r"><%= fmt_num_thousands(vhost.messages_ready) %></td>
<% } %>
<% if (show_column('vhosts', 'msgs-unacked')) { %>
<td class="r"><%= fmt_num_thousands(vhost.messages_unacknowledged) %></td>
<% } %>
<% if (show_column('vhosts', 'msgs-total')) { %>
<td class="r"><%= fmt_num_thousands(vhost.messages) %></td>
<% } %>
<% if (show_column('vhosts', 'from_client')) { %>
<td><%= fmt_detail_rate_bytes(vhost, 'recv_oct') %></td>
<% } %>
<% if (show_column('vhosts', 'to_client')) { %>
<td><%= fmt_detail_rate_bytes(vhost, 'send_oct') %></td>
<% } %>
<% if (rates_mode != 'none') { %>
<% if (show_column('vhosts', 'rate-publish')) { %>
<td class="r"><%= fmt_detail_rate(vhost.message_stats, 'publish') %></td>
<% } %>
<% if (show_column('vhosts', 'rate-deliver')) { %>
<td class="r"><%= fmt_detail_rate(vhost.message_stats, 'deliver_get') %></td>
<% } %>
<% } %>
</tr>
<% } %>
</tbody>
</table>
<% } else { %>
<p>... no vhosts ...</p>
<% } %>
</div>
</div>
</div>
<div class="section-hidden">
<h2>Add a new virtual host</h2>
<div class="hider">
<form action="#/vhosts" method="put">
<table class="form">
<tr>
<th><label>Name:</label></th>
<td><input type="text" name="name"/><span class="mand">*</span></td>
</tr>
</table>
<input type="submit" value="Add virtual host"/>
</form>
</div>
</div>
{application, 'rabbitmq_management_agent', [
{description, "RabbitMQ Management Agent"},
{vsn, "3.7.28"},
{id, "v3.7.27-1-g1d21a2f"},
{modules, ['Elixir.RabbitMQ.CLI.Ctl.Commands.ResetStatsDbCommand','exometer_slide','rabbit_mgmt_agent_app','rabbit_mgmt_agent_config','rabbit_mgmt_agent_sup','rabbit_mgmt_agent_sup_sup','rabbit_mgmt_data','rabbit_mgmt_db_handler','rabbit_mgmt_external_stats','rabbit_mgmt_format','rabbit_mgmt_gc','rabbit_mgmt_metrics_collector','rabbit_mgmt_metrics_gc','rabbit_mgmt_storage']},
{registered, [rabbitmq_management_agent_sup]},
{applications, [kernel,stdlib,xmerl,mnesia,ranch,ssl,crypto,public_key,rabbit_common,rabbit]},
{mod, {rabbit_mgmt_agent_app, []}},
{env, [
{rates_mode, basic},
{sample_retention_policies,
%% List of {MaxAgeInSeconds, SampleEveryNSeconds}
[{global, [{605, 5}, {3660, 60}, {29400, 600}, {86400, 1800}]},
{basic, [{605, 5}, {3600, 60}]},
{detailed, [{605, 5}]}]}
]},
{broker_version_requirements, []}
]}.
\ No newline at end of file
%% The contents of this file are subject to the Mozilla Public License
%% Version 1.1 (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License at
%% http://www.mozilla.org/MPL/
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
%% License for the specific language governing rights and limitations
%% under the License.
%%
%% The Original Code is RabbitMQ.
%%
%% The Initial Developer of the Original Code is Pivotal Software, Inc.
%% Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
%%
-type(event_type() :: queue_stats | queue_exchange_stats | vhost_stats
| channel_queue_stats | channel_stats
| channel_exchange_stats | exchange_stats
| node_stats | node_node_stats | connection_stats).
-type(type() :: deliver_get | fine_stats | queue_msg_rates | queue_msg_counts
| coarse_node_stats | coarse_node_node_stats | coarse_conn_stats
| process_stats).
-type(table_name() :: atom()).
-define(TABLES, [{connection_stats_coarse_conn_stats, set},
{vhost_stats_coarse_conn_stats, set},
{connection_created_stats, set},
{connection_stats, set},
{channel_created_stats, set},
{channel_stats, set},
{channel_stats_fine_stats, set},
{channel_exchange_stats_fine_stats, set},
{channel_queue_stats_deliver_stats, set},
{vhost_stats_fine_stats, set},
{queue_stats_deliver_stats, set},
{vhost_stats_deliver_stats, set},
{channel_stats_deliver_stats, set},
{channel_process_stats, set},
{queue_stats_publish, set},
{queue_exchange_stats_publish, set},
{exchange_stats_publish_out, set},
{exchange_stats_publish_in, set},
{consumer_stats, set},
{queue_stats, set},
{queue_msg_stats, set},
{vhost_msg_stats, set},
{queue_process_stats, set},
{node_stats, set},
{node_coarse_stats, set},
{node_persister_stats, set},
{node_node_stats, set},
{node_node_coarse_stats, set},
{queue_msg_rates, set},
{vhost_msg_rates, set},
{connection_churn_rates, set}]).
-define(INDEX_TABLES, [consumer_stats_queue_index,
consumer_stats_channel_index,
channel_exchange_stats_fine_stats_exchange_index,
channel_exchange_stats_fine_stats_channel_index,
channel_queue_stats_deliver_stats_queue_index,
channel_queue_stats_deliver_stats_channel_index,
queue_exchange_stats_publish_queue_index,
queue_exchange_stats_publish_exchange_index,
node_node_coarse_stats_node_index]).
-define(GC_EVENTS, [connection_closed, channel_closed, consumer_deleted,
exchange_deleted, queue_deleted, vhost_deleted,
node_node_deleted, channel_consumer_deleted]).
-define(DELEGATE_PREFIX, "delegate_management_").
%%------------------------------------------------------------------------------
%% Only for documentation and testing purposes, so we keep track of the number and
%% order of the metrics
-define(connection_stats_coarse_conn_stats(Recv_oct, Send_oct, Reductions),
{Recv_oct, Send_oct, Reductions}).
-define(vhost_stats_coarse_conn_stats(Recv_oct, Send_oct), {Recv_oct, Send_oct}).
-define(connection_created_stats(Id, Name, Props), {Id, Name, Props}).
-define(connection_stats(Id, Props), {Id, Props}).
-define(channel_created_stats(Id, Name, Props), {Id, Name, Props}).
-define(channel_consumer_created_stats(Queue, ChPid, ConsumerTag),
{Queue, {ChPid, ConsumerTag}}).
-define(channel_stats(Id, Props), {Id, Props}).
-define(channel_stats_fine_stats(Publish, Confirm, Return_unroutable),
{Publish, Confirm, Return_unroutable}).
-define(channel_exchange_stats_fine_stats(Publish, Confirm, Return_unroutable),
{Publish, Confirm, Return_unroutable}).
-define(channel_queue_stats_deliver_stats(Get, Get_no_ack, Deliver, Deliver_no_ack,
Redeliver, Ack, Deliver_get),
{Get, Get_no_ack, Deliver, Deliver_no_ack, Redeliver, Ack, Deliver_get}).
-define(vhost_stats_fine_stats(Publish, Confirm, Return_unroutable),
{Publish, Confirm, Return_unroutable}).
-define(queue_stats_deliver_stats(Get, Get_no_ack, Deliver, Deliver_no_ack,
Redeliver, Ack, Deliver_get),
{Get, Get_no_ack, Deliver, Deliver_no_ack, Redeliver, Ack, Deliver_get}).
-define(vhost_stats_deliver_stats(Get, Get_no_ack, Deliver, Deliver_no_ack,
Redeliver, Ack, Deliver_get),
{Get, Get_no_ack, Deliver, Deliver_no_ack, Redeliver, Ack, Deliver_get}).
-define(channel_stats_deliver_stats(Get, Get_no_ack, Deliver, Deliver_no_ack,
Redeliver, Ack, Deliver_get),
{Get, Get_no_ack, Deliver, Deliver_no_ack, Redeliver, Ack, Deliver_get}).
-define(channel_process_stats(Reductions), {Reductions}).
-define(queue_stats_publish(Publish), {Publish}).
-define(queue_exchange_stats_publish(Publish), {Publish}).
-define(exchange_stats_publish_out(Publish_out), {Publish_out}).
-define(exchange_stats_publish_in(Publish_in), {Publish_in}).
-define(consumer_stats(Id, Props), {Id, Props}).
-define(queue_stats(Id, Props), {Id, Props}).
-define(queue_msg_stats(Messages_ready, Messages_unacknowledged, Messages),
{Messages_ready, Messages_unacknowledged, Messages}).
-define(vhost_msg_stats(Messages_ready, Messages_unacknowledged, Messages),
{Messages_ready, Messages_unacknowledged, Messages}).
-define(queue_process_stats(Reductions), {Reductions}).
-define(node_stats(Id, Props), {Id, Props}).
-define(node_coarse_stats(Fd_used, Sockets_used, Mem_used, Disk_free, Proc_used,
Gc_num, Gc_bytes_reclaimed, Context_switches),
{Fd_used, Sockets_used, Mem_used, Disk_free, Proc_used, Gc_num,
Gc_bytes_reclaimed, Context_switches}).
-define(node_persister_stats(Io_read_count, Io_read_bytes, Io_read_avg_time, Io_write_count,
Io_write_bytes, Io_write_avg_time, Io_sync_count, Io_sync_avg_time,
Io_seek_count, Io_seek_avg_time, Io_reopen_count, Mnesia_ram_tx_count,
Mnesia_disk_tx_count, Msg_store_read_count, Msg_store_write_count,
Queue_index_journal_write_count, Queue_index_write_count,
Queue_index_read_count, Io_file_handle_open_attempt_count,
Io_file_handle_open_attempt_avg_time),
{Io_read_count, Io_read_bytes, Io_read_avg_time, Io_write_count, Io_write_bytes,
Io_write_avg_time, Io_sync_count, Io_sync_avg_time, Io_seek_count, Io_seek_avg_time,
Io_reopen_count, Mnesia_ram_tx_count, Mnesia_disk_tx_count, Msg_store_read_count,
Msg_store_write_count, Queue_index_journal_write_count, Queue_index_write_count,
Queue_index_read_count, Io_file_handle_open_attempt_count,
Io_file_handle_open_attempt_avg_time}).
-define(node_node_stats(Send_bytes, Recv_bytes), {Send_bytes, Recv_bytes}).
-define(node_node_coarse_stats(Send_bytes, Recv_bytes), {Send_bytes, Recv_bytes}).
-define(queue_msg_rates(Disk_reads, Disk_writes), {Disk_reads, Disk_writes}).
-define(vhost_msg_rates(Disk_reads, Disk_writes), {Disk_reads, Disk_writes}).
-define(old_aggr_stats(Id, Stats), {Id, Stats}).
-define(connection_churn_rates(Connection_created, Connection_closed, Channel_created,
Channel_closed, Queue_declared, Queue_created,
Queue_deleted),
{Connection_created, Connection_closed, Channel_created, Channel_closed,
Queue_declared, Queue_created, Queue_deleted}).
-define(stats_per_table(Table),
case Table of
connection_stats_coarse_conn_stats ->
[recv_oct, send_oct, reductions];
vhost_stats_coarse_conn_stats ->
[recv_oct, send_oct];
T when T =:= channel_stats_fine_stats;
T =:= channel_exchange_stats_fine_stats;
T =:= vhost_stats_fine_stats ->
[publish, confirm, return_unroutable];
T when T =:= channel_queue_stats_deliver_stats;
T =:= queue_stats_deliver_stats;
T =:= vhost_stats_deliver_stats;
T =:= channel_stats_deliver_stats ->
[get, get_no_ack, deliver, deliver_no_ack, redeliver, ack, deliver_get];
T when T =:= channel_process_stats;
T =:= queue_process_stats ->
[reductions];
T when T =:= queue_stats_publish;
T =:= queue_exchange_stats_publish ->
[publish];
exchange_stats_publish_out ->
[publish_out];
exchange_stats_publish_in ->
[publish_in];
T when T =:= queue_msg_stats;
T =:= vhost_msg_stats ->
[messages_ready, messages_unacknowledged, messages];
node_coarse_stats ->
[fd_used, sockets_used, mem_used, disk_free, proc_used, gc_num,
gc_bytes_reclaimed, context_switches];
node_persister_stats ->
[io_read_count, io_read_bytes, io_read_avg_time, io_write_count,
io_write_bytes, io_write_avg_time, io_sync_count, io_sync_avg_time,
io_seek_count, io_seek_avg_time, io_reopen_count, mnesia_ram_tx_count,
mnesia_disk_tx_count, msg_store_read_count, msg_store_write_count,
queue_index_journal_write_count, queue_index_write_count,
queue_index_read_count, io_file_handle_open_attempt_count,
io_file_handle_open_attempt_avg_time];
node_node_coarse_stats ->
[send_bytes, recv_bytes];
T when T =:= queue_msg_rates;
T =:= vhost_msg_rates ->
[disk_reads, disk_writes];
T when T =:= connection_churn_rates ->
[connection_created, connection_closed, channel_created, channel_closed, queue_declared, queue_created, queue_deleted]
end).
%%------------------------------------------------------------------------------
%% The contents of this file are subject to the Mozilla Public License
%% Version 1.1 (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License at
%% http://www.mozilla.org/MPL/
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
%% License for the specific language governing rights and limitations
%% under the License.
%%
%% The Original Code is RabbitMQ Management Console.
%%
%% The Initial Developer of the Original Code is GoPivotal, Inc.
%% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved.
%%
-record(context, {user,
password = none,
impl}). % storage for a context of the resource handler
-record(range, {first :: integer(),
last :: integer(),
incr :: integer()}).
{application, 'rabbitmq_web_dispatch', [
{description, "RabbitMQ Web Dispatcher"},
{vsn, "3.7.28"},
{id, "v3.7.27-1-g1d21a2f"},
{modules, ['rabbit_cowboy_middleware','rabbit_cowboy_redirect','rabbit_cowboy_stream_h','rabbit_web_dispatch','rabbit_web_dispatch_app','rabbit_web_dispatch_listing_handler','rabbit_web_dispatch_registry','rabbit_web_dispatch_sup','rabbit_web_dispatch_util','webmachine_log','webmachine_log_handler']},
{registered, [rabbitmq_web_dispatch_sup]},
{applications, [kernel,stdlib,inets,rabbit_common,rabbit,cowboy]},
{mod, {rabbit_web_dispatch_app, []}},
{env, []},
{broker_version_requirements, []}
]}.
\ No newline at end of file
[store_msg,persistent_bytes,multiple_routing_keys,exchange_options,queue_options,topic_permission,vhost_limits,user_password_hashing,cluster_name,policy_apply_to,topic_trie_node,mirrored_supervisor,gm,user_admin_to_tags,exchange_event_serial,semi_durable_route,topic_trie,add_opts_to_listener,remove_user_scope,move_messages_to_vhost_store].
% ==============================
% Rabbit app section
% ==============================
%%
%% Network Connectivity
%% ====================
%%
%% By default, RabbitMQ will listen on all interfaces, using
%% the standard (reserved) AMQP port.
%%
%% {tcp_listeners, [5672]},
%% To listen on a specific interface, provide a tuple of {IpAddress, Port}.
%% For example, to listen only on localhost for both IPv4 and IPv6:
%%
%% {tcp_listeners, [{"127.0.0.1", 5672},
%% {"[::1]", 5672}]},
{mapping, "listeners.tcp", "rabbit.tcp_listeners",[
{datatype, {enum, [none]}}
]}.
{mapping, "listeners.tcp.$name", "rabbit.tcp_listeners",[
{datatype, [integer, ip]}
]}.
{translation, "rabbit.tcp_listeners",
fun(Conf) ->
case cuttlefish:conf_get("listeners.tcp", Conf, undefined) of
none -> [];
_ ->
Settings = cuttlefish_variable:filter_by_prefix("listeners.tcp", Conf),
[ V || {_, V} <- Settings ]
end
end}.
%% TLS listeners are configured in the same fashion as TCP listeners,
%% including the option to control the choice of interface.
%%
%% {ssl_listeners, [5671]},
{mapping, "listeners.ssl", "rabbit.ssl_listeners",[
{datatype, {enum, [none]}}
]}.
{mapping, "listeners.ssl.$name", "rabbit.ssl_listeners",[
{datatype, [integer, ip]}
]}.
{translation, "rabbit.ssl_listeners",
fun(Conf) ->
case cuttlefish:conf_get("listeners.ssl", Conf, undefined) of
none -> [];
_ ->
Settings = cuttlefish_variable:filter_by_prefix("listeners.ssl", Conf),
[ V || {_, V} <- Settings ]
end
end}.
%% Number of Erlang processes that will accept connections for the TCP
%% and SSL listeners.
%%
%% {num_tcp_acceptors, 10},
%% {num_ssl_acceptors, 1},
{mapping, "num_acceptors.ssl", "rabbit.num_ssl_acceptors", [
{datatype, integer}
]}.
{mapping, "num_acceptors.tcp", "rabbit.num_tcp_acceptors", [
{datatype, integer}
]}.
%% Maximum time for AMQP 0-8/0-9/0-9-1 handshake (after socket connection
%% and SSL handshake), in milliseconds.
%%
%% {handshake_timeout, 10000},
{mapping, "handshake_timeout", "rabbit.handshake_timeout", [
{datatype, integer}
]}.
%% Set to 'true' to perform reverse DNS lookups when accepting a
%% connection. Hostnames will then be shown instead of IP addresses
%% in rabbitmqctl and the management plugin.
%%
%% {reverse_dns_lookups, true},
{mapping, "reverse_dns_lookups", "rabbit.reverse_dns_lookups", [
{datatype, {enum, [true, false]}}
]}.
{mapping, "erlang.K", "vm_args.+K", [
{default, "true"},
{level, advanced}
]}.
%%
%% Security / AAA
%% ==============
%%
%% The default "guest" user is only permitted to access the server
%% via a loopback interface (e.g. localhost).
%% {loopback_users, [<<"guest">>]},
%%
%% Uncomment the following line if you want to allow access to the
%% guest user from anywhere on the network.
%% {loopback_users, []},
{mapping, "loopback_users", "rabbit.loopback_users", [
{datatype, {enum, [none]}}
]}.
{mapping, "loopback_users.$user", "rabbit.loopback_users", [
{datatype, atom}
]}.
{translation, "rabbit.loopback_users",
fun(Conf) ->
None = cuttlefish:conf_get("loopback_users", Conf, undefined),
case None of
none -> [];
_ ->
Settings = cuttlefish_variable:filter_by_prefix("loopback_users", Conf),
[ list_to_binary(U) || {["loopback_users", U], V} <- Settings, V == true ]
end
end}.
%% TLS options.
%% See https://www.rabbitmq.com/ssl.html for full documentation.
%%
%% {ssl_options, [{cacertfile, "/path/to/testca/cacert.pem"},
%% {certfile, "/path/to/server/cert.pem"},
%% {keyfile, "/path/to/server/key.pem"},
%% {verify, verify_peer},
%% {fail_if_no_peer_cert, false}]},
{mapping, "ssl_allow_poodle_attack", "rabbit.ssl_allow_poodle_attack",
[{datatype, {enum, [true, false]}}]}.
{mapping, "ssl_options", "rabbit.ssl_options", [
{datatype, {enum, [none]}}
]}.
{translation, "rabbit.ssl_options",
fun(Conf) ->
case cuttlefish:conf_get("ssl_options", Conf, undefined) of
none -> [];
_ -> cuttlefish:invalid("Invalid ssl_options")
end
end}.
{mapping, "ssl_options.verify", "rabbit.ssl_options.verify", [
{datatype, {enum, [verify_peer, verify_none]}}]}.
{mapping, "ssl_options.fail_if_no_peer_cert", "rabbit.ssl_options.fail_if_no_peer_cert", [
{datatype, {enum, [true, false]}}]}.
{mapping, "ssl_options.cacertfile", "rabbit.ssl_options.cacertfile",
[{datatype, string}, {validators, ["file_accessible"]}]}.
{mapping, "ssl_options.certfile", "rabbit.ssl_options.certfile",
[{datatype, string}, {validators, ["file_accessible"]}]}.
{mapping, "ssl_options.cacerts.$name", "rabbit.ssl_options.cacerts",
[{datatype, string}]}.
{translation, "rabbit.ssl_options.cacerts",
fun(Conf) ->
Settings = cuttlefish_variable:filter_by_prefix("ssl_options.cacerts", Conf),
[ list_to_binary(V) || {_, V} <- Settings ]
end}.
{mapping, "ssl_options.cert", "rabbit.ssl_options.cert",
[{datatype, string}]}.
{translation, "rabbit.ssl_options.cert",
fun(Conf) ->
list_to_binary(cuttlefish:conf_get("ssl_options.cert", Conf))
end}.
{mapping, "ssl_options.client_renegotiation", "rabbit.ssl_options.client_renegotiation",
[{datatype, {enum, [true, false]}}]}.
{mapping, "ssl_options.crl_check", "rabbit.ssl_options.crl_check",
[{datatype, [{enum, [true, false, peer, best_effort]}]}]}.
{mapping, "ssl_options.depth", "rabbit.ssl_options.depth",
[{datatype, integer}, {validators, ["byte"]}]}.
{mapping, "ssl_options.dh", "rabbit.ssl_options.dh",
[{datatype, string}]}.
{translation, "rabbit.ssl_options.dh",
fun(Conf) ->
list_to_binary(cuttlefish:conf_get("ssl_options.dh", Conf))
end}.
{mapping, "ssl_options.dhfile", "rabbit.ssl_options.dhfile",
[{datatype, string}, {validators, ["file_accessible"]}]}.
{mapping, "ssl_options.honor_cipher_order", "rabbit.ssl_options.honor_cipher_order",
[{datatype, {enum, [true, false]}}]}.
{mapping, "ssl_options.honor_ecc_order", "rabbit.ssl_options.honor_ecc_order",
[{datatype, {enum, [true, false]}}]}.
{mapping, "ssl_options.key.RSAPrivateKey", "rabbit.ssl_options.key",
[{datatype, string}]}.
{mapping, "ssl_options.key.DSAPrivateKey", "rabbit.ssl_options.key",
[{datatype, string}]}.
{mapping, "ssl_options.key.PrivateKeyInfo", "rabbit.ssl_options.key",
[{datatype, string}]}.
{translation, "rabbit.ssl_options.key",
fun(Conf) ->
case cuttlefish_variable:filter_by_prefix("ssl_options.key", Conf) of
[{[_,_,Key], Val}|_] -> {list_to_atom(Key), list_to_binary(Val)};
_ -> undefined
end
end}.
{mapping, "ssl_options.keyfile", "rabbit.ssl_options.keyfile",
[{datatype, string}, {validators, ["file_accessible"]}]}.
{mapping, "ssl_options.log_alert", "rabbit.ssl_options.log_alert",
[{datatype, {enum, [true, false]}}]}.
{mapping, "ssl_options.password", "rabbit.ssl_options.password",
[{datatype, string}]}.
{mapping, "ssl_options.psk_identity", "rabbit.ssl_options.psk_identity",
[{datatype, string}]}.
{mapping, "ssl_options.reuse_sessions", "rabbit.ssl_options.reuse_sessions",
[{datatype, {enum, [true, false]}}]}.
{mapping, "ssl_options.secure_renegotiate", "rabbit.ssl_options.secure_renegotiate",
[{datatype, {enum, [true, false]}}]}.
{mapping, "ssl_options.versions.$version", "rabbit.ssl_options.versions",
[{datatype, atom}]}.
{translation, "rabbit.ssl_options.versions",
fun(Conf) ->
Settings = cuttlefish_variable:filter_by_prefix("ssl_options.versions", Conf),
[V || {_, V} <- Settings]
end}.
{mapping, "ssl_options.ciphers.$cipher", "rabbit.ssl_options.ciphers",
[{datatype, string}]}.
{translation, "rabbit.ssl_options.ciphers",
fun(Conf) ->
Settings = cuttlefish_variable:filter_by_prefix("ssl_options.ciphers", Conf),
lists:reverse([V || {_, V} <- Settings])
end}.
%% ===========================================================================
%% Choose the available SASL mechanism(s) to expose.
%% The two default (built in) mechanisms are 'PLAIN' and
%% 'AMQPLAIN'. Additional mechanisms can be added via
%% plugins.
%%
%% See https://www.rabbitmq.com/authentication.html for more details.
%%
%% {auth_mechanisms, ['PLAIN', 'AMQPLAIN']},
{mapping, "auth_mechanisms.$name", "rabbit.auth_mechanisms", [
{datatype, atom}]}.
{translation, "rabbit.auth_mechanisms",
fun(Conf) ->
Settings = cuttlefish_variable:filter_by_prefix("auth_mechanisms", Conf),
[ V || {_, V} <- Settings ]
end}.
%% Select an authentication backend to use. RabbitMQ provides an
%% internal backend in the core.
%%
%% {auth_backends, [rabbit_auth_backend_internal]},
{translation, "rabbit.auth_backends",
fun(Conf) ->
Settings = cuttlefish_variable:filter_by_prefix("auth_backends", Conf),
BackendModule = fun
(internal) -> rabbit_auth_backend_internal;
(ldap) -> rabbit_auth_backend_ldap;
(http) -> rabbit_auth_backend_http;
(cache) -> rabbit_auth_backend_cache;
(amqp) -> rabbit_auth_backend_amqp;
(dummy) -> rabbit_auth_backend_dummy;
(Other) when is_atom(Other) -> Other;
(_) -> cuttlefish:invalid("Unknown/unsupported auth backend")
end,
AuthBackends = [{Num, {default, BackendModule(V)}} || {["auth_backends", Num], V} <- Settings],
AuthNBackends = [{Num, {authn, BackendModule(V)}} || {["auth_backends", Num, "authn"], V} <- Settings],
AuthZBackends = [{Num, {authz, BackendModule(V)}} || {["auth_backends", Num, "authz"], V} <- Settings],
Backends = lists:foldl(
fun({NumStr, {Type, V}}, Acc) ->
Num = case catch list_to_integer(NumStr) of
N when is_integer(N) -> N;
Err ->
cuttlefish:invalid(
iolist_to_binary(io_lib:format(
"Auth backend position in the chain should be an integer ~p", [Err])))
end,
NewVal = case dict:find(Num, Acc) of
{ok, {AuthN, AuthZ}} ->
case {Type, AuthN, AuthZ} of
{authn, undefined, _} ->
{V, AuthZ};
{authz, _, undefined} ->
{AuthN, V};
_ ->
cuttlefish:invalid(
iolist_to_binary(
io_lib:format(
"Auth backend already defined for the ~pth ~p backend",
[Num, Type])))
end;
error ->
case Type of
authn -> {V, undefined};
authz -> {undefined, V};
default -> {V, V}
end
end,
dict:store(Num, NewVal, Acc)
end,
dict:new(),
AuthBackends ++ AuthNBackends ++ AuthZBackends),
lists:map(
fun
({Num, {undefined, AuthZ}}) ->
cuttlefish:warn(
io_lib:format(
"Auth backend undefined for the ~pth authz backend. Using ~p",
[Num, AuthZ])),
{AuthZ, AuthZ};
({Num, {AuthN, undefined}}) ->
cuttlefish:warn(
io_lib:format(
"Authz backend undefined for the ~pth authn backend. Using ~p",
[Num, AuthN])),
{AuthN, AuthN};
({_Num, {Auth, Auth}}) -> Auth;
({_Num, {AuthN, AuthZ}}) -> {AuthN, AuthZ}
end,
lists:keysort(1, dict:to_list(Backends)))
end}.
{mapping, "auth_backends.$num", "rabbit.auth_backends", [
{datatype, atom}
]}.
{mapping, "auth_backends.$num.authn", "rabbit.auth_backends",[
{datatype, atom}
]}.
{mapping, "auth_backends.$num.authz", "rabbit.auth_backends",[
{datatype, atom}
]}.
%% This pertains to both the rabbitmq_auth_mechanism_ssl plugin and
%% STOMP ssl_cert_login configurations. See the rabbitmq_stomp
%% configuration section later in this file and the README in
%% https://github.com/rabbitmq/rabbitmq-auth-mechanism-ssl for further
%% details.
%%
%% To use the SSL cert's CN instead of its DN as the username
%%
%% {ssl_cert_login_from, common_name},
{mapping, "ssl_cert_login_from", "rabbit.ssl_cert_login_from", [
{datatype, {enum, [distinguished_name, common_name]}}
]}.
%% TLS handshake timeout, in milliseconds.
%%
%% {ssl_handshake_timeout, 5000},
{mapping, "ssl_handshake_timeout", "rabbit.ssl_handshake_timeout", [
{datatype, integer}
]}.
%% Cluster name
{mapping, "cluster_name", "rabbit.cluster_name", [
{datatype, string}
]}.
%% Default worker process pool size. Used to limit maximum concurrency rate
%% of certain operations, e.g. queue initialisation and recovery on node boot.
{mapping, "default_worker_pool_size", "rabbit.default_worker_pool_size", [
{datatype, integer}, {validators, ["non_negative_integer"]}
]}.
%% Password hashing implementation. Will only affect newly
%% created users. To recalculate hash for an existing user
%% it's necessary to update her password.
%%
%% When importing definitions exported from versions earlier
%% than 3.6.0, it is possible to go back to MD5 (only do this
%% as a temporary measure!) by setting this to rabbit_password_hashing_md5.
%%
%% To use SHA-512, set to rabbit_password_hashing_sha512.
%%
%% {password_hashing_module, rabbit_password_hashing_sha256},
{mapping, "password_hashing_module", "rabbit.password_hashing_module", [
{datatype, atom}
]}.
%% Credential validation.
%%
{mapping, "credential_validator.validation_backend", "rabbit.credential_validator.validation_backend", [
{datatype, atom}
]}.
{mapping, "credential_validator.min_length", "rabbit.credential_validator.min_length", [
{datatype, integer}, {validators, ["non_negative_integer"]}
]}.
{mapping, "credential_validator.regexp", "rabbit.credential_validator.regexp", [
{datatype, string}
]}.
%%
%% Default User / VHost
%% ====================
%%
%% On first start RabbitMQ will create a vhost and a user. These
%% config items control what gets created. See
%% https://www.rabbitmq.com/access-control.html for further
%% information about vhosts and access control.
%%
%% {default_vhost, <<"/">>},
%% {default_user, <<"guest">>},
%% {default_pass, <<"guest">>},
%% {default_permissions, [<<".*">>, <<".*">>, <<".*">>]},
{mapping, "default_vhost", "rabbit.default_vhost", [
{datatype, string}
]}.
{translation, "rabbit.default_vhost",
fun(Conf) ->
list_to_binary(cuttlefish:conf_get("default_vhost", Conf))
end}.
{mapping, "default_user", "rabbit.default_user", [
{datatype, string}
]}.
{translation, "rabbit.default_user",
fun(Conf) ->
list_to_binary(cuttlefish:conf_get("default_user", Conf))
end}.
{mapping, "default_pass", "rabbit.default_pass", [
{datatype, string}
]}.
{translation, "rabbit.default_pass",
fun(Conf) ->
list_to_binary(cuttlefish:conf_get("default_pass", Conf))
end}.
{mapping, "default_permissions.configure", "rabbit.default_permissions", [
{datatype, string}
]}.
{mapping, "default_permissions.read", "rabbit.default_permissions", [
{datatype, string}
]}.
{mapping, "default_permissions.write", "rabbit.default_permissions", [
{datatype, string}
]}.
{translation, "rabbit.default_permissions",
fun(Conf) ->
Settings = cuttlefish_variable:filter_by_prefix("default_permissions", Conf),
Configure = proplists:get_value(["default_permissions", "configure"], Settings),
Read = proplists:get_value(["default_permissions", "read"], Settings),
Write = proplists:get_value(["default_permissions", "write"], Settings),
[list_to_binary(Configure), list_to_binary(Read), list_to_binary(Write)]
end}.
%% Tags for default user
%%
%% For more details about tags, see the documentation for the
%% Management Plugin at https://www.rabbitmq.com/management.html.
%%
%% {default_user_tags, [administrator]},
{mapping, "default_user_tags.$tag", "rabbit.default_user_tags",
[{datatype, {enum, [true, false]}}]}.
{translation, "rabbit.default_user_tags",
fun(Conf) ->
Settings = cuttlefish_variable:filter_by_prefix("default_user_tags", Conf),
[ list_to_atom(Key) || {[_,Key], Val} <- Settings, Val == true ]
end}.
%%
%% Additional network and protocol related configuration
%% =====================================================
%%
%% Set the default connection heartbeat timeout (in seconds).
%%
%% {heartbeat, 600},
{mapping, "heartbeat", "rabbit.heartbeat", [{datatype, integer}]}.
%% Set the max permissible size of an AMQP 0-9-1 frame (in bytes).
%%
%% {frame_max, 131072},
{mapping, "frame_max", "rabbit.frame_max", [{datatype, bytesize}]}.
%% Set the max frame size the server will accept before connection
%% tuning starts
%%
%% {initial_frame_max, 4096},
{mapping, "initial_frame_max", "rabbit.initial_frame_max", [{datatype, bytesize}]}.
%% Set the max permissible number of channels per connection.
%% 0 means "no limit".
%%
%% {channel_max, 0},
{mapping, "channel_max", "rabbit.channel_max", [{datatype, integer}]}.
%% Set the max permissible number of client connections per node.
%% `infinity` means "no limit".
%%
%% {connection_max, infinity},
{mapping, "connection_max", "rabbit.connection_max",
[{datatype, [{atom, infinity}, integer]}]}.
{translation, "rabbit.connection_max",
fun(Conf) ->
case cuttlefish:conf_get("connection_max", Conf, undefined) of
undefined -> cuttlefish:unset();
infinity -> infinity;
Val when is_integer(Val) -> Val;
_ -> cuttlefish:invalid("should be a non-negative integer")
end
end
}.
%% Customising Socket Options.
%%
%% See (https://www.erlang.org/doc/man/inet.html#setopts-2) for
%% further documentation.
%%
%% {tcp_listen_options, [{backlog, 128},
%% {nodelay, true},
%% {exit_on_close, false}]},
%% TCP listener section ======================================================
{mapping, "tcp_listen_options", "rabbit.tcp_listen_options", [
{datatype, {enum, [none]}}]}.
{translation, "rabbit.tcp_listen_options",
fun(Conf) ->
case cuttlefish:conf_get("tcp_listen_options", Conf, undefined) of
none -> [];
_ -> cuttlefish:invalid("Invalid tcp_listen_options")
end
end}.
{mapping, "tcp_listen_options.backlog", "rabbit.tcp_listen_options.backlog", [
{datatype, integer}
]}.
{mapping, "tcp_listen_options.nodelay", "rabbit.tcp_listen_options.nodelay", [
{datatype, {enum, [true, false]}}
]}.
{mapping, "tcp_listen_options.buffer", "rabbit.tcp_listen_options.buffer",
[{datatype, integer}]}.
{mapping, "tcp_listen_options.delay_send", "rabbit.tcp_listen_options.delay_send",
[{datatype, {enum, [true, false]}}]}.
{mapping, "tcp_listen_options.dontroute", "rabbit.tcp_listen_options.dontroute",
[{datatype, {enum, [true, false]}}]}.
{mapping, "tcp_listen_options.exit_on_close", "rabbit.tcp_listen_options.exit_on_close",
[{datatype, {enum, [true, false]}}]}.
{mapping, "tcp_listen_options.fd", "rabbit.tcp_listen_options.fd",
[{datatype, integer}]}.
{mapping, "tcp_listen_options.high_msgq_watermark", "rabbit.tcp_listen_options.high_msgq_watermark",
[{datatype, integer}]}.
{mapping, "tcp_listen_options.high_watermark", "rabbit.tcp_listen_options.high_watermark",
[{datatype, integer}]}.
{mapping, "tcp_listen_options.keepalive", "rabbit.tcp_listen_options.keepalive",
[{datatype, {enum, [true, false]}}]}.
{mapping, "tcp_listen_options.low_msgq_watermark", "rabbit.tcp_listen_options.low_msgq_watermark",
[{datatype, integer}]}.
{mapping, "tcp_listen_options.low_watermark", "rabbit.tcp_listen_options.low_watermark",
[{datatype, integer}]}.
{mapping, "tcp_listen_options.port", "rabbit.tcp_listen_options.port",
[{datatype, integer}, {validators, ["port"]}]}.
{mapping, "tcp_listen_options.priority", "rabbit.tcp_listen_options.priority",
[{datatype, integer}]}.
{mapping, "tcp_listen_options.recbuf", "rabbit.tcp_listen_options.recbuf",
[{datatype, integer}]}.
{mapping, "tcp_listen_options.send_timeout", "rabbit.tcp_listen_options.send_timeout",
[{datatype, integer}]}.
{mapping, "tcp_listen_options.send_timeout_close", "rabbit.tcp_listen_options.send_timeout_close",
[{datatype, {enum, [true, false]}}]}.
{mapping, "tcp_listen_options.sndbuf", "rabbit.tcp_listen_options.sndbuf",
[{datatype, integer}]}.
{mapping, "tcp_listen_options.tos", "rabbit.tcp_listen_options.tos",
[{datatype, integer}]}.
{mapping, "tcp_listen_options.linger.on", "rabbit.tcp_listen_options.linger",
[{datatype, {enum, [true, false]}}]}.
{mapping, "tcp_listen_options.linger.timeout", "rabbit.tcp_listen_options.linger",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{translation, "rabbit.tcp_listen_options.linger",
fun(Conf) ->
LingerOn = cuttlefish:conf_get("tcp_listen_options.linger.on", Conf, false),
LingerTimeout = cuttlefish:conf_get("tcp_listen_options.linger.timeout", Conf, 0),
{LingerOn, LingerTimeout}
end}.
%% ==========================================================================
%%
%% Resource Limits & Flow Control
%% ==============================
%%
%% See https://www.rabbitmq.com/memory.html for full details.
%% Memory-based Flow Control threshold.
%%
%% {vm_memory_high_watermark, 0.4},
%% Alternatively, we can set a limit (in bytes) of RAM used by the node.
%%
%% {vm_memory_high_watermark, {absolute, 1073741824}},
%%
%% Or you can set absolute value using memory unit symbols (with RabbitMQ 3.6.0+).
%%
%% {vm_memory_high_watermark, {absolute, "1024M"}},
%%
%% Supported unit symbols:
%%
%% k, kiB: kibibytes (2^10 - 1,024 bytes)
%% M, MiB: mebibytes (2^20 - 1,048,576 bytes)
%% G, GiB: gibibytes (2^30 - 1,073,741,824 bytes)
%% kB: kilobytes (10^3 - 1,000 bytes)
%% MB: megabytes (10^6 - 1,000,000 bytes)
%% GB: gigabytes (10^9 - 1,000,000,000 bytes)
{mapping, "vm_memory_high_watermark.relative", "rabbit.vm_memory_high_watermark", [
{datatype, float}]}.
{mapping, "vm_memory_high_watermark.absolute", "rabbit.vm_memory_high_watermark", [
{datatype, [integer, string]}]}.
{translation, "rabbit.vm_memory_high_watermark",
fun(Conf) ->
Settings = cuttlefish_variable:filter_by_prefix("vm_memory_high_watermark", Conf),
Absolute = proplists:get_value(["vm_memory_high_watermark", "absolute"], Settings),
Relative = proplists:get_value(["vm_memory_high_watermark", "relative"], Settings),
case {Absolute, Relative} of
{undefined, undefined} -> cuttlefish:invalid("No vm watermark defined");
{_, undefined} -> {absolute, Absolute};
_ -> Relative
end
end}.
%% Fraction of the high watermark limit at which queues start to
%% page message out to disc in order to free up memory.
%%
%% Values greater than 0.9 can be dangerous and should be used carefully.
%%
%% {vm_memory_high_watermark_paging_ratio, 0.5},
{mapping, "vm_memory_high_watermark_paging_ratio",
"rabbit.vm_memory_high_watermark_paging_ratio",
[{datatype, float}, {validators, ["less_than_1"]}]}.
%% Interval (in milliseconds) at which we perform the check of the memory
%% levels against the watermarks.
%%
%% {memory_monitor_interval, 2500},
{mapping, "memory_monitor_interval", "rabbit.memory_monitor_interval",
[{datatype, integer}]}.
%% Selects Erlang VM memory consumption calculation strategy.
%% Can be `allocated`, `rss` or `legacy` (aliased as `erlang`).
%%
%% {vm_memory_calculation_strategy, rss},
{mapping, "vm_memory_calculation_strategy", "rabbit.vm_memory_calculation_strategy",
[{datatype, {enum, [rss, erlang, allocated, legacy]}}]}.
%% The total memory available can be calculated from the OS resources
%% (default option) or provided as a configuration parameter
{mapping, "total_memory_available_override_value", "rabbit.total_memory_available_override_value", [
{datatype, [integer, string]}]}.
%% Set disk free limit (in bytes). Once free disk space reaches this
%% lower bound, a disk alarm will be set - see the documentation
%% listed above for more details.
%%
%% {disk_free_limit, 50000000},
%%
%% Or you can set it using memory units (same as in vm_memory_high_watermark)
%% with RabbitMQ 3.6.0+.
%% {disk_free_limit, "50MB"},
%% {disk_free_limit, "50000kB"},
%% {disk_free_limit, "2GB"},
%% Alternatively, we can set a limit relative to total available RAM.
%%
%% Values lower than 1.0 can be dangerous and should be used carefully.
%% {disk_free_limit, {mem_relative, 2.0}},
{mapping, "disk_free_limit.relative", "rabbit.disk_free_limit", [
{datatype, float}]}.
{mapping, "disk_free_limit.absolute", "rabbit.disk_free_limit", [
{datatype, [integer, string]}]}.
{translation, "rabbit.disk_free_limit",
fun(Conf) ->
Settings = cuttlefish_variable:filter_by_prefix("disk_free_limit", Conf),
Absolute = proplists:get_value(["disk_free_limit", "absolute"], Settings),
Relative = proplists:get_value(["disk_free_limit", "relative"], Settings),
case {Absolute, Relative} of
{undefined, undefined} -> cuttlefish:invalid("No disk limit defined");
{_, undefined} -> Absolute;
_ -> {mem_relative, Relative}
end
end}.
%%
%% Clustering
%% =====================
%%
%% How to respond to cluster partitions.
%% See https://www.rabbitmq.com/partitions.html for further details.
%%
%% {cluster_partition_handling, ignore},
{mapping, "cluster_partition_handling", "rabbit.cluster_partition_handling",
[{datatype, {enum, [ignore, pause_minority, autoheal, pause_if_all_down]}}]}.
{mapping, "cluster_partition_handling.pause_if_all_down.recover",
"rabbit.cluster_partition_handling",
[{datatype, {enum, [ignore, autoheal]}}]}.
{mapping, "cluster_partition_handling.pause_if_all_down.nodes.$name",
"rabbit.cluster_partition_handling",
[{datatype, atom}]}.
{translation, "rabbit.cluster_partition_handling",
fun(Conf) ->
case cuttlefish:conf_get("cluster_partition_handling", Conf) of
pause_if_all_down ->
PauseIfAllDownNodes = cuttlefish_variable:filter_by_prefix(
"cluster_partition_handling.pause_if_all_down.nodes",
Conf),
case PauseIfAllDownNodes of
[] ->
cuttlefish:invalid("Nodes required for pause_if_all_down");
_ ->
Nodes = [ V || {K,V} <- PauseIfAllDownNodes ],
PauseIfAllDownRecover = cuttlefish:conf_get(
"cluster_partition_handling.pause_if_all_down.recover",
Conf),
case PauseIfAllDownRecover of
Recover when Recover == ignore; Recover == autoheal ->
{pause_if_all_down, Nodes, Recover};
Invalid ->
cuttlefish:invalid("Recover strategy required for pause_if_all_down")
end
end;
Other -> Other
end
end}.
%% Number of delegate processes to use for intra-cluster
%% communication. On a machine which has a very large number of cores
%% and is also part of a cluster, you may wish to increase this value.
%%
{mapping, "delegate_count", "rabbit.delegate_count", [
{datatype, integer}, {validators, ["non_negative_integer"]}
]}.
%% Mirror sync batch size, in messages. Increasing this will speed
%% up syncing but total batch size in bytes must not exceed 2 GiB.
%% Available in RabbitMQ 3.6.0 or later.
%%
%% {mirroring_sync_batch_size, 4096},
{mapping, "mirroring_sync_batch_size", "rabbit.mirroring_sync_batch_size",
[{datatype, bytesize}, {validators, ["size_less_than_2G"]}]}.
%% Peer discovery backend used by cluster formation.
%%
{mapping, "cluster_formation.peer_discovery_backend", "rabbit.cluster_formation.peer_discovery_backend", [
{datatype, atom}
]}.
{translation, "rabbit.cluster_formation.peer_discovery_backend",
fun(Conf) ->
case cuttlefish:conf_get("cluster_formation.peer_discovery_backend", Conf, rabbit_peer_discovery_classic_config) of
classic_config -> rabbit_peer_discovery_classic_config;
classic -> rabbit_peer_discovery_classic_config;
config -> rabbit_peer_discovery_classic_config;
dns -> rabbit_peer_discovery_dns;
aws -> rabbit_peer_discovery_aws;
consul -> rabbit_peer_discovery_consul;
etcd -> rabbit_peer_discovery_etcd;
kubernetes -> rabbit_peer_discovery_k8s;
k8s -> rabbit_peer_discovery_k8s;
Module -> Module
end
end}.
%% Own node type, used by cluster formation.
%%
{mapping, "cluster_formation.node_type", "rabbit.cluster_formation.node_type", [
{datatype, {enum, [disc, disk, ram]}}
]}.
{translation, "rabbit.cluster_formation.node_type",
fun(Conf) ->
%% if peer discovery backend isn't configured, don't generate
%% node type
case cuttlefish:conf_get("cluster_formation.peer_discovery_backend", Conf, undefined) of
undefined -> cuttlefish:unset();
_Backend ->
case cuttlefish:conf_get("cluster_formation.node_type", Conf) of
disc -> disc;
%% always cast to `disc`
disk -> disc;
ram -> ram;
_Other -> disc
end
end
end}.
%% Cluster formation: Randomized startup delay
{mapping, "cluster_formation.randomized_startup_delay_range.min", "rabbit.cluster_formation.randomized_startup_delay_range",
[{datatype, integer}]}.
{mapping, "cluster_formation.randomized_startup_delay_range.max", "rabbit.cluster_formation.randomized_startup_delay_range",
[{datatype, integer}]}.
{translation, "rabbit.cluster_formation.randomized_startup_delay_range",
fun(Conf) ->
Min = cuttlefish:conf_get("cluster_formation.randomized_startup_delay_range.min", Conf, undefined),
Max = cuttlefish:conf_get("cluster_formation.randomized_startup_delay_range.max", Conf, undefined),
case {Min, Max} of
{undefined, undefined} ->
cuttlefish:unset();
{undefined, Max} ->
%% fallback default
{5, Max};
{Min, undefined} ->
%% fallback default
{Min, 60};
{Min, Max} ->
{Min, Max}
end
end}.
%% Cluster formation: discovery failure retries
{mapping, "cluster_formation.lock_retry_limit", "rabbit.cluster_formation.lock_retry_limit",
[
{datatype, integer},
{validators, ["non_zero_positive_integer"]}
]}.
{mapping, "cluster_formation.lock_retry_timeout", "rabbit.cluster_formation.lock_retry_timeout",
[
{datatype, integer},
{validators, ["non_zero_positive_integer"]}
]}.
{mapping, "cluster_formation.discovery_retry_limit", "rabbit.cluster_formation.discovery_retry_limit",
[
{datatype, integer},
{validators, ["non_zero_positive_integer"]}
]}.
{mapping, "cluster_formation.discovery_retry_interval", "rabbit.cluster_formation.discovery_retry_interval",
[
{datatype, integer},
{validators, ["non_zero_positive_integer"]}
]}.
%% Classic config-driven peer discovery backend.
%%
%% Make clustering happen *automatically* at startup - only applied
%% to nodes that have just been reset or started for the first time.
%% See https://www.rabbitmq.com/clustering.html#auto-config for
%% further details.
%%
%% {cluster_nodes, {['rabbit@my.host.com'], disc}},
{mapping, "cluster_formation.classic_config.nodes.$node", "rabbit.cluster_nodes",
[{datatype, atom}]}.
{translation, "rabbit.cluster_nodes",
fun(Conf) ->
Nodes = [V || {_, V} <- cuttlefish_variable:filter_by_prefix("cluster_formation.classic_config.nodes", Conf)],
case Nodes of
[] -> cuttlefish:unset();
Other ->
case cuttlefish:conf_get("cluster_formation.node_type", Conf, disc) of
disc -> {Other, disc};
%% Always cast to `disc`
disk -> {Other, disc};
ram -> {Other, ram}
end
end
end}.
%% DNS (A records and reverse lookups)-based peer discovery.
%%
{mapping, "cluster_formation.dns.hostname", "rabbit.cluster_formation.peer_discovery_dns.hostname",
[{datatype, string}]}.
{translation, "rabbit.cluster_formation.peer_discovery_dns.hostname",
fun(Conf) ->
case cuttlefish:conf_get("cluster_formation.dns.hostname", Conf, undefined) of
undefined -> cuttlefish:unset();
Value -> list_to_binary(Value)
end
end}.
%% Interval (in milliseconds) at which we send keepalive messages
%% to other cluster members. Note that this is not the same thing
%% as net_ticktime; missed keepalive messages will not cause nodes
%% to be considered down.
%%
%% {cluster_keepalive_interval, 10000},
{mapping, "cluster_keepalive_interval", "rabbit.cluster_keepalive_interval",
[{datatype, integer}]}.
%% Queue master locator
%%
{mapping, "queue_master_locator", "rabbit.queue_master_locator",
[{datatype, string}]}.
{translation, "rabbit.queue_master_locator",
fun(Conf) ->
list_to_binary(cuttlefish:conf_get("queue_master_locator", Conf))
end}.
%%
%% Statistics Collection
%% =====================
%%
%% Set (internal) statistics collection granularity.
%%
%% {collect_statistics, none},
{mapping, "collect_statistics", "rabbit.collect_statistics",
[{datatype, {enum, [none, coarse, fine]}}]}.
%% Statistics collection interval (in milliseconds). Increasing
%% this will reduce the load on management database.
%%
%% {collect_statistics_interval, 5000},
{mapping, "collect_statistics_interval", "rabbit.collect_statistics_interval",
[{datatype, integer}]}.
%%
%% Misc/Advanced Options
%% =====================
%%
%% NB: Change these only if you understand what you are doing!
%%
%% Explicitly enable/disable hipe compilation.
%%
%% {hipe_compile, true},
{mapping, "hipe_compile", "rabbit.hipe_compile",
[{datatype, {enum, [true, false]}}]}.
%% Timeout used when waiting for Mnesia tables in a cluster to
%% become available.
%%
%% {mnesia_table_loading_retry_timeout, 30000},
{mapping, "mnesia_table_loading_retry_timeout", "rabbit.mnesia_table_loading_retry_timeout",
[{datatype, integer}]}.
%% Retries when waiting for Mnesia tables in the cluster startup. Note that
%% this setting is not applied to Mnesia upgrades or node deletions.
%%
%% {mnesia_table_loading_retry_limit, 10},
{mapping, "mnesia_table_loading_retry_limit", "rabbit.mnesia_table_loading_retry_limit",
[{datatype, integer}]}.
%% Size in bytes below which to embed messages in the queue index. See
%% https://www.rabbitmq.com/persistence-conf.html
%%
%% {queue_index_embed_msgs_below, 4096}
{mapping, "queue_index_embed_msgs_below", "rabbit.queue_index_embed_msgs_below",
[{datatype, bytesize}]}.
%% Whether or not to enable background GC.
%%
%% {background_gc_enabled, true}
{mapping, "background_gc_enabled", "rabbit.background_gc_enabled",
[{datatype, {enum, [true, false]}}]}.
%% Interval (in milliseconds) at which we run background GC.
%%
%% {background_gc_target_interval, 60000}
{mapping, "background_gc_target_interval", "rabbit.background_gc_target_interval",
[{datatype, integer}]}.
%% Whether or not to enable proxy protocol support.
%%
%% {proxy_protocol, false}
{mapping, "proxy_protocol", "rabbit.proxy_protocol",
[{datatype, {enum, [true, false]}}]}.
%% Whether to stop the rabbit application if a vhost has
%% to terminate for any reason.
{mapping, "vhost_restart_strategy", "rabbit.vhost_restart_strategy",
[{datatype, {enum, [stop_node, continue, transient, persistent]}}]}.
% ==========================
% Lager section
% ==========================
{mapping, "log.dir", "lager.log_root", [
{datatype, string},
{validators, ["dir_writable"]}]}.
{mapping, "log.console", "rabbit.log.console.enabled", [
{datatype, {enum, [true, false]}}
]}.
{mapping, "log.console.level", "rabbit.log.console.level", [
{datatype, {enum, [debug, info, notice, warning, error, critical, alert, emergency, none]}}
]}.
{mapping, "log.exchange", "rabbit.log.exchange.enabled", [
{datatype, {enum, [true, false]}}
]}.
{mapping, "log.exchange.level", "rabbit.log.exchange.level", [
{datatype, {enum, [debug, info, notice, warning, error, critical, alert, emergency, none]}}
]}.
{mapping, "log.syslog", "rabbit.log.syslog.enabled", [
{datatype, {enum, [true, false]}}
]}.
{mapping, "log.syslog.level", "rabbit.log.syslog.level", [
{datatype, {enum, [debug, info, notice, warning, error, critical, alert, emergency, none]}}
]}.
{mapping, "log.syslog.identity", "syslog.app_name", [
{datatype, string}
]}.
{mapping, "log.syslog.facility", "syslog.facility", [
{datatype, {enum, [kern, kernel, user, mail, daemon, auth, syslog, lpr,
news, uucp, cron, authpriv, ftp, ntp, audit, alert,
clock, local0, local1, local2, local3, local4,
local5, local6, local7]}}
]}.
{mapping, "log.syslog.multiline_mode", "syslog.multiline_mode", [
{datatype, {enum, [true, false]}}
]}.
{mapping, "log.syslog.ip", "syslog.dest_host", [
{datatype, string},
{validators, ["is_ip"]}
]}.
{mapping, "log.syslog.host", "syslog.dest_host", [
{datatype, string}
]}.
{translation, "syslog.dest_host",
fun(Conf) ->
case cuttlefish:conf_get("log.syslog", Conf) of
true ->
case cuttlefish:conf_get("log.syslog.ip", Conf, undefined) of
undefined ->
% If log.syslog.ip is not set, then this must be set
cuttlefish:conf_get("log.syslog.host", Conf);
IpAddr ->
IpAddr
end;
_ ->
cuttlefish:invalid("log.syslog must be set to true to set log.syslog.host or log.syslog.ip")
end
end}.
{mapping, "log.syslog.port", "syslog.dest_port", [
{datatype, integer}
]}.
{mapping, "log.syslog.transport", "syslog.protocol", [
{datatype, {enum, [udp, tcp, tls, ssl]}}
]}.
{mapping, "log.syslog.protocol", "syslog.protocol", [
{datatype, {enum, [rfc3164, rfc5424]}}
]}.
{mapping, "log.syslog.ssl_options.verify", "syslog.protocol", [
{datatype, {enum, [verify_peer, verify_none]}}]}.
{mapping, "log.syslog.ssl_options.fail_if_no_peer_cert", "syslog.protocol", [
{datatype, {enum, [true, false]}}]}.
{mapping, "log.syslog.ssl_options.cacertfile", "syslog.protocol",
[{datatype, string}, {validators, ["file_accessible"]}]}.
{mapping, "log.syslog.ssl_options.certfile", "syslog.protocol",
[{datatype, string}, {validators, ["file_accessible"]}]}.
{mapping, "log.syslog.ssl_options.cacerts.$name", "syslog.protocol",
[{datatype, string}]}.
{mapping, "log.syslog.ssl_options.cert", "syslog.protocol",
[{datatype, string}]}.
{mapping, "log.syslog.ssl_options.client_renegotiation", "syslog.protocol",
[{datatype, {enum, [true, false]}}]}.
{mapping, "log.syslog.ssl_options.crl_check", "syslog.protocol",
[{datatype, [{enum, [true, false, peer, best_effort]}]}]}.
{mapping, "log.syslog.ssl_options.depth", "syslog.protocol",
[{datatype, integer}, {validators, ["byte"]}]}.
{mapping, "log.syslog.ssl_options.dh", "syslog.protocol",
[{datatype, string}]}.
{mapping, "log.syslog.ssl_options.dhfile", "syslog.protocol",
[{datatype, string}, {validators, ["file_accessible"]}]}.
{mapping, "log.syslog.ssl_options.honor_cipher_order", "syslog.protocol",
[{datatype, {enum, [true, false]}}]}.
{mapping, "log.syslog.ssl_options.honor_ecc_order", "syslog.protocol",
[{datatype, {enum, [true, false]}}]}.
{mapping, "log.syslog.ssl_options.key.RSAPrivateKey", "syslog.protocol",
[{datatype, string}]}.
{mapping, "log.syslog.ssl_options.key.DSAPrivateKey", "syslog.protocol",
[{datatype, string}]}.
{mapping, "log.syslog.ssl_options.key.PrivateKeyInfo", "syslog.protocol",
[{datatype, string}]}.
{mapping, "log.syslog.ssl_options.keyfile", "syslog.protocol",
[{datatype, string}, {validators, ["file_accessible"]}]}.
{mapping, "log.syslog.ssl_options.log_alert", "syslog.protocol",
[{datatype, {enum, [true, false]}}]}.
{mapping, "log.syslog.ssl_options.password", "syslog.protocol",
[{datatype, string}]}.
{mapping, "log.syslog.ssl_options.psk_identity", "syslog.protocol",
[{datatype, string}]}.
{mapping, "log.syslog.ssl_options.reuse_sessions", "syslog.protocol",
[{datatype, {enum, [true, false]}}]}.
{mapping, "log.syslog.ssl_options.secure_renegotiate", "syslog.protocol",
[{datatype, {enum, [true, false]}}]}.
{mapping, "log.syslog.ssl_options.versions.$version", "syslog.protocol",
[{datatype, atom}]}.
{translation, "syslog.protocol",
fun(Conf) ->
ParseSslOptions = fun() ->
RawSettings = [
{verify, cuttlefish:conf_get("log.syslog.ssl_options.verify", Conf, undefined)},
{fail_if_no_peer_cert, cuttlefish:conf_get("log.syslog.ssl_options.fail_if_no_peer_cert", Conf, undefined)},
{cacertfile, cuttlefish:conf_get("log.syslog.ssl_options.cacertfile", Conf, undefined)},
{certfile, cuttlefish:conf_get("log.syslog.ssl_options.certfile", Conf, undefined)},
{cert, cuttlefish:conf_get("log.syslog.ssl_options.cert", Conf, undefined)},
{client_renegotiation, cuttlefish:conf_get("log.syslog.ssl_options.client_renegotiation", Conf, undefined)},
{crl_check, cuttlefish:conf_get("log.syslog.ssl_options.crl_check", Conf, undefined)},
{depth, cuttlefish:conf_get("log.syslog.ssl_options.depth", Conf, undefined)},
{dh, cuttlefish:conf_get("log.syslog.ssl_options.dh", Conf, undefined)},
{dhfile, cuttlefish:conf_get("log.syslog.ssl_options.dhfile", Conf, undefined)},
{honor_cipher_order, cuttlefish:conf_get("log.syslog.ssl_options.honor_cipher_order", Conf, undefined)},
{honor_ecc_order, cuttlefish:conf_get("log.syslog.ssl_options.honor_ecc_order", Conf, undefined)},
{keyfile, cuttlefish:conf_get("log.syslog.ssl_options.keyfile", Conf, undefined)},
{log_alert, cuttlefish:conf_get("log.syslog.ssl_options.log_alert", Conf, undefined)},
{password, cuttlefish:conf_get("log.syslog.ssl_options.password", Conf, undefined)},
{psk_identity, cuttlefish:conf_get("log.syslog.ssl_options.psk_identity", Conf, undefined)},
{reuse_sessions, cuttlefish:conf_get("log.syslog.ssl_options.reuse_sessions", Conf, undefined)},
{secure_renegotiate, cuttlefish:conf_get("log.syslog.ssl_options.secure_renegotiate", Conf, undefined)}
],
DefinedSettings = [{K, V} || {K, V} <- RawSettings, V =/= undefined],
lists:map(
fun({K, Val}) when K == dh; K == cert -> {K, list_to_binary(Val)};
({K, Val}) -> {K, Val}
end,
DefinedSettings) ++
[ {K, V}
|| {K, V} <-
[{cacerts, [ list_to_binary(V) || {_, V} <- cuttlefish_variable:filter_by_prefix("log.syslog.ssl_options.cacerts", Conf)]},
{versions, [ V || {_, V} <- cuttlefish_variable:filter_by_prefix("log.syslog.ssl_options.versions", Conf) ]},
{key, case cuttlefish_variable:filter_by_prefix("log.syslog.ssl_options.key", Conf) of
[{[_,_,Key], Val}|_] -> {list_to_atom(Key), list_to_binary(Val)};
_ -> undefined
end}],
V =/= undefined,
V =/= []]
end,
Proto = cuttlefish:conf_get("log.syslog.protocol", Conf, undefined),
Transport = cuttlefish:conf_get("log.syslog.transport", Conf, udp),
case Transport of
TLS when TLS == tls; TLS == ssl ->
case Proto of
rfc3164 ->
cuttlefish:invalid("Syslog protocol rfc3164 is not compatible with TLS");
_ ->
{rfc5424, tls, ParseSslOptions()}
end;
_ when Transport == udp; Transport == tcp ->
case Proto of
undefined -> {rfc3164, Transport};
_ -> {Proto, Transport}
end;
_ -> cuttlefish:invalid("Invalid syslog transport ~p~n", [Transport])
end
end}.
{mapping, "log.file", "rabbit.log.file.file", [
{datatype, [{enum, [false]}, string]}
]}.
{mapping, "log.file.level", "rabbit.log.file.level", [
{datatype,
{enum, ['=debug', debug,
info, '!=info',
notice, '<=notice',
'<warning', warning,
error,
critical,
alert,
emergency,
none]}}
]}.
{mapping, "log.file.rotation.date", "rabbit.log.file.date", [
{datatype, string}
]}.
{mapping, "log.file.rotation.size", "rabbit.log.file.size", [
{datatype, integer}
]}.
{mapping, "log.file.rotation.count", "rabbit.log.file.count", [
{datatype, integer}
]}.
%% Log categories
{mapping, "log.connection.level", "rabbit.log.categories.connection.level", [
{datatype, {enum, [debug, info, notice, warning, error, critical, alert, emergency, none]}}
]}.
{mapping, "log.connection.file", "rabbit.log.categories.connection.file", [
{datatype, string}
]}.
{mapping, "log.channel.level", "rabbit.log.categories.channel.level", [
{datatype, {enum, [debug, info, notice, warning, error, critical, alert, emergency, none]}}
]}.
{mapping, "log.channel.file", "rabbit.log.categories.channel.file", [
{datatype, string}
]}.
{mapping, "log.mirroring.level", "rabbit.log.categories.mirroring.level", [
{datatype, {enum, [debug, info, notice, warning, error, critical, alert, emergency, none]}}
]}.
{mapping, "log.mirroring.file", "rabbit.log.categories.mirroring.file", [
{datatype, string}
]}.
{mapping, "log.queue.level", "rabbit.log.categories.queue.level", [
{datatype, {enum, [debug, info, notice, warning, error, critical, alert, emergency, none]}}
]}.
{mapping, "log.queue.file", "rabbit.log.categories.queue.file", [
{datatype, string}
]}.
{mapping, "log.federation.level", "rabbit.log.categories.federation.level", [
{datatype, {enum, [debug, info, notice, warning, error, critical, alert, emergency, none]}}
]}.
{mapping, "log.federation.file", "rabbit.log.categories.federation.file", [
{datatype, string}
]}.
{mapping, "log.upgrade.level", "rabbit.log.categories.upgrade.level", [
{datatype, {enum, [debug, info, notice, warning, error, critical, alert, emergency, none]}}
]}.
{mapping, "log.upgrade.file", "rabbit.log.categories.upgrade.file", [
{datatype, string}
]}.
{mapping, "log.default.level", "rabbit.log.categories.default.level", [
{datatype, {enum, [debug, info, notice, warning, error, critical, alert, emergency, none]}}
]}.
% ==========================
% Kernel section
% ==========================
{mapping, "net_ticktime", "kernel.net_ticktime",[
{datatype, [integer]},
{validators, ["non_zero_positive_integer"]}
]}.
% ==========================
% sysmon_handler section
% ==========================
%% @doc The threshold at which to warn about the number of processes
%% that are overly busy. Processes with large heaps or that take a
%% long time to garbage collect will count toward this threshold.
{mapping, "sysmon_handler.thresholds.busy_processes", "sysmon_handler.process_limit", [
{datatype, integer},
hidden
]}.
{translation, "sysmon_handler.process_limit",
fun(Conf) ->
case cuttlefish:conf_get("sysmon_handler.thresholds.busy_processes", Conf, undefined) of
undefined ->
cuttlefish:unset();
Int when is_integer(Int) ->
Int;
_ ->
cuttlefish:invalid("should be a non-negative integer")
end
end
}.
%% @doc The threshold at which to warn about the number of ports that
%% are overly busy. Ports with full input buffers count toward this
%% threshold.
{mapping, "sysmon_handler.thresholds.busy_ports", "sysmon_handler.port_limit", [
{datatype, integer},
hidden
]}.
{translation, "sysmon_handler.port_limit",
fun(Conf) ->
case cuttlefish:conf_get("sysmon_handler.thresholds.busy_ports", Conf, undefined) of
undefined ->
cuttlefish:unset();
Int when is_integer(Int) ->
Int;
_ ->
cuttlefish:invalid("should be a non-negative integer")
end
end
}.
%% @doc A process will become busy when it exceeds this amount of time
%% doing garbage collection.
%% @see sysmon_handler.thresholds.busy_processes
{mapping, "sysmon_handler.triggers.process.garbage_collection", "sysmon_handler.gc_ms_limit", [
{datatype, [{atom, off},
{duration, ms}]},
hidden
]}.
{translation, "sysmon_handler.gc_ms_limit",
fun(Conf) ->
case cuttlefish:conf_get("sysmon_handler.triggers.process.garbage_collection", Conf, undefined) of
undefined ->
cuttlefish:unset();
off ->
0;
Int when is_integer(Int) ->
Int;
_ ->
cuttlefish:invalid("should be a non-negative integer")
end
end
}.
%% @doc A process will become busy when it exceeds this amount of time
%% during a single process scheduling & execution cycle.
{mapping, "sysmon_handler.triggers.process.long_scheduled_execution", "sysmon_handler.schedule_ms_limit", [
{datatype, [{atom, off},
{duration, ms}]},
hidden
]}.
{translation, "sysmon_handler.schedule_ms_limit",
fun(Conf) ->
case cuttlefish:conf_get("sysmon_handler.triggers.process.long_scheduled_execution", Conf, undefined) of
undefined ->
cuttlefish:unset();
off ->
0;
Int when is_integer(Int) ->
Int;
_ ->
cuttlefish:invalid("should be a non-negative integer")
end
end
}.
%% @doc A process will become busy when its heap exceeds this size.
%% @see sysmon_handler.thresholds.busy_processes
{mapping, "sysmon_handler.triggers.process.heap_size", "sysmon_handler.heap_word_limit", [
{datatype, [{atom, off},
bytesize]},
hidden
]}.
{translation, "sysmon_handler.heap_word_limit",
fun(Conf) ->
case cuttlefish:conf_get("sysmon_handler.triggers.process.heap_size", Conf, undefined) of
undefined ->
cuttlefish:unset();
off ->
0;
Bytes when is_integer(Bytes) ->
WordSize = erlang:system_info(wordsize),
Bytes div WordSize;
_ ->
cuttlefish:invalid("should be a non-negative integer")
end
end
}.
%% @doc Whether ports with full input buffers will be counted as
%% busy. Ports can represent open files or network sockets.
%% @see sysmon_handler.thresholds.busy_ports
{mapping, "sysmon_handler.triggers.port", "sysmon_handler.busy_port", [
{datatype, flag},
hidden
]}.
{translation, "sysmon_handler.busy_port",
fun(Conf) ->
case cuttlefish:conf_get("sysmon_handler.triggers.port", Conf, undefined) of
undefined ->
cuttlefish:unset();
Val -> Val
end
end
}.
%% @doc Whether distribution ports with full input buffers will be
%% counted as busy. Distribution ports connect Erlang nodes within a
%% single cluster.
%% @see sysmon_handler.thresholds.busy_ports
{mapping, "sysmon_handler.triggers.distribution_port", "sysmon_handler.busy_dist_port", [
{datatype, flag},
hidden
]}.
{translation, "sysmon_handler.busy_dist_port",
fun(Conf) ->
case cuttlefish:conf_get("sysmon_handler.triggers.distribution_port", Conf, undefined) of
undefined ->
cuttlefish:unset();
Val -> Val
end
end
}.
% ===============================
% Validators
% ===============================
{validator, "size_less_than_2G", "Byte size should be less than 2G and greater than 0",
fun(Size) when is_integer(Size) ->
Size > 0 andalso Size < 2147483648
end}.
{validator, "less_than_1", "Flooat is not beetween 0 and 1",
fun(Float) when is_float(Float) ->
Float > 0 andalso Float < 1
end}.
{validator, "port", "Invalid port number",
fun(Port) when is_integer(Port) ->
Port > 0 andalso Port < 65535
end}.
{validator, "byte", "Integer is not 0<i<255",
fun(Int) when is_integer(Int) ->
Int > 0 andalso Int < 255
end}.
{validator, "dir_writable", "Cannot create file in dir",
fun(Dir) ->
TestFile = filename:join(Dir, "test_file"),
file:delete(TestFile),
Res = ok == file:write_file(TestFile, <<"test">>),
file:delete(TestFile),
Res
end}.
{validator, "file_accessible", "file doesn/t exist or isn't readable",
fun(File) ->
ReadFile = file:read_file_info(File),
element(1, ReadFile) == ok
end}.
{validator, "is_ip", "string is a valid IP address",
fun(IpStr) ->
Res = inet:parse_address(IpStr),
element(1, Res) == ok
end}.
{validator, "non_negative_integer", "number should be greater or equal to zero",
fun(Int) when is_integer(Int) ->
Int >= 0
end}.
{validator, "non_zero_positive_integer", "number should be greater or equal to one",
fun(Int) when is_integer(Int) ->
Int >= 1
end}.
%% ----------------------------------------------------------------------------
%% RabbitMQ Management Plugin
%%
%% See https://www.rabbitmq.com/management.html for details
%% ----------------------------------------------------------------------------
%% Load definitions from a JSON file or directory of files. See
%% https://www.rabbitmq.com/management.html#load-definitions
%%
%% {load_definitions, "/path/to/schema.json"},
%% {load_definitions, "/path/to/schemas"},
{mapping, "management.load_definitions", "rabbitmq_management.load_definitions",
[{datatype, string},
{validators, ["file_accessible"]}]}.
%% Log all requests to the management HTTP API to a file.
%%
%% {http_log_dir, "/path/to/access.log"},
{mapping, "management.http_log_dir", "rabbitmq_management.http_log_dir",
[{datatype, string}]}.
%% HTTP (TCP) listener options ========================================================
%% HTTP listener consistent with Web STOMP and Web MQTT.
%%
%% {tcp_config, [{port, 15672},
%% {ip, "127.0.0.1"}]}
{mapping, "management.tcp.port", "rabbitmq_management.tcp_config.port",
[{datatype, integer}]}.
{mapping, "management.tcp.ip", "rabbitmq_management.tcp_config.ip",
[{datatype, string},
{validators, ["is_ip"]}]}.
{mapping, "management.tcp.compress", "rabbitmq_management.tcp_config.cowboy_opts.compress",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.tcp.idle_timeout", "rabbitmq_management.tcp_config.cowboy_opts.idle_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.tcp.inactivity_timeout", "rabbitmq_management.tcp_config.cowboy_opts.inactivity_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.tcp.request_timeout", "rabbitmq_management.tcp_config.cowboy_opts.request_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.tcp.shutdown_timeout", "rabbitmq_management.tcp_config.cowboy_opts.shutdown_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.tcp.max_keepalive", "rabbitmq_management.tcp_config.cowboy_opts.max_keepalive",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
%% HTTPS (TLS) listener options ========================================================
%% HTTPS listener consistent with Web STOMP and Web MQTT.
%%
%% {ssl_config, [{port, 15671},
%% {ip, "127.0.0.1"},
%% {cacertfile, "/path/to/cacert.pem"},
%% {certfile, "/path/to/cert.pem"},
%% {keyfile, "/path/to/key.pem"}]}
{mapping, "management.ssl.port", "rabbitmq_management.ssl_config.port",
[{datatype, integer}]}.
{mapping, "management.ssl.backlog", "rabbitmq_management.ssl_config.backlog",
[{datatype, integer}]}.
{mapping, "management.ssl.ip", "rabbitmq_management.ssl_config.ip",
[{datatype, string}, {validators, ["is_ip"]}]}.
{mapping, "management.ssl.certfile", "rabbitmq_management.ssl_config.certfile",
[{datatype, string}, {validators, ["file_accessible"]}]}.
{mapping, "management.ssl.keyfile", "rabbitmq_management.ssl_config.keyfile",
[{datatype, string}, {validators, ["file_accessible"]}]}.
{mapping, "management.ssl.cacertfile", "rabbitmq_management.ssl_config.cacertfile",
[{datatype, string}, {validators, ["file_accessible"]}]}.
{mapping, "management.ssl.password", "rabbitmq_management.ssl_config.password",
[{datatype, string}]}.
{mapping, "management.ssl.verify", "rabbitmq_management.ssl_config.verify", [
{datatype, {enum, [verify_peer, verify_none]}}]}.
{mapping, "management.ssl.fail_if_no_peer_cert", "rabbitmq_management.ssl_config.fail_if_no_peer_cert", [
{datatype, {enum, [true, false]}}]}.
{mapping, "management.ssl.honor_cipher_order", "rabbitmq_management.ssl_config.honor_cipher_order",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.ssl.honor_ecc_order", "rabbitmq_management.ssl_config.honor_ecc_order",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.ssl.reuse_sessions", "rabbitmq_management.ssl_config.reuse_sessions",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.ssl.secure_renegotiate", "rabbitmq_management.ssl_config.secure_renegotiate",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.ssl.client_renegotiation", "rabbitmq_management.ssl_config.client_renegotiation",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.ssl.depth", "rabbitmq_management.ssl_config.depth",
[{datatype, integer}, {validators, ["byte"]}]}.
{mapping, "management.ssl.versions.$version", "rabbitmq_management.ssl_config.versions",
[{datatype, atom}]}.
{translation, "rabbitmq_management.ssl_config.versions",
fun(Conf) ->
Settings = cuttlefish_variable:filter_by_prefix("management.ssl.versions", Conf),
[V || {_, V} <- Settings]
end}.
{mapping, "management.ssl.ciphers.$cipher", "rabbitmq_management.ssl_config.ciphers",
[{datatype, string}]}.
{translation, "rabbitmq_management.ssl_config.ciphers",
fun(Conf) ->
Settings = cuttlefish_variable:filter_by_prefix("management.ssl.ciphers", Conf),
lists:reverse([V || {_, V} <- Settings])
end}.
{mapping, "management.ssl.compress", "rabbitmq_management.ssl_config.cowboy_opts.compress",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.ssl.idle_timeout", "rabbitmq_management.ssl_config.cowboy_opts.idle_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.ssl.inactivity_timeout", "rabbitmq_management.ssl_config.cowboy_opts.inactivity_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.ssl.request_timeout", "rabbitmq_management.ssl_config.cowboy_opts.request_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.ssl.shutdown_timeout", "rabbitmq_management.ssl_config.cowboy_opts.shutdown_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.ssl.max_keepalive", "rabbitmq_management.ssl_config.cowboy_opts.max_keepalive",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
%% Legacy listener options ========================================================
%% Legacy (pre-3.7.9) TCP listener format.
%%
%% {listener, [{port, 12345},
%% {ip, "127.0.0.1"},
%% {ssl, true},
%% {ssl_opts, [{cacertfile, "/path/to/cacert.pem"},
%% {certfile, "/path/to/cert.pem"},
%% {keyfile, "/path/to/key.pem"}]}]},
{mapping, "management.listener.port", "rabbitmq_management.listener.port",
[{datatype, integer}]}.
{mapping, "management.listener.ip", "rabbitmq_management.listener.ip",
[{datatype, string},
{validators, ["is_ip"]}]}.
{mapping, "management.listener.ssl", "rabbitmq_management.listener.ssl",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.listener.server.compress", "rabbitmq_management.listener.cowboy_opts.compress",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.listener.server.idle_timeout", "rabbitmq_management.listener.cowboy_opts.idle_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.listener.server.inactivity_timeout", "rabbitmq_management.listener.cowboy_opts.inactivity_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.listener.server.request_timeout", "rabbitmq_management.listener.cowboy_opts.request_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.listener.server.shutdown_timeout", "rabbitmq_management.listener.cowboy_opts.shutdown_timeout",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
{mapping, "management.listener.server.max_keepalive", "rabbitmq_management.listener.cowboy_opts.max_keepalive",
[{datatype, integer}, {validators, ["non_negative_integer"]}]}.
%% Legacy HTTPS listener options ========================================================
{mapping, "management.listener.ssl_opts", "rabbitmq_management.listener.ssl_opts", [
{datatype, {enum, [none]}}
]}.
{translation, "rabbitmq_management.listener.ssl_opts",
fun(Conf) ->
case cuttlefish:conf_get("management.listener.ssl_opts", Conf, undefined) of
none -> [];
_ -> cuttlefish:invalid("Invalid management.listener.ssl_opts")
end
end}.
{mapping, "management.listener.ssl_opts.verify", "rabbitmq_management.listener.ssl_opts.verify", [
{datatype, {enum, [verify_peer, verify_none]}}]}.
{mapping, "management.listener.ssl_opts.fail_if_no_peer_cert", "rabbitmq_management.listener.ssl_opts.fail_if_no_peer_cert", [
{datatype, {enum, [true, false]}}]}.
{mapping, "management.listener.ssl_opts.cacertfile", "rabbitmq_management.listener.ssl_opts.cacertfile",
[{datatype, string}, {validators, ["file_accessible"]}]}.
{mapping, "management.listener.ssl_opts.certfile", "rabbitmq_management.listener.ssl_opts.certfile",
[{datatype, string}, {validators, ["file_accessible"]}]}.
{mapping, "management.listener.ssl_opts.cacerts.$name", "rabbitmq_management.listener.ssl_opts.cacerts",
[{datatype, string}]}.
{translation, "rabbitmq_management.listener.ssl_opts.cacerts",
fun(Conf) ->
Settings = cuttlefish_variable:filter_by_prefix("management.listener.ssl_opts.cacerts", Conf),
[ list_to_binary(V) || {_, V} <- Settings ]
end}.
{mapping, "management.listener.ssl_opts.honor_cipher_order", "rabbitmq_management.listener.ssl_opts.honor_cipher_order",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.listener.ssl_opts.honor_ecc_order", "rabbitmq_management.listener.ssl_opts.honor_ecc_order",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.listener.ssl_opts.reuse_sessions", "rabbitmq_management.listener.ssl_opts.reuse_sessions",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.listener.ssl_opts.secure_renegotiate", "rabbitmq_management.listener.ssl_opts.secure_renegotiate",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.listener.ssl_opts.client_renegotiation", "rabbitmq_management.listener.ssl_opts.client_renegotiation",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.listener.ssl_opts.versions.$version", "rabbitmq_management.listener.ssl_opts.versions",
[{datatype, atom}]}.
{translation, "rabbitmq_management.listener.ssl_opts.versions",
fun(Conf) ->
Settings = cuttlefish_variable:filter_by_prefix("management.listener.ssl_opts.versions", Conf),
[ V || {_, V} <- Settings ]
end}.
{mapping, "management.listener.ssl_opts.cert", "rabbitmq_management.listener.ssl_opts.cert",
[{datatype, string}]}.
{translation, "rabbitmq_management.listener.ssl_opts.cert",
fun(Conf) ->
list_to_binary(cuttlefish:conf_get("management.listener.ssl_opts.cert", Conf))
end}.
{mapping, "management.listener.ssl_opts.crl_check", "rabbitmq_management.listener.ssl_opts.crl_check",
[{datatype, [{enum, [true, false, peer, best_effort]}]}]}.
{mapping, "management.listener.ssl_opts.depth", "rabbitmq_management.listener.ssl_opts.depth",
[{datatype, integer}, {validators, ["byte"]}]}.
{mapping, "management.listener.ssl_opts.dh", "rabbitmq_management.listener.ssl_opts.dh",
[{datatype, string}]}.
{translation, "rabbitmq_management.listener.ssl_opts.dh",
fun(Conf) ->
list_to_binary(cuttlefish:conf_get("management.listener.ssl_opts.dh", Conf))
end}.
{mapping, "management.listener.ssl_opts.dhfile", "rabbitmq_management.listener.ssl_opts.dhfile",
[{datatype, string}, {validators, ["file_accessible"]}]}.
{mapping, "management.listener.ssl_opts.key.RSAPrivateKey", "rabbitmq_management.listener.ssl_opts.key",
[{datatype, string}]}.
{mapping, "management.listener.ssl_opts.key.DSAPrivateKey", "rabbitmq_management.listener.ssl_opts.key",
[{datatype, string}]}.
{mapping, "management.listener.ssl_opts.key.PrivateKeyInfo", "rabbitmq_management.listener.ssl_opts.key",
[{datatype, string}]}.
{translation, "rabbitmq_management.listener.ssl_opts.key",
fun(Conf) ->
case cuttlefish_variable:filter_by_prefix("management.listener.ssl_opts.key", Conf) of
[{[_,_,Key], Val}|_] -> {list_to_atom(Key), list_to_binary(Val)};
_ -> undefined
end
end}.
{mapping, "management.listener.ssl_opts.keyfile", "rabbitmq_management.listener.ssl_opts.keyfile",
[{datatype, string}, {validators, ["file_accessible"]}]}.
{mapping, "management.listener.ssl_opts.log_alert", "rabbitmq_management.listener.ssl_opts.log_alert",
[{datatype, {enum, [true, false]}}]}.
{mapping, "management.listener.ssl_opts.password", "rabbitmq_management.listener.ssl_opts.password",
[{datatype, string}]}.
{mapping, "management.listener.ssl_opts.psk_identity", "rabbitmq_management.listener.ssl_opts.psk_identity",
[{datatype, string}]}.
%% A custom path prefix for all HTTP request handlers.
%%
%% {path_prefix, "/a/prefix"},
{mapping, "management.path_prefix", "rabbitmq_management.path_prefix",
[{datatype, string}]}.
%% Login session timeout in minutes
{mapping, "management.login_session_timeout", "rabbitmq_management.login_session_timeout", [
{datatype, integer}, {validators, ["non_negative_integer"]}
]}.
%% CORS
{mapping, "management.cors.allow_origins", "rabbitmq_management.cors_allow_origins", [
{datatype, {enum, [none]}}
]}.
{mapping, "management.cors.allow_origins.$name", "rabbitmq_management.cors_allow_origins", [
{datatype, string}
]}.
{translation, "rabbitmq_management.cors_allow_origins",
fun(Conf) ->
case cuttlefish:conf_get("management.cors.allow_origins", Conf, undefined) of
none -> [];
_ ->
Settings = cuttlefish_variable:filter_by_prefix("management.cors.allow_origins", Conf),
[V || {_, V} <- Settings]
end
end}.
{mapping, "management.cors.max_age", "rabbitmq_management.cors_max_age", [
{datatype, integer}, {validators, ["non_negative_integer"]}
]}.
{translation, "rabbitmq_management.cors_max_age",
fun(Conf) ->
case cuttlefish:conf_get("management.cors.max_age", Conf, undefined) of
undefined -> cuttlefish:unset();
Value -> Value
end
end}.
%% CSP (https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
{mapping, "management.csp.policy", "rabbitmq_management.content_security_policy", [
{datatype, string}
]}.
{translation, "rabbitmq_management.content_security_policy",
fun(Conf) ->
case cuttlefish:conf_get("management.csp.policy", Conf, undefined) of
undefined -> cuttlefish:unset();
Value -> Value
end
end}.
%% HSTS (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security)
{mapping, "management.hsts.policy", "rabbitmq_management.strict_transport_security", [
{datatype, string}
]}.
{translation, "rabbitmq_management.strict_transport_security",
fun(Conf) ->
case cuttlefish:conf_get("management.hsts.policy", Conf, undefined) of
undefined -> cuttlefish:unset();
Value -> Value
end
end}.
%% ===========================================================================
%% One of 'basic', 'detailed' or 'none'. See
%% https://www.rabbitmq.com/management.html#fine-stats for more details.
%% {rates_mode, basic},
{mapping, "management.rates_mode", "rabbitmq_management.rates_mode",
[{datatype, {enum, [basic, detailed, none]}}]}.
%% Configure how long aggregated data (such as message rates and queue
%% lengths) is retained. Please read the plugin's documentation in
%% https://www.rabbitmq.com/management.html#configuration for more
%% details.
%%
%% {sample_retention_policies,
%% [{global, [{60, 5}, {3600, 60}, {86400, 1200}]},
%% {basic, [{60, 5}, {3600, 60}]},
%% {detailed, [{10, 5}]}]}
% ]},
{mapping, "management.sample_retention_policies.$section.$interval",
"rabbitmq_management.sample_retention_policies",
[{datatype, integer}]}.
{translation, "rabbitmq_management.sample_retention_policies",
fun(Conf) ->
Global = cuttlefish_variable:filter_by_prefix("management.sample_retention_policies.global", Conf),
Basic = cuttlefish_variable:filter_by_prefix("management.sample_retention_policies.basic", Conf),
Detailed = cuttlefish_variable:filter_by_prefix("management.sample_retention_policies.detailed", Conf),
TranslateKey = fun("minute") -> 60;
("hour") -> 3600;
("day") -> 86400;
(Other) -> list_to_integer(Other)
end,
TranslatePolicy = fun(Section) ->
[ {TranslateKey(Key), Val} || {[_,_,_,Key], Val} <- Section ]
end,
[{global, TranslatePolicy(Global)},
{basic, TranslatePolicy(Basic)},
{detailed, TranslatePolicy(Detailed)}]
end}.
{validator, "is_dir", "is not directory",
fun(File) ->
ReadFile = file:list_dir(File),
element(1, ReadFile) == ok
end}.
|TID: N/A|2022-03-01 16:42:19.733|INFO |health-cloud-search-center-web-dev|||| main|c.u.j.c.EnableEncryptablePropertiesConfiguration:72||Bootstraping jasypt-string-boot auto configuration in context: health-cloud-search-center-1|
|TID: N/A|2022-03-01 16:42:19.737|INFO |health-cloud-search-center-web-dev|||| main|c.j.c.HealthCloudSearchWebApplication:655||The following profiles are active: dev|
|TID: N/A|2022-03-01 16:42:22.524|WARN |health-cloud-search-center-web-dev|||| main|o.s.b.a.e.EndpointId:155||Endpoint ID 'nacos-config' contains invalid characters, please migrate to a valid format.|
|TID: N/A|2022-03-01 16:42:22.532|WARN |health-cloud-search-center-web-dev|||| main|o.s.b.a.e.EndpointId:155||Endpoint ID 'nacos-discovery' contains invalid characters, please migrate to a valid format.|
|TID: N/A|2022-03-01 16:42:23.009|INFO |health-cloud-search-center-web-dev|||| main|o.s.d.r.c.RepositoryConfigurationDelegate:249||Multiple Spring Data modules found, entering strict repository configuration mode!|
|TID: N/A|2022-03-01 16:42:23.015|INFO |health-cloud-search-center-web-dev|||| main|o.s.d.r.c.RepositoryConfigurationDelegate:127||Bootstrapping Spring Data Redis repositories in DEFAULT mode.|
|TID: N/A|2022-03-01 16:42:23.173|INFO |health-cloud-search-center-web-dev|||| main|o.s.d.r.c.RepositoryConfigurationDelegate:187||Finished Spring Data repository scanning in 139ms. Found 0 Redis repository interfaces.|
|TID: N/A|2022-03-01 16:42:23.356|WARN |health-cloud-search-center-web-dev|||| main|o.s.b.a.e.EndpointId:155||Endpoint ID 'service-registry' contains invalid characters, please migrate to a valid format.|
|TID: N/A|2022-03-01 16:42:23.489|INFO |health-cloud-search-center-web-dev|||| main|o.s.c.c.s.GenericScope:295||BeanFactory id=200a3a8f-754b-3736-9a4b-f0be94177e3e|
|TID: N/A|2022-03-01 16:42:23.888|INFO |health-cloud-search-center-web-dev|||| main|c.u.j.c.EnableEncryptablePropertiesBeanFactoryPostProcessor:50||Post-processing PropertySource instances|
|TID: N/A|2022-03-01 16:42:23.919|INFO |health-cloud-search-center-web-dev|||| main|c.u.j.EncryptablePropertySourceConverter:39||Converting PropertySource bootstrapProperties-health-cloud-search-center-dev.yaml,health-cloud-biz [org.springframework.cloud.bootstrap.config.BootstrapPropertySource] to EncryptableEnumerablePropertySourceWrapper|
|TID: N/A|2022-03-01 16:42:23.919|INFO |health-cloud-search-center-web-dev|||| main|c.u.j.EncryptablePropertySourceConverter:39||Converting PropertySource bootstrapProperties-health-cloud-search-center.yaml,health-cloud-biz [org.springframework.cloud.bootstrap.config.BootstrapPropertySource] to EncryptableEnumerablePropertySourceWrapper|
|TID: N/A|2022-03-01 16:42:23.920|INFO |health-cloud-search-center-web-dev|||| main|c.u.j.EncryptablePropertySourceConverter:39||Converting PropertySource bootstrapProperties-health-cloud-search-center,health-cloud-biz [org.springframework.cloud.bootstrap.config.BootstrapPropertySource] to EncryptableEnumerablePropertySourceWrapper|
|TID: N/A|2022-03-01 16:42:23.923|INFO |health-cloud-search-center-web-dev|||| main|c.u.j.EncryptablePropertySourceConverter:39||Converting PropertySource configurationProperties [org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource] to AOP Proxy|
|TID: N/A|2022-03-01 16:42:23.923|INFO |health-cloud-search-center-web-dev|||| main|c.u.j.EncryptablePropertySourceConverter:39||Converting PropertySource servletConfigInitParams [org.springframework.core.env.PropertySource$StubPropertySource] to EncryptablePropertySourceWrapper|
|TID: N/A|2022-03-01 16:42:23.924|INFO |health-cloud-search-center-web-dev|||| main|c.u.j.EncryptablePropertySourceConverter:39||Converting PropertySource servletContextInitParams [org.springframework.core.env.PropertySource$StubPropertySource] to EncryptablePropertySourceWrapper|
|TID: N/A|2022-03-01 16:42:23.924|INFO |health-cloud-search-center-web-dev|||| main|c.u.j.EncryptablePropertySourceConverter:39||Converting PropertySource systemProperties [org.springframework.core.env.PropertiesPropertySource] to EncryptableMapPropertySourceWrapper|
|TID: N/A|2022-03-01 16:42:23.925|INFO |health-cloud-search-center-web-dev|||| main|c.u.j.EncryptablePropertySourceConverter:39||Converting PropertySource systemEnvironment [org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor$OriginAwareSystemEnvironmentPropertySource] to EncryptableMapPropertySourceWrapper|
|TID: N/A|2022-03-01 16:42:23.926|INFO |health-cloud-search-center-web-dev|||| main|c.u.j.EncryptablePropertySourceConverter:39||Converting PropertySource random [org.springframework.boot.env.RandomValuePropertySource] to EncryptablePropertySourceWrapper|
|TID: N/A|2022-03-01 16:42:23.927|INFO |health-cloud-search-center-web-dev|||| main|c.u.j.EncryptablePropertySourceConverter:39||Converting PropertySource springCloudClientHostInfo [org.springframework.core.env.MapPropertySource] to EncryptableMapPropertySourceWrapper|
|TID: N/A|2022-03-01 16:42:23.927|INFO |health-cloud-search-center-web-dev|||| main|c.u.j.EncryptablePropertySourceConverter:39||Converting PropertySource applicationConfig: [classpath:/application.properties] [org.springframework.boot.env.OriginTrackedMapPropertySource] to EncryptableMapPropertySourceWrapper|
|TID: N/A|2022-03-01 16:42:23.928|INFO |health-cloud-search-center-web-dev|||| main|c.u.j.EncryptablePropertySourceConverter:39||Converting PropertySource springCloudDefaultProperties [org.springframework.core.env.MapPropertySource] to EncryptableMapPropertySourceWrapper|
|TID: N/A|2022-03-01 16:42:23.987|INFO |health-cloud-search-center-web-dev|||| main|c.u.j.f.DefaultLazyPropertyFilter:31||Property Filter custom Bean not found with name 'encryptablePropertyFilter'. Initializing Default Property Filter|
|TID: N/A|2022-03-01 16:42:25.520|INFO |health-cloud-search-center-web-dev|||| main|o.s.b.w.e.t.TomcatWebServer:92||Tomcat initialized with port(s): 9001 (http)|
|TID: N/A|2022-03-01 16:42:25.536|INFO |health-cloud-search-center-web-dev|||| main|o.a.c.h.Http11NioProtocol:173||Initializing ProtocolHandler ["http-nio-9001"]|
|TID: N/A|2022-03-01 16:42:25.538|INFO |health-cloud-search-center-web-dev|||| main|o.a.c.c.StandardService:173||Starting service [Tomcat]|
|TID: N/A|2022-03-01 16:42:25.538|INFO |health-cloud-search-center-web-dev|||| main|o.a.c.c.StandardEngine:173||Starting Servlet engine: [Apache Tomcat/9.0.31]|
|TID: N/A|2022-03-01 16:42:25.693|INFO |health-cloud-search-center-web-dev|||| main|o.a.c.c.C.[.[.[/search-center]:173||Initializing Spring embedded WebApplicationContext|
|TID: N/A|2022-03-01 16:42:25.694|INFO |health-cloud-search-center-web-dev|||| main|o.s.w.c.ContextLoader:284||Root WebApplicationContext: initialization completed in 5903 ms|
|TID: N/A|2022-03-01 16:42:25.711|INFO |health-cloud-search-center-web-dev|||| main|c.u.j.r.DefaultLazyPropertyResolver:31||Property Resolver custom Bean not found with name 'encryptablePropertyResolver'. Initializing Default Property Resolver|
|TID: N/A|2022-03-01 16:42:25.716|INFO |health-cloud-search-center-web-dev|||| main|c.u.j.d.DefaultLazyPropertyDetector:30||Property Detector custom Bean not found with name 'encryptablePropertyDetector'. Initializing Default Property Detector|
|TID: N/A|2022-03-01 16:42:28.391|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:48||获取到clazz: class com.jiankangyouyi.cloud.search.model.ArticleEsBean|
|TID: N/A|2022-03-01 16:42:28.397|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:57||获取到EsIndex索引: @com.jiankangyouyi.cloud.es.EsIndex(value=jy_db.article)|
|TID: N/A|2022-03-01 16:42:28.398|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:64||获取到EsDoc文档: @com.jiankangyouyi.cloud.es.EsDoc(value=_doc)|
|TID: N/A|2022-03-01 16:42:28.399|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:76||id字段未找到: 未找到|
|TID: N/A|2022-03-01 16:42:28.400|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:87||找到 id|
|TID: N/A|2022-03-01 16:42:28.402|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:93||获取到EsID索引: @com.jiankangyouyi.cloud.es.EsID(value=_id)|
|TID: N/A|2022-03-01 16:42:28.605|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.ElasticSearchAutoConfiguration:38||auth验证已开启, es环境: dev|
|TID: N/A|2022-03-01 16:42:28.617|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.ElasticSearchAutoConfiguration:98||es restHighLevelClient 开始连接|
|TID: N/A|2022-03-01 16:42:28.618|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.ElasticSearchAutoConfiguration:101||auth验证参数设置|
|TID: N/A|2022-03-01 16:42:28.621|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.ElasticSearchAutoConfiguration:106||username: app_search-center, password: ******|
|TID: N/A|2022-03-01 16:42:28.834|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.ElasticSearchAutoConfiguration:117||es地址:192.168.1.201, es端口: 9300|
|TID: N/A|2022-03-01 16:42:28.835|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.ElasticSearchAutoConfiguration:118||es restHighLevelClient连接成功,耗时: 217ms |
|TID: N/A|2022-03-01 16:42:28.898|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:48||获取到clazz: class com.jiankangyouyi.cloud.search.model.MedicatedGruelEsBean|
|TID: N/A|2022-03-01 16:42:28.899|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:57||获取到EsIndex索引: @com.jiankangyouyi.cloud.es.EsIndex(value=ego_gw_db.medicated.gruel)|
|TID: N/A|2022-03-01 16:42:28.899|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:64||获取到EsDoc文档: @com.jiankangyouyi.cloud.es.EsDoc(value=_doc)|
|TID: N/A|2022-03-01 16:42:28.900|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:76||id字段未找到: 未找到|
|TID: N/A|2022-03-01 16:42:28.900|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:87||找到 id|
|TID: N/A|2022-03-01 16:42:28.901|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:93||获取到EsID索引: @com.jiankangyouyi.cloud.es.EsID(value=_id)|
|TID: N/A|2022-03-01 16:42:28.907|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:48||获取到clazz: class com.jiankangyouyi.cloud.search.model.UserInfoEsBean|
|TID: N/A|2022-03-01 16:42:28.908|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:57||获取到EsIndex索引: @com.jiankangyouyi.cloud.es.EsIndex(value=jy_db.biz_user_info)|
|TID: N/A|2022-03-01 16:42:28.908|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:64||获取到EsDoc文档: @com.jiankangyouyi.cloud.es.EsDoc(value=_doc)|
|TID: N/A|2022-03-01 16:42:28.910|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:76||id字段未找到: 未找到|
|TID: N/A|2022-03-01 16:42:28.911|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:87||找到 id|
|TID: N/A|2022-03-01 16:42:28.911|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:93||获取到EsID索引: @com.jiankangyouyi.cloud.es.EsID(value=_id)|
|TID: N/A|2022-03-01 16:42:28.917|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:48||获取到clazz: class com.jiankangyouyi.cloud.search.model.FoodMenuEsBean|
|TID: N/A|2022-03-01 16:42:28.917|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:57||获取到EsIndex索引: @com.jiankangyouyi.cloud.es.EsIndex(value=jy_db.food2.menu)|
|TID: N/A|2022-03-01 16:42:28.917|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:64||获取到EsDoc文档: @com.jiankangyouyi.cloud.es.EsDoc(value=_doc)|
|TID: N/A|2022-03-01 16:42:28.918|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:76||id字段未找到: 未找到|
|TID: N/A|2022-03-01 16:42:28.918|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:87||找到 id|
|TID: N/A|2022-03-01 16:42:28.919|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:93||获取到EsID索引: @com.jiankangyouyi.cloud.es.EsID(value=_id)|
|TID: N/A|2022-03-01 16:42:28.927|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:48||获取到clazz: class com.jiankangyouyi.cloud.search.model.SubbranchInfoEsBean|
|TID: N/A|2022-03-01 16:42:28.928|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:57||获取到EsIndex索引: @com.jiankangyouyi.cloud.es.EsIndex(value=biz_subbranch_info)|
|TID: N/A|2022-03-01 16:42:28.929|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:64||获取到EsDoc文档: @com.jiankangyouyi.cloud.es.EsDoc(value=_doc)|
|TID: N/A|2022-03-01 16:42:28.929|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:76||id字段未找到: 未找到|
|TID: N/A|2022-03-01 16:42:28.931|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:87||找到 id|
|TID: N/A|2022-03-01 16:42:28.931|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:93||获取到EsID索引: @com.jiankangyouyi.cloud.es.EsID(value=id)|
|TID: N/A|2022-03-01 16:42:28.942|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:48||获取到clazz: class com.jiankangyouyi.cloud.search.model.DynamicEsBean|
|TID: N/A|2022-03-01 16:42:28.943|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:57||获取到EsIndex索引: @com.jiankangyouyi.cloud.es.EsIndex(value=jy_db.dynamic)|
|TID: N/A|2022-03-01 16:42:28.944|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:64||获取到EsDoc文档: @com.jiankangyouyi.cloud.es.EsDoc(value=_doc)|
|TID: N/A|2022-03-01 16:42:28.945|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:83||_id字段未找到: 未找到|
|TID: N/A|2022-03-01 16:42:28.947|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:87||找到 _id|
|TID: N/A|2022-03-01 16:42:28.948|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:93||获取到EsID索引: @com.jiankangyouyi.cloud.es.EsID(value=_id)|
|TID: N/A|2022-03-01 16:42:28.954|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:48||获取到clazz: class com.jiankangyouyi.cloud.search.model.SmartInquirySymptomEsBean|
|TID: N/A|2022-03-01 16:42:28.954|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:57||获取到EsIndex索引: @com.jiankangyouyi.cloud.es.EsIndex(value=ego_gw_db.smart.inquiry.symptom)|
|TID: N/A|2022-03-01 16:42:28.955|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:64||获取到EsDoc文档: @com.jiankangyouyi.cloud.es.EsDoc(value=_doc)|
|TID: N/A|2022-03-01 16:42:28.956|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:83||_id字段未找到: 未找到|
|TID: N/A|2022-03-01 16:42:28.956|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:87||找到 _id|
|TID: N/A|2022-03-01 16:42:28.956|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:93||获取到EsID索引: @com.jiankangyouyi.cloud.es.EsID(value=_id)|
|TID: N/A|2022-03-01 16:42:28.963|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:48||获取到clazz: class com.jiankangyouyi.cloud.search.model.CourseEsBean|
|TID: N/A|2022-03-01 16:42:28.963|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:57||获取到EsIndex索引: @com.jiankangyouyi.cloud.es.EsIndex(value=jy_db.course)|
|TID: N/A|2022-03-01 16:42:28.964|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:64||获取到EsDoc文档: @com.jiankangyouyi.cloud.es.EsDoc(value=_doc)|
|TID: N/A|2022-03-01 16:42:28.965|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:83||_id字段未找到: 未找到|
|TID: N/A|2022-03-01 16:42:28.966|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:87||找到 _id|
|TID: N/A|2022-03-01 16:42:28.966|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:93||获取到EsID索引: @com.jiankangyouyi.cloud.es.EsID(value=_id)|
|TID: N/A|2022-03-01 16:42:28.973|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:48||获取到clazz: class com.jiankangyouyi.cloud.search.model.Food3EsBean|
|TID: N/A|2022-03-01 16:42:28.974|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:57||获取到EsIndex索引: @com.jiankangyouyi.cloud.es.EsIndex(value=jy_health_db.food.v3)|
|TID: N/A|2022-03-01 16:42:28.975|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:64||获取到EsDoc文档: @com.jiankangyouyi.cloud.es.EsDoc(value=_doc)|
|TID: N/A|2022-03-01 16:42:28.977|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:76||id字段未找到: 未找到|
|TID: N/A|2022-03-01 16:42:28.977|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:87||找到 id|
|TID: N/A|2022-03-01 16:42:28.977|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:93||获取到EsID索引: @com.jiankangyouyi.cloud.es.EsID(value=_id)|
|TID: N/A|2022-03-01 16:42:28.983|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:48||获取到clazz: class com.jiankangyouyi.cloud.search.model.DynamicCommentEsBean|
|TID: N/A|2022-03-01 16:42:28.983|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:57||获取到EsIndex索引: @com.jiankangyouyi.cloud.es.EsIndex(value=jy_db.dynamic.comment)|
|TID: N/A|2022-03-01 16:42:28.984|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:64||获取到EsDoc文档: @com.jiankangyouyi.cloud.es.EsDoc(value=_doc)|
|TID: N/A|2022-03-01 16:42:28.985|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:83||_id字段未找到: 未找到|
|TID: N/A|2022-03-01 16:42:28.985|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:87||找到 _id|
|TID: N/A|2022-03-01 16:42:28.986|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:93||获取到EsID索引: @com.jiankangyouyi.cloud.es.EsID(value=_id)|
|TID: N/A|2022-03-01 16:42:28.993|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:48||获取到clazz: class com.jiankangyouyi.cloud.search.model.Food2EsBean|
|TID: N/A|2022-03-01 16:42:28.993|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:57||获取到EsIndex索引: @com.jiankangyouyi.cloud.es.EsIndex(value=jy_db.food2.v2)|
|TID: N/A|2022-03-01 16:42:28.994|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:64||获取到EsDoc文档: @com.jiankangyouyi.cloud.es.EsDoc(value=_doc)|
|TID: N/A|2022-03-01 16:42:28.996|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:76||id字段未找到: 未找到|
|TID: N/A|2022-03-01 16:42:28.996|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:87||找到 id|
|TID: N/A|2022-03-01 16:42:28.996|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:93||获取到EsID索引: @com.jiankangyouyi.cloud.es.EsID(value=_id)|
|TID: N/A|2022-03-01 16:42:29.002|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:48||获取到clazz: class com.jiankangyouyi.cloud.search.model.ExerciseEsBean|
|TID: N/A|2022-03-01 16:42:29.003|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:57||获取到EsIndex索引: @com.jiankangyouyi.cloud.es.EsIndex(value=jy_db.exercise)|
|TID: N/A|2022-03-01 16:42:29.003|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:64||获取到EsDoc文档: @com.jiankangyouyi.cloud.es.EsDoc(value=_doc)|
|TID: N/A|2022-03-01 16:42:29.004|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:83||_id字段未找到: 未找到|
|TID: N/A|2022-03-01 16:42:29.004|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:87||找到 _id|
|TID: N/A|2022-03-01 16:42:29.004|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:93||获取到EsID索引: @com.jiankangyouyi.cloud.es.EsID(value=_id)|
|TID: N/A|2022-03-01 16:42:29.010|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:48||获取到clazz: class com.jiankangyouyi.cloud.search.model.QuestionEsBean|
|TID: N/A|2022-03-01 16:42:29.011|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:57||获取到EsIndex索引: @com.jiankangyouyi.cloud.es.EsIndex(value=jy_db.question)|
|TID: N/A|2022-03-01 16:42:29.011|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:64||获取到EsDoc文档: @com.jiankangyouyi.cloud.es.EsDoc(value=_doc)|
|TID: N/A|2022-03-01 16:42:29.013|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:76||id字段未找到: 未找到|
|TID: N/A|2022-03-01 16:42:29.013|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:87||找到 id|
|TID: N/A|2022-03-01 16:42:29.013|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:93||获取到EsID索引: @com.jiankangyouyi.cloud.es.EsID(value=_id)|
|TID: N/A|2022-03-01 16:42:29.020|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:48||获取到clazz: class com.jiankangyouyi.cloud.search.model.StatisticsFoodEsBean|
|TID: N/A|2022-03-01 16:42:29.021|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:57||获取到EsIndex索引: @com.jiankangyouyi.cloud.es.EsIndex(value=yi_yun_db.food3.v2)|
|TID: N/A|2022-03-01 16:42:29.022|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:64||获取到EsDoc文档: @com.jiankangyouyi.cloud.es.EsDoc(value=_doc)|
|TID: N/A|2022-03-01 16:42:29.024|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:76||id字段未找到: 未找到|
|TID: N/A|2022-03-01 16:42:29.024|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:87||找到 id|
|TID: N/A|2022-03-01 16:42:29.024|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:93||获取到EsID索引: @com.jiankangyouyi.cloud.es.EsID(value=_id)|
|TID: N/A|2022-03-01 16:42:29.031|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:48||获取到clazz: class com.jiankangyouyi.cloud.search.model.FoodAdditiveEsBean|
|TID: N/A|2022-03-01 16:42:29.032|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:57||获取到EsIndex索引: @com.jiankangyouyi.cloud.es.EsIndex(value=ego_gw_db.food.additive)|
|TID: N/A|2022-03-01 16:42:29.032|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:64||获取到EsDoc文档: @com.jiankangyouyi.cloud.es.EsDoc(value=_doc)|
|TID: N/A|2022-03-01 16:42:29.033|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:76||id字段未找到: 未找到|
|TID: N/A|2022-03-01 16:42:29.033|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:87||找到 id|
|TID: N/A|2022-03-01 16:42:29.033|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:93||获取到EsID索引: @com.jiankangyouyi.cloud.es.EsID(value=_id)|
|TID: N/A|2022-03-01 16:42:29.039|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:48||获取到clazz: class com.jiankangyouyi.cloud.search.model.DiseaseEsBean|
|TID: N/A|2022-03-01 16:42:29.039|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:57||获取到EsIndex索引: @com.jiankangyouyi.cloud.es.EsIndex(value=ego_gw_db.disease)|
|TID: N/A|2022-03-01 16:42:29.042|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:64||获取到EsDoc文档: @com.jiankangyouyi.cloud.es.EsDoc(value=_doc)|
|TID: N/A|2022-03-01 16:42:29.042|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:76||id字段未找到: 未找到|
|TID: N/A|2022-03-01 16:42:29.042|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:87||找到 id|
|TID: N/A|2022-03-01 16:42:29.043|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:93||获取到EsID索引: @com.jiankangyouyi.cloud.es.EsID(value=_id)|
|TID: N/A|2022-03-01 16:42:29.050|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:48||获取到clazz: class com.jiankangyouyi.cloud.search.model.FoodBasicEsBean|
|TID: N/A|2022-03-01 16:42:29.050|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:57||获取到EsIndex索引: @com.jiankangyouyi.cloud.es.EsIndex(value=expertise_db.food.basic)|
|TID: N/A|2022-03-01 16:42:29.050|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:64||获取到EsDoc文档: @com.jiankangyouyi.cloud.es.EsDoc(value=_doc)|
|TID: N/A|2022-03-01 16:42:29.052|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:83||_id字段未找到: 未找到|
|TID: N/A|2022-03-01 16:42:29.053|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:87||找到 _id|
|TID: N/A|2022-03-01 16:42:29.053|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:93||获取到EsID索引: @com.jiankangyouyi.cloud.es.EsID(value=_id)|
|TID: N/A|2022-03-01 16:42:29.059|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:48||获取到clazz: class com.jiankangyouyi.cloud.search.model.DrugEsBean|
|TID: N/A|2022-03-01 16:42:29.059|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:57||获取到EsIndex索引: @com.jiankangyouyi.cloud.es.EsIndex(value=ego_gw_db.drug)|
|TID: N/A|2022-03-01 16:42:29.059|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:64||获取到EsDoc文档: @com.jiankangyouyi.cloud.es.EsDoc(value=_doc)|
|TID: N/A|2022-03-01 16:42:29.060|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:76||id字段未找到: 未找到|
|TID: N/A|2022-03-01 16:42:29.060|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:87||找到 id|
|TID: N/A|2022-03-01 16:42:29.061|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:93||获取到EsID索引: @com.jiankangyouyi.cloud.es.EsID(value=_id)|
|TID: N/A|2022-03-01 16:42:29.076|INFO |health-cloud-search-center-web-dev|||| main|o.s.s.c.ThreadPoolTaskExecutor:181||Initializing ExecutorService 'taskExecutor'|
|TID: N/A|2022-03-01 16:42:29.084|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:48||获取到clazz: class com.jiankangyouyi.cloud.search.model.FoodProcessToEsBean|
|TID: N/A|2022-03-01 16:42:29.084|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:57||获取到EsIndex索引: @com.jiankangyouyi.cloud.es.EsIndex(value=health_db.v3.food.process)|
|TID: N/A|2022-03-01 16:42:29.084|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:64||获取到EsDoc文档: @com.jiankangyouyi.cloud.es.EsDoc(value=_doc)|
|TID: N/A|2022-03-01 16:42:29.086|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:76||id字段未找到: 未找到|
|TID: N/A|2022-03-01 16:42:29.086|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:87||找到 id|
|TID: N/A|2022-03-01 16:42:29.087|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:93||获取到EsID索引: @com.jiankangyouyi.cloud.es.EsID(value=_id)|
|TID: N/A|2022-03-01 16:42:29.094|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:48||获取到clazz: class com.jiankangyouyi.cloud.search.model.FoodProcessToEsBeanV4|
|TID: N/A|2022-03-01 16:42:29.094|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:57||获取到EsIndex索引: @com.jiankangyouyi.cloud.es.EsIndex(value=health_db.v4.food.process)|
|TID: N/A|2022-03-01 16:42:29.095|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:64||获取到EsDoc文档: @com.jiankangyouyi.cloud.es.EsDoc(value=_doc)|
|TID: N/A|2022-03-01 16:42:29.097|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:76||id字段未找到: 未找到|
|TID: N/A|2022-03-01 16:42:29.097|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:87||找到 id|
|TID: N/A|2022-03-01 16:42:29.097|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:93||获取到EsID索引: @com.jiankangyouyi.cloud.es.EsID(value=_id)|
|TID: N/A|2022-03-01 16:42:29.118|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:48||获取到clazz: class com.jiankangyouyi.cloud.search.model.StatisticsFoodEsBeanV4|
|TID: N/A|2022-03-01 16:42:29.118|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:57||获取到EsIndex索引: @com.jiankangyouyi.cloud.es.EsIndex(value=yi_yun_db.food3.v4)|
|TID: N/A|2022-03-01 16:42:29.119|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:64||获取到EsDoc文档: @com.jiankangyouyi.cloud.es.EsDoc(value=_doc)|
|TID: N/A|2022-03-01 16:42:29.119|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:76||id字段未找到: 未找到|
|TID: N/A|2022-03-01 16:42:29.119|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:87||找到 id|
|TID: N/A|2022-03-01 16:42:29.120|DEBUG|health-cloud-search-center-web-dev|||| main|c.j.c.e.BaseEsRepositoryImpl:93||获取到EsID索引: @com.jiankangyouyi.cloud.es.EsID(value=_id)|
|TID: N/A|2022-03-01 16:42:29.178|INFO |health-cloud-search-center-web-dev|||| main|c.j.c.s.m.AppListener:73||>>>>> HealthCloud哨兵【被关禁闭】|
|TID: N/A|2022-03-01 16:42:29.351|WARN |health-cloud-search-center-web-dev|||| main|c.n.c.s.URLConfigurationSource:121||No URLs will be polled as dynamic configuration sources.|
|TID: N/A|2022-03-01 16:42:29.352|INFO |health-cloud-search-center-web-dev|||| main|c.n.c.s.URLConfigurationSource:122||To enable URLs as dynamic configuration sources, define System property archaius.configurationSource.additionalUrls or make config.properties available on classpath.|
|TID: N/A|2022-03-01 16:42:29.363|WARN |health-cloud-search-center-web-dev|||| main|c.n.c.s.URLConfigurationSource:121||No URLs will be polled as dynamic configuration sources.|
|TID: N/A|2022-03-01 16:42:29.364|INFO |health-cloud-search-center-web-dev|||| main|c.n.c.s.URLConfigurationSource:122||To enable URLs as dynamic configuration sources, define System property archaius.configurationSource.additionalUrls or make config.properties available on classpath.|
|TID: N/A|2022-03-01 16:42:29.871|INFO |health-cloud-search-center-web-dev|||| main|c.j.c.c.c.WebConfig:20||添加@ResponseWrapper拦截器......|
|TID: N/A|2022-03-01 16:42:30.069|INFO |health-cloud-search-center-web-dev|||| main|o.s.s.c.ThreadPoolTaskScheduler:181||Initializing ExecutorService 'Nacso-Watch-Task-Scheduler'|
|TID: N/A|2022-03-01 16:42:30.887|INFO |health-cloud-search-center-web-dev|||| main|o.s.b.a.e.w.EndpointLinksResolver:58||Exposing 20 endpoint(s) beneath base path '/actuator'|
|TID: N/A|2022-03-01 16:42:30.985|INFO |health-cloud-search-center-web-dev|||| main|o.a.c.h.Http11NioProtocol:173||Starting ProtocolHandler ["http-nio-9001"]|
|TID: N/A|2022-03-01 16:42:31.046|INFO |health-cloud-search-center-web-dev|||| main|o.s.b.w.e.t.TomcatWebServer:204||Tomcat started on port(s): 9001 (http) with context path '/search-center'|
|TID: N/A|2022-03-01 16:42:31.085|INFO |health-cloud-search-center-web-dev|||| main|c.a.c.n.r.NacosServiceRegistry:65||nacos registry, DEFAULT_GROUP health-cloud-search-center 172.16.220.4:9001 register finished|
|TID: N/A|2022-03-01 16:42:31.096|INFO |health-cloud-search-center-web-dev|||| main|c.j.c.HealthCloudSearchWebApplication:61||Started HealthCloudSearchWebApplication in 16.405 seconds (JVM running for 18.222)|
|TID: N/A|2022-03-01 16:42:31.110|INFO |health-cloud-search-center-web-dev|||| main|c.j.c.s.m.AppListener:87||>>>>> HealthCloud哨兵 <<<<< api将不注册入网关! 【api注册功能被阉割,请检查配置】 |
|TID: N/A|2022-03-01 16:42:31.117|INFO |health-cloud-search-center-web-dev|||| main|c.a.n.c.c.i.ClientWorker:197||[fixed-119.3.216.80_8848-121.36.35.198_8848-119.3.214.140_8848-5d2b1d22-a8d1-4005-9a84-8818228d2b7f] [subscribe] health-cloud-search-center.yaml+health-cloud-biz+5d2b1d22-a8d1-4005-9a84-8818228d2b7f|
|TID: N/A|2022-03-01 16:42:31.122|INFO |health-cloud-search-center-web-dev|||| main|c.a.n.c.c.i.CacheData:92||[fixed-119.3.216.80_8848-121.36.35.198_8848-119.3.214.140_8848-5d2b1d22-a8d1-4005-9a84-8818228d2b7f] [add-listener] ok, tenant=5d2b1d22-a8d1-4005-9a84-8818228d2b7f, dataId=health-cloud-search-center.yaml, group=health-cloud-biz, cnt=1|
|TID: N/A|2022-03-01 16:42:31.123|INFO |health-cloud-search-center-web-dev|||| main|c.a.n.c.c.i.ClientWorker:197||[fixed-119.3.216.80_8848-121.36.35.198_8848-119.3.214.140_8848-5d2b1d22-a8d1-4005-9a84-8818228d2b7f] [subscribe] health-cloud-search-center-dev.yaml+health-cloud-biz+5d2b1d22-a8d1-4005-9a84-8818228d2b7f|
|TID: N/A|2022-03-01 16:42:31.124|INFO |health-cloud-search-center-web-dev|||| main|c.a.n.c.c.i.CacheData:92||[fixed-119.3.216.80_8848-121.36.35.198_8848-119.3.214.140_8848-5d2b1d22-a8d1-4005-9a84-8818228d2b7f] [add-listener] ok, tenant=5d2b1d22-a8d1-4005-9a84-8818228d2b7f, dataId=health-cloud-search-center-dev.yaml, group=health-cloud-biz, cnt=1|
|TID: N/A|2022-03-01 16:42:31.124|INFO |health-cloud-search-center-web-dev|||| main|c.a.n.c.c.i.ClientWorker:197||[fixed-119.3.216.80_8848-121.36.35.198_8848-119.3.214.140_8848-5d2b1d22-a8d1-4005-9a84-8818228d2b7f] [subscribe] health-cloud-search-center+health-cloud-biz+5d2b1d22-a8d1-4005-9a84-8818228d2b7f|
|TID: N/A|2022-03-01 16:42:31.125|INFO |health-cloud-search-center-web-dev|||| main|c.a.n.c.c.i.CacheData:92||[fixed-119.3.216.80_8848-121.36.35.198_8848-119.3.214.140_8848-5d2b1d22-a8d1-4005-9a84-8818228d2b7f] [add-listener] ok, tenant=5d2b1d22-a8d1-4005-9a84-8818228d2b7f, dataId=health-cloud-search-center, group=health-cloud-biz, cnt=1|
|TID: N/A|2022-03-01 16:42:31.126|INFO |health-cloud-search-center-web-dev|||| main|c.j.c.HealthCloudSearchWebApplication:26||启动成功!!!|
|TID: N/A|2022-03-01 16:42:31.126|INFO |health-cloud-search-center-web-dev|||| main|c.j.c.HealthCloudSearchWebApplication:27||Search-Center地址: http://127.0.0.1:9001/|
|TID: N/A|2022-03-01 16:42:31.133|INFO |health-cloud-search-center-web-dev||||com.alibaba.nacos.client.Worker.longPolling.fixed-119.3.216.80_8848-121.36.35.198_8848-119.3.214.140_8848-5d2b1d22-a8d1-4005-9a84-8818228d2b7f|c.a.n.c.c.i.ClientWorker:522||get changedGroupKeys:[]|
|TID: N/A|2022-03-01 16:42:31.147|INFO |health-cloud-search-center-web-dev||||com.alibaba.nacos.client.Worker.longPolling.fixed-119.3.216.80_8848-121.36.35.198_8848-119.3.214.140_8848-5d2b1d22-a8d1-4005-9a84-8818228d2b7f|c.a.n.c.c.i.ClientWorker:522||get changedGroupKeys:[]|
|TID: N/A|2022-03-01 16:43:00.848|INFO |health-cloud-search-center-web-dev||||com.alibaba.nacos.client.Worker.longPolling.fixed-119.3.216.80_8848-121.36.35.198_8848-119.3.214.140_8848-5d2b1d22-a8d1-4005-9a84-8818228d2b7f|c.a.n.c.c.i.ClientWorker:522||get changedGroupKeys:[]|
|TID: N/A|2022-03-01 16:43:30.352|INFO |health-cloud-search-center-web-dev||||com.alibaba.nacos.client.Worker.longPolling.fixed-119.3.216.80_8848-121.36.35.198_8848-119.3.214.140_8848-5d2b1d22-a8d1-4005-9a84-8818228d2b7f|c.a.n.c.c.i.ClientWorker:522||get changedGroupKeys:[]|
|TID: N/A|2022-03-01 16:43:36.347|INFO |health-cloud-search-center-web-dev||||SpringContextShutdownHook|o.s.s.c.ThreadPoolTaskScheduler:218||Shutting down ExecutorService 'Nacso-Watch-Task-Scheduler'|
|TID: N/A|2022-03-01 16:43:36.373|INFO |health-cloud-search-center-web-dev||||SpringContextShutdownHook|c.a.c.n.r.NacosServiceRegistry:80||De-registering from Nacos Server now...|
|TID: N/A|2022-03-01 16:43:36.381|INFO |health-cloud-search-center-web-dev||||SpringContextShutdownHook|c.a.c.n.r.NacosServiceRegistry:100||De-registration finished.|
|TID: N/A|2022-03-01 16:43:36.385|INFO |health-cloud-search-center-web-dev||||SpringContextShutdownHook|o.s.s.c.ThreadPoolTaskExecutor:218||Shutting down ExecutorService 'taskExecutor'|
|TID: N/A|2022-03-01 16:42:31.117|INFO |health-cloud-search-center-web-dev|||| main|c.a.n.c.c.i.ClientWorker:197||[fixed-119.3.216.80_8848-121.36.35.198_8848-119.3.214.140_8848-5d2b1d22-a8d1-4005-9a84-8818228d2b7f] [subscribe] health-cloud-search-center.yaml+health-cloud-biz+5d2b1d22-a8d1-4005-9a84-8818228d2b7f|
|TID: N/A|2022-03-01 16:42:31.122|INFO |health-cloud-search-center-web-dev|||| main|c.a.n.c.c.i.CacheData:92||[fixed-119.3.216.80_8848-121.36.35.198_8848-119.3.214.140_8848-5d2b1d22-a8d1-4005-9a84-8818228d2b7f] [add-listener] ok, tenant=5d2b1d22-a8d1-4005-9a84-8818228d2b7f, dataId=health-cloud-search-center.yaml, group=health-cloud-biz, cnt=1|
|TID: N/A|2022-03-01 16:42:31.123|INFO |health-cloud-search-center-web-dev|||| main|c.a.n.c.c.i.ClientWorker:197||[fixed-119.3.216.80_8848-121.36.35.198_8848-119.3.214.140_8848-5d2b1d22-a8d1-4005-9a84-8818228d2b7f] [subscribe] health-cloud-search-center-dev.yaml+health-cloud-biz+5d2b1d22-a8d1-4005-9a84-8818228d2b7f|
|TID: N/A|2022-03-01 16:42:31.124|INFO |health-cloud-search-center-web-dev|||| main|c.a.n.c.c.i.CacheData:92||[fixed-119.3.216.80_8848-121.36.35.198_8848-119.3.214.140_8848-5d2b1d22-a8d1-4005-9a84-8818228d2b7f] [add-listener] ok, tenant=5d2b1d22-a8d1-4005-9a84-8818228d2b7f, dataId=health-cloud-search-center-dev.yaml, group=health-cloud-biz, cnt=1|
|TID: N/A|2022-03-01 16:42:31.124|INFO |health-cloud-search-center-web-dev|||| main|c.a.n.c.c.i.ClientWorker:197||[fixed-119.3.216.80_8848-121.36.35.198_8848-119.3.214.140_8848-5d2b1d22-a8d1-4005-9a84-8818228d2b7f] [subscribe] health-cloud-search-center+health-cloud-biz+5d2b1d22-a8d1-4005-9a84-8818228d2b7f|
|TID: N/A|2022-03-01 16:42:31.125|INFO |health-cloud-search-center-web-dev|||| main|c.a.n.c.c.i.CacheData:92||[fixed-119.3.216.80_8848-121.36.35.198_8848-119.3.214.140_8848-5d2b1d22-a8d1-4005-9a84-8818228d2b7f] [add-listener] ok, tenant=5d2b1d22-a8d1-4005-9a84-8818228d2b7f, dataId=health-cloud-search-center, group=health-cloud-biz, cnt=1|
|TID: N/A|2022-03-01 16:42:31.133|INFO |health-cloud-search-center-web-dev||||com.alibaba.nacos.client.Worker.longPolling.fixed-119.3.216.80_8848-121.36.35.198_8848-119.3.214.140_8848-5d2b1d22-a8d1-4005-9a84-8818228d2b7f|c.a.n.c.c.i.ClientWorker:522||get changedGroupKeys:[]|
|TID: N/A|2022-03-01 16:42:31.147|INFO |health-cloud-search-center-web-dev||||com.alibaba.nacos.client.Worker.longPolling.fixed-119.3.216.80_8848-121.36.35.198_8848-119.3.214.140_8848-5d2b1d22-a8d1-4005-9a84-8818228d2b7f|c.a.n.c.c.i.ClientWorker:522||get changedGroupKeys:[]|
|TID: N/A|2022-03-01 16:43:00.848|INFO |health-cloud-search-center-web-dev||||com.alibaba.nacos.client.Worker.longPolling.fixed-119.3.216.80_8848-121.36.35.198_8848-119.3.214.140_8848-5d2b1d22-a8d1-4005-9a84-8818228d2b7f|c.a.n.c.c.i.ClientWorker:522||get changedGroupKeys:[]|
|TID: N/A|2022-03-01 16:43:30.352|INFO |health-cloud-search-center-web-dev||||com.alibaba.nacos.client.Worker.longPolling.fixed-119.3.216.80_8848-121.36.35.198_8848-119.3.214.140_8848-5d2b1d22-a8d1-4005-9a84-8818228d2b7f|c.a.n.c.c.i.ClientWorker:522||get changedGroupKeys:[]|
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment