chore: add precommit hook for testing code style (#1448)
* chore: add precommit hook for testing code style * chore: use prettier + eslint for js code
This commit is contained in:
parent
a4dd67ae45
commit
be219ddee0
|
@ -1,10 +0,0 @@
|
||||||
{
|
|
||||||
"indent_size": 4,
|
|
||||||
"indent_char": " ",
|
|
||||||
"indent_level": 0,
|
|
||||||
"indent_with_tabs": false,
|
|
||||||
"preserve_newlines": true,
|
|
||||||
"max_preserve_newlines": 2,
|
|
||||||
"end_with_newline": true,
|
|
||||||
"jslint_happy": true
|
|
||||||
}
|
|
|
@ -61,23 +61,24 @@ endif()
|
||||||
|
|
||||||
set(TR_VCS_REVISION_FILE "${CMAKE_SOURCE_DIR}/REVISION")
|
set(TR_VCS_REVISION_FILE "${CMAKE_SOURCE_DIR}/REVISION")
|
||||||
|
|
||||||
|
if(IS_DIRECTORY ${CMAKE_SOURCE_DIR}/.git)
|
||||||
|
find_package(Git)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(NOT "$ENV{JENKINS_URL}" STREQUAL "" AND NOT "$ENV{GIT_COMMIT}" STREQUAL "")
|
if(NOT "$ENV{JENKINS_URL}" STREQUAL "" AND NOT "$ENV{GIT_COMMIT}" STREQUAL "")
|
||||||
set(TR_VCS_REVISION "$ENV{GIT_COMMIT}")
|
set(TR_VCS_REVISION "$ENV{GIT_COMMIT}")
|
||||||
elseif(NOT "$ENV{TEAMCITY_PROJECT_NAME}" STREQUAL "" AND NOT "$ENV{BUILD_VCS_NUMBER}" STREQUAL "")
|
elseif(NOT "$ENV{TEAMCITY_PROJECT_NAME}" STREQUAL "" AND NOT "$ENV{BUILD_VCS_NUMBER}" STREQUAL "")
|
||||||
set(TR_VCS_REVISION "$ENV{BUILD_VCS_NUMBER}")
|
set(TR_VCS_REVISION "$ENV{BUILD_VCS_NUMBER}")
|
||||||
elseif(IS_DIRECTORY ${CMAKE_SOURCE_DIR}/.git)
|
elseif(GIT_FOUND)
|
||||||
find_package(Git)
|
execute_process(
|
||||||
if(GIT_FOUND)
|
COMMAND
|
||||||
execute_process(
|
${GIT_EXECUTABLE} rev-list --max-count=1 HEAD
|
||||||
COMMAND
|
WORKING_DIRECTORY
|
||||||
${GIT_EXECUTABLE} rev-list --max-count=1 HEAD
|
${CMAKE_SOURCE_DIR}
|
||||||
WORKING_DIRECTORY
|
OUTPUT_VARIABLE
|
||||||
${CMAKE_SOURCE_DIR}
|
TR_VCS_REVISION
|
||||||
OUTPUT_VARIABLE
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
TR_VCS_REVISION
|
)
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if("${TR_VCS_REVISION}" STREQUAL "" AND EXISTS "${TR_VCS_REVISION_FILE}")
|
if("${TR_VCS_REVISION}" STREQUAL "" AND EXISTS "${TR_VCS_REVISION_FILE}")
|
||||||
|
@ -664,4 +665,32 @@ set(CPACK_SOURCE_IGNORE_FILES
|
||||||
\\\\.git
|
\\\\.git
|
||||||
)
|
)
|
||||||
|
|
||||||
|
## Code Formatting
|
||||||
|
|
||||||
|
if(GIT_FOUND)
|
||||||
|
message(STATUS "Looking for uncrustify")
|
||||||
|
execute_process(COMMAND
|
||||||
|
"${GIT_EXECUTABLE}" rev-parse --show-toplevel
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
OUTPUT_VARIABLE TR_GIT_ROOT
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
if(TR_GIT_ROOT)
|
||||||
|
configure_file(
|
||||||
|
"${CMAKE_SOURCE_DIR}/format/pre-commit"
|
||||||
|
"${TR_GIT_ROOT}/.git/hooks/pre-commit"
|
||||||
|
COPYONLY
|
||||||
|
)
|
||||||
|
add_custom_target(check-format
|
||||||
|
COMMAND "${CMAKE_SOURCE_DIR}/format/format.sh" --all --check
|
||||||
|
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||||
|
)
|
||||||
|
add_custom_target(format
|
||||||
|
COMMAND "${CMAKE_SOURCE_DIR}/format/format.sh" --all
|
||||||
|
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||||
|
)
|
||||||
|
endif(TR_GIT_ROOT)
|
||||||
|
unset(TR_GIT_ROOT)
|
||||||
|
endif(GIT_FOUND)
|
||||||
|
|
||||||
include(CPack)
|
include(CPack)
|
||||||
|
|
|
@ -1,79 +1,4 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
set -euo pipefail
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
|
exec "${DIR}/format/format.sh" --all
|
||||||
[ -z "${1:-}" ] || cd "$1"
|
|
||||||
|
|
||||||
echo '=================='
|
|
||||||
echo '=== uncrustify ==='
|
|
||||||
echo '=================='
|
|
||||||
echo ''
|
|
||||||
|
|
||||||
find \
|
|
||||||
cli \
|
|
||||||
daemon \
|
|
||||||
gtk \
|
|
||||||
libtransmission \
|
|
||||||
utils \
|
|
||||||
\( -name '*.c' -o -name '*.h' \) \
|
|
||||||
! \( -name 'ConvertUTF.*' -o -name 'jsonsl.*' -o -name 'wildmat.c' \) \
|
|
||||||
-print0 |
|
|
||||||
xargs \
|
|
||||||
-0 \
|
|
||||||
uncrustify \
|
|
||||||
--replace \
|
|
||||||
--no-backup \
|
|
||||||
-c uncrustify.cfg
|
|
||||||
|
|
||||||
find \
|
|
||||||
qt \
|
|
||||||
tests \
|
|
||||||
\( -name '*.cc' -o -name '*.h' \) \
|
|
||||||
-print0 |
|
|
||||||
xargs \
|
|
||||||
-0 \
|
|
||||||
uncrustify \
|
|
||||||
--replace \
|
|
||||||
--no-backup \
|
|
||||||
-l CPP \
|
|
||||||
-c uncrustify.cfg
|
|
||||||
|
|
||||||
echo ''
|
|
||||||
echo '================================================================='
|
|
||||||
echo '=== const placement (until uncrustify supports it, hopefully) ==='
|
|
||||||
echo '================================================================='
|
|
||||||
echo ''
|
|
||||||
|
|
||||||
find \
|
|
||||||
cli \
|
|
||||||
daemon \
|
|
||||||
gtk \
|
|
||||||
libtransmission \
|
|
||||||
qt \
|
|
||||||
utils \
|
|
||||||
\( -name '*.c' -o -name '*.cc' -o -name '*.h' \) \
|
|
||||||
! \( -name 'ConvertUTF.*' -o -name 'jsonsl.*' -o -name 'wildmat.c' \) \
|
|
||||||
-print0 |
|
|
||||||
xargs \
|
|
||||||
-0 \
|
|
||||||
-n1 \
|
|
||||||
perl \
|
|
||||||
-pi \
|
|
||||||
-e 'BEGIN { print STDOUT "Processing: ${ARGV[0]}\n" } s/((?:^|[(,;]|\bstatic\s+)\s*)\b(const)\b(?!\s+\w+\s*\[)/\1>\2</g'
|
|
||||||
|
|
||||||
echo ''
|
|
||||||
echo '==================='
|
|
||||||
echo '=== js-beautify ==='
|
|
||||||
echo '==================='
|
|
||||||
echo ''
|
|
||||||
|
|
||||||
find \
|
|
||||||
web \
|
|
||||||
! -path '*/jquery/*' \
|
|
||||||
-name '*.js' \
|
|
||||||
-print0 |
|
|
||||||
xargs \
|
|
||||||
-0 \
|
|
||||||
js-beautify \
|
|
||||||
--config .jsbeautifyrc \
|
|
||||||
--replace
|
|
||||||
|
|
|
@ -6,4 +6,5 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- .:/src
|
- .:/src
|
||||||
- ./code_style.sh:/code_style.sh:ro
|
- ./code_style.sh:/code_style.sh:ro
|
||||||
|
- ./format:/format:ro
|
||||||
command: ["/bin/sh", "/code_style.sh", "/src"]
|
command: ["/bin/sh", "/code_style.sh", "/src"]
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
FROM alpine
|
FROM alpine
|
||||||
|
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
|
bash \
|
||||||
|
git \
|
||||||
npm \
|
npm \
|
||||||
perl \
|
perl \
|
||||||
uncrustify \
|
uncrustify \
|
||||||
&& npm install -g \
|
&& npm install -g \
|
||||||
js-beautify
|
eslint \
|
||||||
|
prettier
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"commonjs": true,
|
||||||
|
"es6": true,
|
||||||
|
"jquery": true
|
||||||
|
},
|
||||||
|
"extends": "eslint:recommended",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 6,
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"jsx": true
|
||||||
|
},
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"curly": ["error", "all"],
|
||||||
|
"no-undef": "off",
|
||||||
|
"no-unused-vars": "off",
|
||||||
|
"no-var": "off",
|
||||||
|
"prefer-const": "off",
|
||||||
|
"semi": ["error", "always"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
|
||||||
|
find_sourcefiles_in_dirs(){
|
||||||
|
if [ "$changed" -ne "0" ]; then
|
||||||
|
files="$(git diff --name-only -- "${@}")";
|
||||||
|
elif [ "$staged" -ne "0" ]; then
|
||||||
|
files="$(git diff --name-only --staged -- "${@}")";
|
||||||
|
else
|
||||||
|
files="$(find "${@}")";
|
||||||
|
fi
|
||||||
|
# remove skipfiles
|
||||||
|
files="$(echo "${files}" | sort | comm -23 - "${root}/format/skipfiles.txt")"
|
||||||
|
echo "${files}"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# globals
|
||||||
|
|
||||||
|
all=0
|
||||||
|
changed=0
|
||||||
|
check=0
|
||||||
|
exitcode=0
|
||||||
|
staged=0
|
||||||
|
root="$(git rev-parse --show-toplevel)"
|
||||||
|
eslint_args=(-c "${root}/format/eslint.config.json")
|
||||||
|
prettier_args=(--config "${root}/format/prettier.config.json" --loglevel warn)
|
||||||
|
uncrustify_args=(-c "${root}/format/uncrustify.cfg")
|
||||||
|
|
||||||
|
# parse command line
|
||||||
|
|
||||||
|
for var in "$@"
|
||||||
|
do
|
||||||
|
case "$var" in
|
||||||
|
--changed) changed=1;;
|
||||||
|
--cached|--staged) staged=1;;
|
||||||
|
--check|--test) check=1;;
|
||||||
|
--all) all=1;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "${changed}${staged}${all}" -eq "000" ]; then
|
||||||
|
echo "usage: $0 {--all|--changed|--staged} [--check]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${check}" -ne "0" ]; then
|
||||||
|
prettier_args+=(--check);
|
||||||
|
uncrustify_args+=(--check -q);
|
||||||
|
else
|
||||||
|
eslint_args+=(--fix)
|
||||||
|
prettier_args+=(--write)
|
||||||
|
uncrustify_args+=(--replace --no-backup)
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
cd "${root}" || exit 1
|
||||||
|
|
||||||
|
# format C/C++ files
|
||||||
|
tool='uncrustify'
|
||||||
|
tool_args=("${uncrustify_args[@]}")
|
||||||
|
cish_files=()
|
||||||
|
if ! command -v "${tool}" &> /dev/null; then
|
||||||
|
echo "skipping $tool (not found)"
|
||||||
|
else
|
||||||
|
# C
|
||||||
|
dirs=(cli daemon gtk libtransmission utils)
|
||||||
|
filestr=$(find_sourcefiles_in_dirs "${dirs[@]}") # newline-delimited string
|
||||||
|
filestr=$(echo "$filestr" | grep -e "\.[ch]$") # remove non-C files
|
||||||
|
IFS=$'\n' read -d '' -ra files <<< "${filestr}"; # convert to array
|
||||||
|
if [ ${#files[@]} -ne 0 ]; then
|
||||||
|
"${tool}" "${tool_args[@]}" -l C "${files[@]}" 1>/dev/null || exitcode=1
|
||||||
|
cish_files+=("${files[@]}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# C++
|
||||||
|
dirs=(qt tests)
|
||||||
|
filestr=$(find_sourcefiles_in_dirs "${dirs[@]}") # newline-delimited string
|
||||||
|
filestr=$(echo "$filestr" | grep -e "\.cc$" -e "\.h$") # remove non-C++ files
|
||||||
|
IFS=$'\n' read -d '' -ra files <<< "${filestr}"; # convert to array
|
||||||
|
if [ ${#files[@]} -ne 0 ]; then
|
||||||
|
"${tool}" "${tool_args[@]}" -l CPP "${files[@]}" 1>/dev/null || exitcode=1
|
||||||
|
cish_files+=("${files[@]}")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# check const placement.
|
||||||
|
# do this manually since neither clang-format nor uncrustify do it
|
||||||
|
if [ ${#cish_files[@]} -ne 0 ]; then
|
||||||
|
if [ "${check}" -ne "0" ]; then
|
||||||
|
matches="$(grep --line-number --with-filename -P '((?:^|[(,;]|\bstatic\s+)\s*)\b(const)\b(?!\s+\w+\s*\[)' "${cish_files[@]}")"
|
||||||
|
if [ -n "${matches}" ]; then
|
||||||
|
echo 'const in wrong place:'
|
||||||
|
echo "${matches}"
|
||||||
|
exitcode=1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
perl -pi -e 's/((?:^|[(,;]|\bstatic\s+)\s*)\b(const)\b(?!\s+\w+\s*\[)/\1>\2</g' "${cish_files[@]}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${check}" -ne "0" ]; then
|
||||||
|
if [ "${exitcode}" -eq "1" ]; then
|
||||||
|
echo "style check failed. re-run format/format.sh without --check to reformat."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# format JS files
|
||||||
|
tool='prettier'
|
||||||
|
tool_args=("${prettier_args[@]}")
|
||||||
|
if ! command -v "${tool}" &> /dev/null; then
|
||||||
|
echo "skipping $tool (not found)"
|
||||||
|
else
|
||||||
|
dirs=(web)
|
||||||
|
filestr=$(find_sourcefiles_in_dirs "${dirs[@]}") # newline-delimited string
|
||||||
|
filestr=$(echo "$filestr" | grep -e "\.js$") # remove non-JS files
|
||||||
|
IFS=$'\n' read -d '' -ra files <<< "${filestr}"; # convert to array
|
||||||
|
if [ ${#files[@]} -ne 0 ]; then
|
||||||
|
"${tool}" "${tool_args[@]}" "${files[@]}" || exitcode=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# lint JS files
|
||||||
|
tool='eslint'
|
||||||
|
tool_args=("${eslint_args[@]}")
|
||||||
|
if ! command -v "${tool}" &> /dev/null; then
|
||||||
|
echo "skipping $tool (not found)"
|
||||||
|
else
|
||||||
|
dirs=(web)
|
||||||
|
filestr=$(find_sourcefiles_in_dirs "${dirs[@]}") # newline-delimited string
|
||||||
|
filestr=$(echo "$filestr" | grep -e "\.js$") # remove non-JS files
|
||||||
|
IFS=$'\n' read -d '' -ra files <<< "${filestr}"; # convert to array
|
||||||
|
if [ ${#files[@]} -ne 0 ]; then
|
||||||
|
"${tool}" "${tool_args[@]}" "${files[@]}" || exitcode=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
exit $exitcode
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
[ -z "$TR_SKIP_HOOKS" ] || exit 0
|
||||||
|
|
||||||
|
root="$(git rev-parse --show-toplevel)"
|
||||||
|
exec "${root}/format/format.sh" --staged --test
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"arrowParens": "always",
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"embeddedLanguageFormatting": "auto",
|
||||||
|
"htmlWhitespaceSensitivity": "css",
|
||||||
|
"insertPragma": false,
|
||||||
|
"jsxBracketSameLine": false,
|
||||||
|
"jsxSingleQuote": false,
|
||||||
|
"printWidth": 100,
|
||||||
|
"proseWrap": "preserve",
|
||||||
|
"quoteProps": "as-needed",
|
||||||
|
"requirePragma": false,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"useTabs": false,
|
||||||
|
"vueIndentScriptAndStyle": false
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
libtransmission/ConvertUTF.c
|
||||||
|
libtransmission/ConvertUTF.h
|
||||||
|
libtransmission/jsonsl.c
|
||||||
|
libtransmission/jsonsl.h
|
||||||
|
libtransmission/wildmat.c
|
||||||
|
web/javascript/jquery/jquery-migrate.min.js
|
||||||
|
web/javascript/jquery/jquery.min.js
|
||||||
|
web/javascript/jquery/jquery.transmenu.js
|
||||||
|
web/javascript/jquery/jquery.transmenu.min.js
|
||||||
|
web/javascript/jquery/jquery.ui-contextmenu.js
|
||||||
|
web/javascript/jquery/jquery.ui-contextmenu.min.js
|
||||||
|
web/javascript/jquery/jquery-ui.min.js
|
|
@ -1,4 +1,4 @@
|
||||||
# Uncrustify-0.70.1_f
|
# Uncrustify_d-0.71.0-13-42bfdca8
|
||||||
newlines = auto
|
newlines = auto
|
||||||
input_tab_size = 8
|
input_tab_size = 8
|
||||||
output_tab_size = 8
|
output_tab_size = 8
|
||||||
|
@ -6,6 +6,7 @@ string_escape_char = 92
|
||||||
string_escape_char2 = 0
|
string_escape_char2 = 0
|
||||||
string_replace_tab_chars = false
|
string_replace_tab_chars = false
|
||||||
tok_split_gte = false
|
tok_split_gte = false
|
||||||
|
disable_processing_nl_cont = false
|
||||||
disable_processing_cmt = " *INDENT-OFF*"
|
disable_processing_cmt = " *INDENT-OFF*"
|
||||||
enable_processing_cmt = " *INDENT-ON*"
|
enable_processing_cmt = " *INDENT-ON*"
|
||||||
enable_digraphs = false
|
enable_digraphs = false
|
||||||
|
@ -159,6 +160,7 @@ sp_throw_paren = force
|
||||||
sp_after_throw = force
|
sp_after_throw = force
|
||||||
sp_catch_paren = force
|
sp_catch_paren = force
|
||||||
sp_oc_catch_paren = ignore
|
sp_oc_catch_paren = ignore
|
||||||
|
sp_before_oc_proto_list = ignore
|
||||||
sp_oc_classname_paren = ignore
|
sp_oc_classname_paren = ignore
|
||||||
sp_version_paren = ignore
|
sp_version_paren = ignore
|
||||||
sp_scope_paren = ignore
|
sp_scope_paren = ignore
|
||||||
|
@ -177,7 +179,7 @@ sp_finally_brace = ignore
|
||||||
sp_brace_finally = ignore
|
sp_brace_finally = ignore
|
||||||
sp_try_brace = ignore
|
sp_try_brace = ignore
|
||||||
sp_getset_brace = ignore
|
sp_getset_brace = ignore
|
||||||
sp_word_brace = remove
|
sp_word_brace_init_lst = ignore
|
||||||
sp_word_brace_ns = add
|
sp_word_brace_ns = add
|
||||||
sp_before_dc = remove
|
sp_before_dc = remove
|
||||||
sp_after_dc = remove
|
sp_after_dc = remove
|
||||||
|
@ -284,6 +286,7 @@ indent_macro_brace = true
|
||||||
indent_member = 4
|
indent_member = 4
|
||||||
indent_member_single = true
|
indent_member_single = true
|
||||||
indent_sing_line_comments = 0
|
indent_sing_line_comments = 0
|
||||||
|
indent_sparen_extra = 0
|
||||||
indent_relative_single_line_comments = false
|
indent_relative_single_line_comments = false
|
||||||
indent_switch_case = 0
|
indent_switch_case = 0
|
||||||
indent_switch_break_with_case = false
|
indent_switch_break_with_case = false
|
||||||
|
@ -308,7 +311,9 @@ indent_first_for_expr = false
|
||||||
indent_square_nl = false
|
indent_square_nl = false
|
||||||
indent_preserve_sql = false
|
indent_preserve_sql = false
|
||||||
indent_align_assign = false
|
indent_align_assign = false
|
||||||
|
indent_off_after_assign = false
|
||||||
indent_align_paren = false
|
indent_align_paren = false
|
||||||
|
indent_oc_inside_msg_sel = false
|
||||||
indent_oc_block = false
|
indent_oc_block = false
|
||||||
indent_oc_block_msg = 0
|
indent_oc_block_msg = 0
|
||||||
indent_oc_msg_colon = 0
|
indent_oc_msg_colon = 0
|
||||||
|
@ -322,8 +327,11 @@ indent_min_vbrace_open = 0
|
||||||
indent_vbrace_open_on_tabstop = false
|
indent_vbrace_open_on_tabstop = false
|
||||||
indent_token_after_brace = false
|
indent_token_after_brace = false
|
||||||
indent_cpp_lambda_body = true
|
indent_cpp_lambda_body = true
|
||||||
|
indent_compound_literal_return = true
|
||||||
indent_using_block = true
|
indent_using_block = true
|
||||||
indent_ternary_operator = 0
|
indent_ternary_operator = 0
|
||||||
|
indent_inside_ternary_operator = false
|
||||||
|
indent_off_after_return = false
|
||||||
indent_off_after_return_new = false
|
indent_off_after_return_new = false
|
||||||
indent_single_after_return = true
|
indent_single_after_return = true
|
||||||
indent_ignore_asm_block = false
|
indent_ignore_asm_block = false
|
||||||
|
@ -367,6 +375,7 @@ nl_brace_else = force
|
||||||
nl_elseif_brace = force
|
nl_elseif_brace = force
|
||||||
nl_else_brace = force
|
nl_else_brace = force
|
||||||
nl_else_if = remove
|
nl_else_if = remove
|
||||||
|
nl_before_opening_brace_func_class_def = ignore
|
||||||
nl_before_if_closing_paren = remove
|
nl_before_if_closing_paren = remove
|
||||||
nl_brace_finally = ignore
|
nl_brace_finally = ignore
|
||||||
nl_finally_brace = ignore
|
nl_finally_brace = ignore
|
||||||
|
@ -433,6 +442,7 @@ nl_func_decl_start_multi_line = false
|
||||||
nl_func_def_start_multi_line = false
|
nl_func_def_start_multi_line = false
|
||||||
nl_func_decl_args = remove
|
nl_func_decl_args = remove
|
||||||
nl_func_def_args = remove
|
nl_func_def_args = remove
|
||||||
|
nl_func_call_args = ignore
|
||||||
nl_func_decl_args_multi_line = false
|
nl_func_decl_args_multi_line = false
|
||||||
nl_func_def_args_multi_line = false
|
nl_func_def_args_multi_line = false
|
||||||
nl_func_decl_end = remove
|
nl_func_decl_end = remove
|
||||||
|
@ -445,9 +455,11 @@ nl_func_decl_empty = remove
|
||||||
nl_func_def_empty = remove
|
nl_func_def_empty = remove
|
||||||
nl_func_call_empty = remove
|
nl_func_call_empty = remove
|
||||||
nl_func_call_start = ignore
|
nl_func_call_start = ignore
|
||||||
|
nl_func_call_end = ignore
|
||||||
nl_func_call_start_multi_line = false
|
nl_func_call_start_multi_line = false
|
||||||
nl_func_call_args_multi_line = false
|
nl_func_call_args_multi_line = false
|
||||||
nl_func_call_end_multi_line = false
|
nl_func_call_end_multi_line = false
|
||||||
|
nl_func_call_args_multi_line_ignore_closures = false
|
||||||
nl_template_start = false
|
nl_template_start = false
|
||||||
nl_template_args = false
|
nl_template_args = false
|
||||||
nl_template_end = false
|
nl_template_end = false
|
||||||
|
@ -545,6 +557,10 @@ eat_blanks_before_close_brace = true
|
||||||
nl_remove_extra_newlines = 0
|
nl_remove_extra_newlines = 0
|
||||||
nl_after_annotation = ignore
|
nl_after_annotation = ignore
|
||||||
nl_between_annotation = ignore
|
nl_between_annotation = ignore
|
||||||
|
nl_before_whole_file_ifdef = 0
|
||||||
|
nl_after_whole_file_ifdef = 0
|
||||||
|
nl_before_whole_file_endif = 0
|
||||||
|
nl_after_whole_file_endif = 0
|
||||||
pos_arith = trail
|
pos_arith = trail
|
||||||
pos_assign = trail
|
pos_assign = trail
|
||||||
pos_bool = trail
|
pos_bool = trail
|
||||||
|
@ -625,6 +641,7 @@ align_asm_colon = false
|
||||||
align_oc_msg_colon_span = 0
|
align_oc_msg_colon_span = 0
|
||||||
align_oc_msg_colon_first = false
|
align_oc_msg_colon_first = false
|
||||||
align_oc_decl_colon = false
|
align_oc_decl_colon = false
|
||||||
|
align_oc_msg_colon_xcode_like = false
|
||||||
cmt_width = 0
|
cmt_width = 0
|
||||||
cmt_reflow_mode = 0
|
cmt_reflow_mode = 0
|
||||||
cmt_convert_tab_to_spaces = false
|
cmt_convert_tab_to_spaces = false
|
||||||
|
@ -673,6 +690,11 @@ mod_sort_case_sensitive = false
|
||||||
mod_sort_import = false
|
mod_sort_import = false
|
||||||
mod_sort_using = false
|
mod_sort_using = false
|
||||||
mod_sort_include = false
|
mod_sort_include = false
|
||||||
|
mod_sort_incl_import_prioritize_filename = false
|
||||||
|
mod_sort_incl_import_prioritize_extensionless = false
|
||||||
|
mod_sort_incl_import_prioritize_angle_over_quotes = false
|
||||||
|
mod_sort_incl_import_ignore_extension = false
|
||||||
|
mod_sort_incl_import_grouping_enabled = false
|
||||||
mod_move_case_break = false
|
mod_move_case_break = false
|
||||||
mod_case_brace = ignore
|
mod_case_brace = ignore
|
||||||
mod_remove_empty_return = false
|
mod_remove_empty_return = false
|
||||||
|
@ -708,9 +730,12 @@ use_indent_continue_only_once = true
|
||||||
indent_cpp_lambda_only_once = true
|
indent_cpp_lambda_only_once = true
|
||||||
use_sp_after_angle_always = false
|
use_sp_after_angle_always = false
|
||||||
use_options_overriding_for_qt_macros = false
|
use_options_overriding_for_qt_macros = false
|
||||||
|
use_form_feed_no_more_as_whitespace_character = false
|
||||||
warn_level_tabs_found_in_verbatim_string_literals = 2
|
warn_level_tabs_found_in_verbatim_string_literals = 2
|
||||||
|
debug_max_number_of_loops = 0
|
||||||
|
debug_line_number_to_protocol = 0
|
||||||
type PtrArrayCompareFunc
|
type PtrArrayCompareFunc
|
||||||
type SIZE_T
|
type SIZE_T
|
||||||
type tr_session
|
type tr_session
|
||||||
# option(s) with 'not default' value: 226
|
# option(s) with 'not default' value: 225
|
||||||
#
|
#
|
|
@ -43,7 +43,7 @@ extern char const* speed_T_str;
|
||||||
{ \
|
{ \
|
||||||
static GQuark q; \
|
static GQuark q; \
|
||||||
\
|
\
|
||||||
if G_UNLIKELY(q == 0) \
|
if (G_UNLIKELY(q == 0)) \
|
||||||
{ \
|
{ \
|
||||||
q = g_quark_from_static_string(#QN); \
|
q = g_quark_from_static_string(#QN); \
|
||||||
} \
|
} \
|
||||||
|
|
|
@ -267,7 +267,7 @@ TEST_F(VariantTest, bencParseAndReencode) {
|
||||||
{ " ", false }
|
{ " ", false }
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const auto& test : tests)
|
for (auto const& test : tests)
|
||||||
{
|
{
|
||||||
tr_variant val;
|
tr_variant val;
|
||||||
char const* end = nullptr;
|
char const* end = nullptr;
|
||||||
|
|
|
@ -5,53 +5,53 @@
|
||||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var transmission,
|
let transmission,
|
||||||
dialog,
|
dialog,
|
||||||
isMobileDevice = RegExp("(iPhone|iPod|Android)").test(navigator.userAgent),
|
isMobileDevice = RegExp('(iPhone|iPod|Android)').test(navigator.userAgent),
|
||||||
scroll_timeout;
|
scroll_timeout;
|
||||||
|
|
||||||
// http://forum.jquery.com/topic/combining-ui-dialog-and-tabs
|
// http://forum.jquery.com/topic/combining-ui-dialog-and-tabs
|
||||||
$.fn.tabbedDialog = function (dialog_opts) {
|
$.fn.tabbedDialog = function (dialog_opts) {
|
||||||
this.tabs({
|
this.tabs({
|
||||||
selected: 0
|
selected: 0,
|
||||||
});
|
});
|
||||||
this.dialog(dialog_opts);
|
this.dialog(dialog_opts);
|
||||||
this.find('.ui-tab-dialog-close').append(this.parent().find('.ui-dialog-titlebar-close'));
|
this.find('.ui-tab-dialog-close').append(this.parent().find('.ui-dialog-titlebar-close'));
|
||||||
this.find('.ui-tab-dialog-close').css({
|
this.find('.ui-tab-dialog-close').css({
|
||||||
'position': 'absolute',
|
position: 'absolute',
|
||||||
'right': '0',
|
right: '0',
|
||||||
'top': '16px'
|
top: '16px',
|
||||||
});
|
});
|
||||||
this.find('.ui-tab-dialog-close > a').css({
|
this.find('.ui-tab-dialog-close > a').css({
|
||||||
'float': 'none',
|
float: 'none',
|
||||||
'padding': '0'
|
padding: '0',
|
||||||
});
|
});
|
||||||
var tabul = this.find('ul:first');
|
const tabul = this.find('ul:first');
|
||||||
this.parent().addClass('ui-tabs').prepend(tabul).draggable('option', 'handle', tabul);
|
this.parent().addClass('ui-tabs').prepend(tabul).draggable('option', 'handle', tabul);
|
||||||
this.siblings('.ui-dialog-titlebar').remove();
|
this.siblings('.ui-dialog-titlebar').remove();
|
||||||
tabul.addClass('ui-dialog-titlebar');
|
tabul.addClass('ui-dialog-titlebar');
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks to see if the content actually changed before poking the DOM.
|
* Checks to see if the content actually changed before poking the DOM.
|
||||||
*/
|
*/
|
||||||
function setInnerHTML(e, html) {
|
function setInnerHTML(e, html) {
|
||||||
if (!e) {
|
if (!e) {
|
||||||
return;
|
return;
|
||||||
};
|
}
|
||||||
|
|
||||||
/* innerHTML is listed as a string, but the browser seems to change it.
|
/* innerHTML is listed as a string, but the browser seems to change it.
|
||||||
* For example, "∞" gets changed to "∞" somewhere down the line.
|
* For example, "∞" gets changed to "∞" somewhere down the line.
|
||||||
* So, let's use an arbitrary different field to test our state... */
|
* So, let's use an arbitrary different field to test our state... */
|
||||||
if (e.currentHTML != html) {
|
if (e.currentHTML != html) {
|
||||||
e.currentHTML = html;
|
e.currentHTML = html;
|
||||||
e.innerHTML = html;
|
e.innerHTML = html;
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
function sanitizeText(text) {
|
function sanitizeText(text) {
|
||||||
return text.replace(/</g, "<").replace(/>/g, ">");
|
return text.replace(/</g, '<').replace(/>/g, '>');
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Many of our text changes are triggered by periodic refreshes
|
* Many of our text changes are triggered by periodic refreshes
|
||||||
|
@ -59,51 +59,51 @@ function sanitizeText(text) {
|
||||||
* so see if the text actually changed before poking the DOM.
|
* so see if the text actually changed before poking the DOM.
|
||||||
*/
|
*/
|
||||||
function setTextContent(e, text) {
|
function setTextContent(e, text) {
|
||||||
if (e && (e.textContent != text)) {
|
if (e && e.textContent != text) {
|
||||||
e.textContent = text;
|
e.textContent = text;
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Given a numerator and denominator, return a ratio string
|
* Given a numerator and denominator, return a ratio string
|
||||||
*/
|
*/
|
||||||
Math.ratio = function (numerator, denominator) {
|
Math.ratio = function (numerator, denominator) {
|
||||||
var result = Math.floor(100 * numerator / denominator) / 100;
|
let result = Math.floor((100 * numerator) / denominator) / 100;
|
||||||
|
|
||||||
// check for special cases
|
// check for special cases
|
||||||
if (result == Number.POSITIVE_INFINITY || result == Number.NEGATIVE_INFINITY) {
|
if (result == Number.POSITIVE_INFINITY || result == Number.NEGATIVE_INFINITY) {
|
||||||
result = -2;
|
result = -2;
|
||||||
} else if (isNaN(result)) {
|
} else if (isNaN(result)) {
|
||||||
result = -1;
|
result = -1;
|
||||||
};
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Round a string of a number to a specified number of decimal places
|
* Round a string of a number to a specified number of decimal places
|
||||||
*/
|
*/
|
||||||
Number.prototype.toTruncFixed = function (place) {
|
Number.prototype.toTruncFixed = function (place) {
|
||||||
var ret = Math.floor(this * Math.pow(10, place)) / Math.pow(10, place);
|
const ret = Math.floor(this * Math.pow(10, place)) / Math.pow(10, place);
|
||||||
return ret.toFixed(place);
|
return ret.toFixed(place);
|
||||||
};
|
};
|
||||||
|
|
||||||
Number.prototype.toStringWithCommas = function () {
|
Number.prototype.toStringWithCommas = function () {
|
||||||
return this.toString().replace(/\B(?=(?:\d{3})+(?!\d))/g, ",");
|
return this.toString().replace(/\B(?=(?:\d{3})+(?!\d))/g, ',');
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Trim whitespace from a string
|
* Trim whitespace from a string
|
||||||
*/
|
*/
|
||||||
String.prototype.trim = function () {
|
String.prototype.trim = function () {
|
||||||
return this.replace(/^\s*/, "").replace(/\s*$/, "");
|
return this.replace(/^\s*/, '').replace(/\s*$/, '');
|
||||||
};
|
};
|
||||||
|
|
||||||
/***
|
/***
|
||||||
**** Preferences
|
**** Preferences
|
||||||
***/
|
***/
|
||||||
|
|
||||||
function Prefs() {};
|
function Prefs() {}
|
||||||
Prefs.prototype = {};
|
Prefs.prototype = {};
|
||||||
|
|
||||||
Prefs._RefreshRate = 'refresh_rate';
|
Prefs._RefreshRate = 'refresh_rate';
|
||||||
|
@ -133,25 +133,25 @@ Prefs._SortByState = 'state';
|
||||||
Prefs._CompactDisplayState = 'compact_display_state';
|
Prefs._CompactDisplayState = 'compact_display_state';
|
||||||
|
|
||||||
Prefs._Defaults = {
|
Prefs._Defaults = {
|
||||||
'filter': 'all',
|
filter: 'all',
|
||||||
'refresh_rate': 5,
|
refresh_rate: 5,
|
||||||
'sort_direction': 'ascending',
|
sort_direction: 'ascending',
|
||||||
'sort_method': 'name',
|
sort_method: 'name',
|
||||||
'turtle-state': false,
|
'turtle-state': false,
|
||||||
'compact_display_state': false
|
compact_display_state: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set a preference option
|
* Set a preference option
|
||||||
*/
|
*/
|
||||||
Prefs.setValue = function (key, val) {
|
Prefs.setValue = function (key, val) {
|
||||||
if (!(key in Prefs._Defaults)) {
|
if (!(key in Prefs._Defaults)) {
|
||||||
console.warn("unrecognized preference key '%s'", key);
|
console.warn("unrecognized preference key '%s'", key);
|
||||||
};
|
}
|
||||||
|
|
||||||
var date = new Date();
|
const date = new Date();
|
||||||
date.setFullYear(date.getFullYear() + 1);
|
date.setFullYear(date.getFullYear() + 1);
|
||||||
document.cookie = key + "=" + val + "; expires=" + date.toGMTString() + "; path=/";
|
document.cookie = key + '=' + val + '; expires=' + date.toGMTString() + '; path=/';
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -161,30 +161,30 @@ Prefs.setValue = function (key, val) {
|
||||||
* @param fallback if the option isn't set, return this instead
|
* @param fallback if the option isn't set, return this instead
|
||||||
*/
|
*/
|
||||||
Prefs.getValue = function (key, fallback) {
|
Prefs.getValue = function (key, fallback) {
|
||||||
var val;
|
let val;
|
||||||
|
|
||||||
if (!(key in Prefs._Defaults)) {
|
if (!(key in Prefs._Defaults)) {
|
||||||
console.warn("unrecognized preference key '%s'", key);
|
console.warn("unrecognized preference key '%s'", key);
|
||||||
};
|
}
|
||||||
|
|
||||||
var lines = document.cookie.split(';');
|
const lines = document.cookie.split(';');
|
||||||
for (var i = 0, len = lines.length; !val && i < len; ++i) {
|
for (let i = 0, len = lines.length; !val && i < len; ++i) {
|
||||||
var line = lines[i].trim();
|
const line = lines[i].trim();
|
||||||
var delim = line.indexOf('=');
|
const delim = line.indexOf('=');
|
||||||
if ((delim === key.length) && line.indexOf(key) === 0) {
|
if (delim === key.length && line.indexOf(key) === 0) {
|
||||||
val = line.substring(delim + 1);
|
val = line.substring(delim + 1);
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// FIXME: we support strings and booleans... add number support too?
|
// FIXME: we support strings and booleans... add number support too?
|
||||||
if (!val) {
|
if (!val) {
|
||||||
val = fallback;
|
val = fallback;
|
||||||
} else if (val === 'true') {
|
} else if (val === 'true') {
|
||||||
val = true;
|
val = true;
|
||||||
} else if (val === 'false') {
|
} else if (val === 'false') {
|
||||||
val = false;
|
val = false;
|
||||||
};
|
}
|
||||||
return val;
|
return val;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -193,75 +193,48 @@ Prefs.getValue = function (key, fallback) {
|
||||||
* @pararm o object to be populated (optional)
|
* @pararm o object to be populated (optional)
|
||||||
*/
|
*/
|
||||||
Prefs.getClutchPrefs = function (o) {
|
Prefs.getClutchPrefs = function (o) {
|
||||||
if (!o) {
|
if (!o) {
|
||||||
o = {};
|
o = {};
|
||||||
};
|
}
|
||||||
for (var key in Prefs._Defaults) {
|
for (const key in Prefs._Defaults) {
|
||||||
o[key] = Prefs.getValue(key, Prefs._Defaults[key]);
|
o[key] = Prefs.getValue(key, Prefs._Defaults[key]);
|
||||||
};
|
}
|
||||||
return o;
|
return o;
|
||||||
};
|
};
|
||||||
|
|
||||||
// forceNumeric() plug-in implementation
|
// forceNumeric() plug-in implementation
|
||||||
jQuery.fn.forceNumeric = function () {
|
jQuery.fn.forceNumeric = function () {
|
||||||
return this.each(function () {
|
return this.each(function () {
|
||||||
$(this).keydown(function (e) {
|
$(this).keydown(function (e) {
|
||||||
var key = e.which || e.keyCode;
|
const key = e.which || e.keyCode;
|
||||||
return !e.shiftKey && !e.altKey && !e.ctrlKey &&
|
return (
|
||||||
// numbers
|
(!e.shiftKey &&
|
||||||
key >= 48 && key <= 57 ||
|
!e.altKey &&
|
||||||
// Numeric keypad
|
!e.ctrlKey &&
|
||||||
key >= 96 && key <= 105 ||
|
// numbers
|
||||||
// comma, period and minus, . on keypad
|
key >= 48 &&
|
||||||
key === 190 || key === 188 || key === 109 || key === 110 ||
|
key <= 57) ||
|
||||||
// Backspace and Tab and Enter
|
// Numeric keypad
|
||||||
key === 8 || key === 9 || key === 13 ||
|
(key >= 96 && key <= 105) ||
|
||||||
// Home and End
|
// comma, period and minus, . on keypad
|
||||||
key === 35 || key === 36 ||
|
key === 190 ||
|
||||||
// left and right arrows
|
key === 188 ||
|
||||||
key === 37 || key === 39 ||
|
key === 109 ||
|
||||||
// Del and Ins
|
key === 110 ||
|
||||||
key === 46 || key === 45;
|
// Backspace and Tab and Enter
|
||||||
});
|
key === 8 ||
|
||||||
|
key === 9 ||
|
||||||
|
key === 13 ||
|
||||||
|
// Home and End
|
||||||
|
key === 35 ||
|
||||||
|
key === 36 ||
|
||||||
|
// left and right arrows
|
||||||
|
key === 37 ||
|
||||||
|
key === 39 ||
|
||||||
|
// Del and Ins
|
||||||
|
key === 46 ||
|
||||||
|
key === 45
|
||||||
|
);
|
||||||
});
|
});
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* http://blog.stevenlevithan.com/archives/parseuri
|
|
||||||
*
|
|
||||||
* parseUri 1.2.2
|
|
||||||
* (c) Steven Levithan <stevenlevithan.com>
|
|
||||||
* MIT License
|
|
||||||
*/
|
|
||||||
function parseUri(str) {
|
|
||||||
var o = parseUri.options;
|
|
||||||
var m = o.parser[o.strictMode ? "strict" : "loose"].exec(str);
|
|
||||||
var uri = {};
|
|
||||||
var i = 14;
|
|
||||||
|
|
||||||
while (i--) {
|
|
||||||
uri[o.key[i]] = m[i] || "";
|
|
||||||
};
|
|
||||||
|
|
||||||
uri[o.q.name] = {};
|
|
||||||
uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
|
|
||||||
if ($1) {
|
|
||||||
uri[o.q.name][$1] = $2;
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return uri;
|
|
||||||
};
|
|
||||||
|
|
||||||
parseUri.options = {
|
|
||||||
strictMode: false,
|
|
||||||
key: ["source", "protocol", "authority", "userInfo", "user", "password", "host", "port", "relative", "path", "directory", "file", "query", "anchor"],
|
|
||||||
q: {
|
|
||||||
name: "queryKey",
|
|
||||||
parser: /(?:^|&)([^&=]*)=?([^&]*)/g
|
|
||||||
},
|
|
||||||
parser: {
|
|
||||||
strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
|
|
||||||
loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,113 +6,124 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function Dialog() {
|
function Dialog() {
|
||||||
this.initialize();
|
this.initialize();
|
||||||
};
|
}
|
||||||
|
|
||||||
Dialog.prototype = {
|
Dialog.prototype = {
|
||||||
|
/*
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
initialize: function () {
|
||||||
/*
|
/*
|
||||||
* Constructor
|
* Private Interface Variables
|
||||||
*/
|
*/
|
||||||
initialize: function () {
|
this._container = $('#dialog_container');
|
||||||
|
this._heading = $('#dialog_heading');
|
||||||
|
this._message = $('#dialog_message');
|
||||||
|
this._cancel_button = $('#dialog_cancel_button');
|
||||||
|
this._confirm_button = $('#dialog_confirm_button');
|
||||||
|
this._callback = null;
|
||||||
|
|
||||||
/*
|
// Observe the buttons
|
||||||
* Private Interface Variables
|
this._cancel_button.bind(
|
||||||
*/
|
'click',
|
||||||
this._container = $('#dialog_container');
|
{
|
||||||
this._heading = $('#dialog_heading');
|
dialog: this,
|
||||||
this._message = $('#dialog_message');
|
},
|
||||||
this._cancel_button = $('#dialog_cancel_button');
|
this.onCancelClicked
|
||||||
this._confirm_button = $('#dialog_confirm_button');
|
);
|
||||||
this._callback = null;
|
this._confirm_button.bind(
|
||||||
|
'click',
|
||||||
|
{
|
||||||
|
dialog: this,
|
||||||
|
},
|
||||||
|
this.onConfirmClicked
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
// Observe the buttons
|
/*--------------------------------------------
|
||||||
this._cancel_button.bind('click', {
|
*
|
||||||
dialog: this
|
* E V E N T F U N C T I O N S
|
||||||
}, this.onCancelClicked);
|
*
|
||||||
this._confirm_button.bind('click', {
|
*--------------------------------------------*/
|
||||||
dialog: this
|
|
||||||
}, this.onConfirmClicked);
|
|
||||||
},
|
|
||||||
|
|
||||||
/*--------------------------------------------
|
executeCallback: function () {
|
||||||
*
|
this._callback();
|
||||||
* E V E N T F U N C T I O N S
|
dialog.hideDialog();
|
||||||
*
|
},
|
||||||
*--------------------------------------------*/
|
|
||||||
|
|
||||||
executeCallback: function () {
|
hideDialog: function () {
|
||||||
this._callback();
|
$('body.dialog_showing').removeClass('dialog_showing');
|
||||||
dialog.hideDialog();
|
this._container.hide();
|
||||||
},
|
transmission.hideMobileAddressbar();
|
||||||
|
transmission.updateButtonStates();
|
||||||
|
},
|
||||||
|
|
||||||
hideDialog: function () {
|
isVisible: function () {
|
||||||
$('body.dialog_showing').removeClass('dialog_showing');
|
return this._container.is(':visible');
|
||||||
this._container.hide();
|
},
|
||||||
transmission.hideMobileAddressbar();
|
|
||||||
transmission.updateButtonStates();
|
|
||||||
},
|
|
||||||
|
|
||||||
isVisible: function () {
|
onCancelClicked: function (event) {
|
||||||
return this._container.is(':visible');
|
event.data.dialog.hideDialog();
|
||||||
},
|
},
|
||||||
|
|
||||||
onCancelClicked: function (event) {
|
onConfirmClicked: function (event) {
|
||||||
event.data.dialog.hideDialog();
|
event.data.dialog.executeCallback();
|
||||||
},
|
},
|
||||||
|
|
||||||
onConfirmClicked: function (event) {
|
/*--------------------------------------------
|
||||||
event.data.dialog.executeCallback();
|
*
|
||||||
},
|
* I N T E R F A C E F U N C T I O N S
|
||||||
|
*
|
||||||
|
*--------------------------------------------*/
|
||||||
|
|
||||||
/*--------------------------------------------
|
/*
|
||||||
*
|
* Display a confirm dialog
|
||||||
* I N T E R F A C E F U N C T I O N S
|
*/
|
||||||
*
|
confirm: function (
|
||||||
*--------------------------------------------*/
|
dialog_heading,
|
||||||
|
dialog_message,
|
||||||
/*
|
confirm_button_label,
|
||||||
* Display a confirm dialog
|
callback,
|
||||||
*/
|
cancel_button_label
|
||||||
confirm: function (dialog_heading, dialog_message, confirm_button_label,
|
) {
|
||||||
callback, cancel_button_label) {
|
if (!isMobileDevice) {
|
||||||
if (!isMobileDevice) {
|
$('.dialog_container').hide();
|
||||||
$('.dialog_container').hide();
|
|
||||||
};
|
|
||||||
setTextContent(this._heading[0], dialog_heading);
|
|
||||||
setTextContent(this._message[0], dialog_message);
|
|
||||||
setTextContent(this._cancel_button[0], cancel_button_label || 'Cancel');
|
|
||||||
setTextContent(this._confirm_button[0], confirm_button_label);
|
|
||||||
this._confirm_button.show();
|
|
||||||
this._callback = callback;
|
|
||||||
$('body').addClass('dialog_showing');
|
|
||||||
this._container.show();
|
|
||||||
transmission.updateButtonStates();
|
|
||||||
if (isMobileDevice) {
|
|
||||||
transmission.hideMobileAddressbar();
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Display an alert dialog
|
|
||||||
*/
|
|
||||||
alert: function (dialog_heading, dialog_message, cancel_button_label) {
|
|
||||||
if (!isMobileDevice) {
|
|
||||||
$('.dialog_container').hide();
|
|
||||||
};
|
|
||||||
setTextContent(this._heading[0], dialog_heading);
|
|
||||||
setTextContent(this._message[0], dialog_message);
|
|
||||||
// jquery::hide() doesn't work here in Safari for some odd reason
|
|
||||||
this._confirm_button.css('display', 'none');
|
|
||||||
setTextContent(this._cancel_button[0], cancel_button_label);
|
|
||||||
// Just in case
|
|
||||||
$('#upload_container').hide();
|
|
||||||
$('#move_container').hide();
|
|
||||||
$('body').addClass('dialog_showing');
|
|
||||||
transmission.updateButtonStates();
|
|
||||||
if (isMobileDevice) {
|
|
||||||
transmission.hideMobileAddressbar();
|
|
||||||
};
|
|
||||||
this._container.show();
|
|
||||||
}
|
}
|
||||||
|
setTextContent(this._heading[0], dialog_heading);
|
||||||
|
setTextContent(this._message[0], dialog_message);
|
||||||
|
setTextContent(this._cancel_button[0], cancel_button_label || 'Cancel');
|
||||||
|
setTextContent(this._confirm_button[0], confirm_button_label);
|
||||||
|
this._confirm_button.show();
|
||||||
|
this._callback = callback;
|
||||||
|
$('body').addClass('dialog_showing');
|
||||||
|
this._container.show();
|
||||||
|
transmission.updateButtonStates();
|
||||||
|
if (isMobileDevice) {
|
||||||
|
transmission.hideMobileAddressbar();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Display an alert dialog
|
||||||
|
*/
|
||||||
|
alert: function (dialog_heading, dialog_message, cancel_button_label) {
|
||||||
|
if (!isMobileDevice) {
|
||||||
|
$('.dialog_container').hide();
|
||||||
|
}
|
||||||
|
setTextContent(this._heading[0], dialog_heading);
|
||||||
|
setTextContent(this._message[0], dialog_message);
|
||||||
|
// jquery::hide() doesn't work here in Safari for some odd reason
|
||||||
|
this._confirm_button.css('display', 'none');
|
||||||
|
setTextContent(this._cancel_button[0], cancel_button_label);
|
||||||
|
// Just in case
|
||||||
|
$('#upload_container').hide();
|
||||||
|
$('#move_container').hide();
|
||||||
|
$('body').addClass('dialog_showing');
|
||||||
|
transmission.updateButtonStates();
|
||||||
|
if (isMobileDevice) {
|
||||||
|
transmission.hideMobileAddressbar();
|
||||||
|
}
|
||||||
|
this._container.show();
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,200 +6,207 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function FileRow(torrent, depth, name, indices, even) {
|
function FileRow(torrent, depth, name, indices, even) {
|
||||||
var fields = {
|
const fields = {
|
||||||
have: 0,
|
have: 0,
|
||||||
indices: [],
|
indices: [],
|
||||||
isWanted: true,
|
isWanted: true,
|
||||||
priorityLow: false,
|
priorityLow: false,
|
||||||
priorityNormal: false,
|
priorityNormal: false,
|
||||||
priorityHigh: false,
|
priorityHigh: false,
|
||||||
me: this,
|
me: this,
|
||||||
size: 0,
|
size: 0,
|
||||||
torrent: null
|
torrent: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
var elements = {
|
const elements = {
|
||||||
priority_low_button: null,
|
priority_low_button: null,
|
||||||
priority_normal_button: null,
|
priority_normal_button: null,
|
||||||
priority_high_button: null,
|
priority_high_button: null,
|
||||||
progress: null,
|
progress: null,
|
||||||
root: null
|
root: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
var initialize = function (torrent, depth, name, indices, even) {
|
const initialize = function (torrent, depth, name, indices, even) {
|
||||||
fields.torrent = torrent;
|
fields.torrent = torrent;
|
||||||
fields.indices = indices;
|
fields.indices = indices;
|
||||||
createRow(torrent, depth, name, even);
|
createRow(torrent, depth, name, even);
|
||||||
};
|
};
|
||||||
|
|
||||||
var refreshWantedHTML = function () {
|
const refreshWantedHTML = function () {
|
||||||
var e = $(elements.root);
|
const e = $(elements.root);
|
||||||
e.toggleClass('skip', !fields.isWanted);
|
e.toggleClass('skip', !fields.isWanted);
|
||||||
e.toggleClass('complete', isDone());
|
e.toggleClass('complete', isDone());
|
||||||
$(e[0].checkbox).prop('disabled', !isEditable());
|
$(e[0].checkbox).prop('disabled', !isEditable());
|
||||||
$(e[0].checkbox).prop('checked', fields.isWanted);
|
$(e[0].checkbox).prop('checked', fields.isWanted);
|
||||||
};
|
};
|
||||||
|
|
||||||
var refreshProgressHTML = function () {
|
const refreshProgressHTML = function () {
|
||||||
var pct = 100 * (fields.size ? (fields.have / fields.size) : 1.0)
|
const pct = 100 * (fields.size ? fields.have / fields.size : 1.0);
|
||||||
var c = [Transmission.fmt.size(fields.have), ' of ', Transmission.fmt.size(fields.size), ' (', Transmission.fmt.percentString(pct), '%)'].join('');
|
const c = [
|
||||||
setTextContent(elements.progress, c);
|
Transmission.fmt.size(fields.have),
|
||||||
};
|
' of ',
|
||||||
|
Transmission.fmt.size(fields.size),
|
||||||
|
' (',
|
||||||
|
Transmission.fmt.percentString(pct),
|
||||||
|
'%)',
|
||||||
|
].join('');
|
||||||
|
setTextContent(elements.progress, c);
|
||||||
|
};
|
||||||
|
|
||||||
var refreshImpl = function () {
|
const refreshImpl = function () {
|
||||||
var i,
|
let i,
|
||||||
file,
|
file,
|
||||||
have = 0,
|
have = 0,
|
||||||
size = 0,
|
size = 0,
|
||||||
wanted = false,
|
wanted = false,
|
||||||
low = false,
|
low = false,
|
||||||
normal = false,
|
normal = false,
|
||||||
high = false;
|
high = false;
|
||||||
|
|
||||||
// loop through the file_indices that affect this row
|
// loop through the file_indices that affect this row
|
||||||
for (i = 0; i < fields.indices.length; ++i) {
|
for (i = 0; i < fields.indices.length; ++i) {
|
||||||
file = fields.torrent.getFile(fields.indices[i]);
|
file = fields.torrent.getFile(fields.indices[i]);
|
||||||
have += file.bytesCompleted;
|
have += file.bytesCompleted;
|
||||||
size += file.length;
|
size += file.length;
|
||||||
wanted |= file.wanted;
|
wanted |= file.wanted;
|
||||||
switch (file.priority) {
|
switch (file.priority) {
|
||||||
case -1:
|
case -1:
|
||||||
low = true;
|
low = true;
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
normal = true;
|
normal = true;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
high = true;
|
high = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((fields.have != have) || (fields.size != size)) {
|
if (fields.have != have || fields.size != size) {
|
||||||
fields.have = have;
|
fields.have = have;
|
||||||
fields.size = size;
|
fields.size = size;
|
||||||
refreshProgressHTML();
|
refreshProgressHTML();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fields.isWanted !== wanted) {
|
if (fields.isWanted !== wanted) {
|
||||||
fields.isWanted = wanted;
|
fields.isWanted = wanted;
|
||||||
refreshWantedHTML();
|
refreshWantedHTML();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fields.priorityLow !== low) {
|
if (fields.priorityLow !== low) {
|
||||||
fields.priorityLow = low;
|
fields.priorityLow = low;
|
||||||
$(elements.priority_low_button).toggleClass('selected', low);
|
$(elements.priority_low_button).toggleClass('selected', low);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fields.priorityNormal !== normal) {
|
if (fields.priorityNormal !== normal) {
|
||||||
fields.priorityNormal = normal;
|
fields.priorityNormal = normal;
|
||||||
$(elements.priority_normal_button).toggleClass('selected', normal);
|
$(elements.priority_normal_button).toggleClass('selected', normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fields.priorityHigh !== high) {
|
if (fields.priorityHigh !== high) {
|
||||||
fields.priorityHigh = high;
|
fields.priorityHigh = high;
|
||||||
$(elements.priority_high_button).toggleClass('selected', high);
|
$(elements.priority_high_button).toggleClass('selected', high);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var isDone = function () {
|
var isDone = function () {
|
||||||
return fields.have >= fields.size;
|
return fields.have >= fields.size;
|
||||||
};
|
};
|
||||||
|
|
||||||
var isEditable = function () {
|
var isEditable = function () {
|
||||||
return (fields.torrent.getFileCount() > 1) && !isDone();
|
return fields.torrent.getFileCount() > 1 && !isDone();
|
||||||
};
|
};
|
||||||
|
|
||||||
var createRow = function (torrent, depth, name, even) {
|
var createRow = function (torrent, depth, name, even) {
|
||||||
var e, root, box;
|
let e, root, box;
|
||||||
|
|
||||||
root = document.createElement('li');
|
root = document.createElement('li');
|
||||||
root.className = 'inspector_torrent_file_list_entry' + (even ? 'even' : 'odd');
|
root.className = 'inspector_torrent_file_list_entry' + (even ? 'even' : 'odd');
|
||||||
elements.root = root;
|
elements.root = root;
|
||||||
|
|
||||||
e = document.createElement('input');
|
e = document.createElement('input');
|
||||||
e.type = 'checkbox';
|
e.type = 'checkbox';
|
||||||
e.className = "file_wanted_control";
|
e.className = 'file_wanted_control';
|
||||||
e.title = 'Download file';
|
e.title = 'Download file';
|
||||||
$(e).change(function (ev) {
|
$(e).change(function (ev) {
|
||||||
fireWantedChanged($(ev.currentTarget).prop('checked'));
|
fireWantedChanged($(ev.currentTarget).prop('checked'));
|
||||||
});
|
});
|
||||||
root.checkbox = e;
|
root.checkbox = e;
|
||||||
root.appendChild(e);
|
root.appendChild(e);
|
||||||
|
|
||||||
e = document.createElement('div');
|
e = document.createElement('div');
|
||||||
e.className = 'file-priority-radiobox';
|
e.className = 'file-priority-radiobox';
|
||||||
box = e;
|
box = e;
|
||||||
|
|
||||||
e = document.createElement('div');
|
e = document.createElement('div');
|
||||||
e.className = 'low';
|
e.className = 'low';
|
||||||
e.title = 'Low Priority';
|
e.title = 'Low Priority';
|
||||||
$(e).click(function () {
|
$(e).click(function () {
|
||||||
firePriorityChanged(-1);
|
firePriorityChanged(-1);
|
||||||
});
|
});
|
||||||
elements.priority_low_button = e;
|
elements.priority_low_button = e;
|
||||||
box.appendChild(e);
|
box.appendChild(e);
|
||||||
|
|
||||||
e = document.createElement('div');
|
e = document.createElement('div');
|
||||||
e.className = 'normal';
|
e.className = 'normal';
|
||||||
e.title = 'Normal Priority';
|
e.title = 'Normal Priority';
|
||||||
$(e).click(function () {
|
$(e).click(function () {
|
||||||
firePriorityChanged(0);
|
firePriorityChanged(0);
|
||||||
});
|
});
|
||||||
elements.priority_normal_button = e;
|
elements.priority_normal_button = e;
|
||||||
box.appendChild(e);
|
box.appendChild(e);
|
||||||
|
|
||||||
e = document.createElement('div');
|
e = document.createElement('div');
|
||||||
e.title = 'High Priority';
|
e.title = 'High Priority';
|
||||||
e.className = 'high';
|
e.className = 'high';
|
||||||
$(e).click(function () {
|
$(e).click(function () {
|
||||||
firePriorityChanged(1);
|
firePriorityChanged(1);
|
||||||
});
|
});
|
||||||
elements.priority_high_button = e;
|
elements.priority_high_button = e;
|
||||||
box.appendChild(e);
|
box.appendChild(e);
|
||||||
|
|
||||||
root.appendChild(box);
|
root.appendChild(box);
|
||||||
|
|
||||||
e = document.createElement('div');
|
e = document.createElement('div');
|
||||||
e.className = "inspector_torrent_file_list_entry_name";
|
e.className = 'inspector_torrent_file_list_entry_name';
|
||||||
setTextContent(e, name);
|
setTextContent(e, name);
|
||||||
$(e).click(fireNameClicked);
|
$(e).click(fireNameClicked);
|
||||||
root.appendChild(e);
|
root.appendChild(e);
|
||||||
|
|
||||||
e = document.createElement('div');
|
e = document.createElement('div');
|
||||||
e.className = "inspector_torrent_file_list_entry_progress";
|
e.className = 'inspector_torrent_file_list_entry_progress';
|
||||||
root.appendChild(e);
|
root.appendChild(e);
|
||||||
$(e).click(fireNameClicked);
|
$(e).click(fireNameClicked);
|
||||||
elements.progress = e;
|
elements.progress = e;
|
||||||
|
|
||||||
$(root).css('margin-left', '' + (depth * 16) + 'px');
|
$(root).css('margin-left', '' + depth * 16 + 'px');
|
||||||
|
|
||||||
refreshImpl();
|
refreshImpl();
|
||||||
return root;
|
return root;
|
||||||
};
|
};
|
||||||
|
|
||||||
var fireWantedChanged = function (do_want) {
|
var fireWantedChanged = function (do_want) {
|
||||||
$(fields.me).trigger('wantedToggled', [fields.indices, do_want]);
|
$(fields.me).trigger('wantedToggled', [fields.indices, do_want]);
|
||||||
};
|
};
|
||||||
|
|
||||||
var firePriorityChanged = function (priority) {
|
var firePriorityChanged = function (priority) {
|
||||||
$(fields.me).trigger('priorityToggled', [fields.indices, priority]);
|
$(fields.me).trigger('priorityToggled', [fields.indices, priority]);
|
||||||
};
|
};
|
||||||
|
|
||||||
var fireNameClicked = function () {
|
var fireNameClicked = function () {
|
||||||
$(fields.me).trigger('nameClicked', [fields.me, fields.indices]);
|
$(fields.me).trigger('nameClicked', [fields.me, fields.indices]);
|
||||||
};
|
};
|
||||||
|
|
||||||
/***
|
/***
|
||||||
**** PUBLIC
|
**** PUBLIC
|
||||||
***/
|
***/
|
||||||
|
|
||||||
this.getElement = function () {
|
this.getElement = function () {
|
||||||
return elements.root;
|
return elements.root;
|
||||||
};
|
};
|
||||||
this.refresh = function () {
|
this.refresh = function () {
|
||||||
refreshImpl();
|
refreshImpl();
|
||||||
};
|
};
|
||||||
|
|
||||||
initialize(torrent, depth, name, indices, even);
|
initialize(torrent, depth, name, indices, even);
|
||||||
};
|
}
|
||||||
|
|
|
@ -6,287 +6,296 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Transmission.fmt = (function () {
|
Transmission.fmt = (function () {
|
||||||
var speed_K = 1000;
|
const speed_K = 1000;
|
||||||
var speed_K_str = 'kB/s';
|
const speed_K_str = 'kB/s';
|
||||||
var speed_M_str = 'MB/s';
|
const speed_M_str = 'MB/s';
|
||||||
var speed_G_str = 'GB/s';
|
const speed_G_str = 'GB/s';
|
||||||
|
|
||||||
var size_K = 1000;
|
const size_K = 1000;
|
||||||
var size_B_str = 'B';
|
const size_B_str = 'B';
|
||||||
var size_K_str = 'kB';
|
const size_K_str = 'kB';
|
||||||
var size_M_str = 'MB';
|
const size_M_str = 'MB';
|
||||||
var size_G_str = 'GB';
|
const size_G_str = 'GB';
|
||||||
var size_T_str = 'TB';
|
const size_T_str = 'TB';
|
||||||
|
|
||||||
var mem_K = 1024;
|
const mem_K = 1024;
|
||||||
var mem_B_str = 'B';
|
const mem_B_str = 'B';
|
||||||
var mem_K_str = 'KiB';
|
const mem_K_str = 'KiB';
|
||||||
var mem_M_str = 'MiB';
|
const mem_M_str = 'MiB';
|
||||||
var mem_G_str = 'GiB';
|
const mem_G_str = 'GiB';
|
||||||
var mem_T_str = 'TiB';
|
const mem_T_str = 'TiB';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
/*
|
||||||
|
* Format a percentage to a string
|
||||||
|
*/
|
||||||
|
percentString: function (x) {
|
||||||
|
if (x < 10.0) {
|
||||||
|
return x.toTruncFixed(2);
|
||||||
|
} else if (x < 100.0) {
|
||||||
|
return x.toTruncFixed(1);
|
||||||
|
} else {
|
||||||
|
return x.toTruncFixed(0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Format a percentage to a string
|
* Format a ratio to a string
|
||||||
*/
|
*/
|
||||||
percentString: function (x) {
|
ratioString: function (x) {
|
||||||
if (x < 10.0) {
|
if (x === -1) {
|
||||||
return x.toTruncFixed(2);
|
return 'None';
|
||||||
} else if (x < 100.0) {
|
}
|
||||||
return x.toTruncFixed(1);
|
if (x === -2) {
|
||||||
} else {
|
return '∞';
|
||||||
return x.toTruncFixed(0);
|
}
|
||||||
}
|
return this.percentString(x);
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Format a ratio to a string
|
* Formats the a memory size into a human-readable string
|
||||||
*/
|
* @param {Number} bytes the filesize in bytes
|
||||||
ratioString: function (x) {
|
* @return {String} human-readable string
|
||||||
if (x === -1) {
|
*/
|
||||||
return "None";
|
mem: function (bytes) {
|
||||||
}
|
if (bytes < mem_K) {
|
||||||
if (x === -2) {
|
return [bytes, mem_B_str].join(' ');
|
||||||
return '∞';
|
}
|
||||||
}
|
|
||||||
return this.percentString(x);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
let convertedSize;
|
||||||
* Formats the a memory size into a human-readable string
|
let unit;
|
||||||
* @param {Number} bytes the filesize in bytes
|
|
||||||
* @return {String} human-readable string
|
|
||||||
*/
|
|
||||||
mem: function (bytes) {
|
|
||||||
if (bytes < mem_K)
|
|
||||||
return [bytes, mem_B_str].join(' ');
|
|
||||||
|
|
||||||
var convertedSize;
|
if (bytes < Math.pow(mem_K, 2)) {
|
||||||
var unit;
|
convertedSize = bytes / mem_K;
|
||||||
|
unit = mem_K_str;
|
||||||
|
} else if (bytes < Math.pow(mem_K, 3)) {
|
||||||
|
convertedSize = bytes / Math.pow(mem_K, 2);
|
||||||
|
unit = mem_M_str;
|
||||||
|
} else if (bytes < Math.pow(mem_K, 4)) {
|
||||||
|
convertedSize = bytes / Math.pow(mem_K, 3);
|
||||||
|
unit = mem_G_str;
|
||||||
|
} else {
|
||||||
|
convertedSize = bytes / Math.pow(mem_K, 4);
|
||||||
|
unit = mem_T_str;
|
||||||
|
}
|
||||||
|
|
||||||
if (bytes < Math.pow(mem_K, 2)) {
|
// try to have at least 3 digits and at least 1 decimal
|
||||||
convertedSize = bytes / mem_K;
|
return convertedSize <= 9.995
|
||||||
unit = mem_K_str;
|
? [convertedSize.toTruncFixed(2), unit].join(' ')
|
||||||
} else if (bytes < Math.pow(mem_K, 3)) {
|
: [convertedSize.toTruncFixed(1), unit].join(' ');
|
||||||
convertedSize = bytes / Math.pow(mem_K, 2);
|
},
|
||||||
unit = mem_M_str;
|
|
||||||
} else if (bytes < Math.pow(mem_K, 4)) {
|
|
||||||
convertedSize = bytes / Math.pow(mem_K, 3);
|
|
||||||
unit = mem_G_str;
|
|
||||||
} else {
|
|
||||||
convertedSize = bytes / Math.pow(mem_K, 4);
|
|
||||||
unit = mem_T_str;
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to have at least 3 digits and at least 1 decimal
|
/**
|
||||||
return convertedSize <= 9.995 ? [convertedSize.toTruncFixed(2), unit].join(' ') : [convertedSize.toTruncFixed(1), unit].join(' ');
|
* Formats the a disk capacity or file size into a human-readable string
|
||||||
},
|
* @param {Number} bytes the filesize in bytes
|
||||||
|
* @return {String} human-readable string
|
||||||
|
*/
|
||||||
|
size: function (bytes) {
|
||||||
|
if (bytes < size_K) {
|
||||||
|
return [bytes, size_B_str].join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
let convertedSize;
|
||||||
* Formats the a disk capacity or file size into a human-readable string
|
let unit;
|
||||||
* @param {Number} bytes the filesize in bytes
|
|
||||||
* @return {String} human-readable string
|
|
||||||
*/
|
|
||||||
size: function (bytes) {
|
|
||||||
if (bytes < size_K) {
|
|
||||||
return [bytes, size_B_str].join(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
var convertedSize;
|
if (bytes < Math.pow(size_K, 2)) {
|
||||||
var unit;
|
convertedSize = bytes / size_K;
|
||||||
|
unit = size_K_str;
|
||||||
|
} else if (bytes < Math.pow(size_K, 3)) {
|
||||||
|
convertedSize = bytes / Math.pow(size_K, 2);
|
||||||
|
unit = size_M_str;
|
||||||
|
} else if (bytes < Math.pow(size_K, 4)) {
|
||||||
|
convertedSize = bytes / Math.pow(size_K, 3);
|
||||||
|
unit = size_G_str;
|
||||||
|
} else {
|
||||||
|
convertedSize = bytes / Math.pow(size_K, 4);
|
||||||
|
unit = size_T_str;
|
||||||
|
}
|
||||||
|
|
||||||
if (bytes < Math.pow(size_K, 2)) {
|
// try to have at least 3 digits and at least 1 decimal
|
||||||
convertedSize = bytes / size_K;
|
return convertedSize <= 9.995
|
||||||
unit = size_K_str;
|
? [convertedSize.toTruncFixed(2), unit].join(' ')
|
||||||
} else if (bytes < Math.pow(size_K, 3)) {
|
: [convertedSize.toTruncFixed(1), unit].join(' ');
|
||||||
convertedSize = bytes / Math.pow(size_K, 2);
|
},
|
||||||
unit = size_M_str;
|
|
||||||
} else if (bytes < Math.pow(size_K, 4)) {
|
|
||||||
convertedSize = bytes / Math.pow(size_K, 3);
|
|
||||||
unit = size_G_str;
|
|
||||||
} else {
|
|
||||||
convertedSize = bytes / Math.pow(size_K, 4);
|
|
||||||
unit = size_T_str;
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to have at least 3 digits and at least 1 decimal
|
speedBps: function (Bps) {
|
||||||
return convertedSize <= 9.995 ? [convertedSize.toTruncFixed(2), unit].join(' ') : [convertedSize.toTruncFixed(1), unit].join(' ');
|
return this.speed(this.toKBps(Bps));
|
||||||
},
|
},
|
||||||
|
|
||||||
speedBps: function (Bps) {
|
toKBps: function (Bps) {
|
||||||
return this.speed(this.toKBps(Bps));
|
return Math.floor(Bps / speed_K);
|
||||||
},
|
},
|
||||||
|
|
||||||
toKBps: function (Bps) {
|
speed: function (KBps) {
|
||||||
return Math.floor(Bps / speed_K);
|
let speed = KBps;
|
||||||
},
|
|
||||||
|
|
||||||
speed: function (KBps) {
|
if (speed <= 999.95) {
|
||||||
var speed = KBps;
|
// 0 KBps to 999 K
|
||||||
|
return [speed.toTruncFixed(0), speed_K_str].join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
if (speed <= 999.95) { // 0 KBps to 999 K
|
speed /= speed_K;
|
||||||
return [speed.toTruncFixed(0), speed_K_str].join(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
speed /= speed_K;
|
if (speed <= 99.995) {
|
||||||
|
// 1 M to 99.99 M
|
||||||
|
return [speed.toTruncFixed(2), speed_M_str].join(' ');
|
||||||
|
}
|
||||||
|
if (speed <= 999.95) {
|
||||||
|
// 100 M to 999.9 M
|
||||||
|
return [speed.toTruncFixed(1), speed_M_str].join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
if (speed <= 99.995) { // 1 M to 99.99 M
|
// insane speeds
|
||||||
return [speed.toTruncFixed(2), speed_M_str].join(' ');
|
speed /= speed_K;
|
||||||
}
|
return [speed.toTruncFixed(2), speed_G_str].join(' ');
|
||||||
if (speed <= 999.95) { // 100 M to 999.9 M
|
},
|
||||||
return [speed.toTruncFixed(1), speed_M_str].join(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
// insane speeds
|
timeInterval: function (seconds) {
|
||||||
speed /= speed_K;
|
const days = Math.floor(seconds / 86400);
|
||||||
return [speed.toTruncFixed(2), speed_G_str].join(' ');
|
const hours = Math.floor((seconds % 86400) / 3600);
|
||||||
},
|
const minutes = Math.floor((seconds % 3600) / 60);
|
||||||
|
seconds = Math.floor(seconds % 60);
|
||||||
|
const d = days + ' ' + (days > 1 ? 'days' : 'day');
|
||||||
|
const h = hours + ' ' + (hours > 1 ? 'hours' : 'hour');
|
||||||
|
const m = minutes + ' ' + (minutes > 1 ? 'minutes' : 'minute');
|
||||||
|
const s = seconds + ' ' + (seconds > 1 ? 'seconds' : 'second');
|
||||||
|
|
||||||
timeInterval: function (seconds) {
|
if (days) {
|
||||||
var days = Math.floor(seconds / 86400),
|
if (days >= 4 || !hours) {
|
||||||
hours = Math.floor((seconds % 86400) / 3600),
|
return d;
|
||||||
minutes = Math.floor((seconds % 3600) / 60),
|
|
||||||
seconds = Math.floor(seconds % 60),
|
|
||||||
d = days + ' ' + (days > 1 ? 'days' : 'day'),
|
|
||||||
h = hours + ' ' + (hours > 1 ? 'hours' : 'hour'),
|
|
||||||
m = minutes + ' ' + (minutes > 1 ? 'minutes' : 'minute'),
|
|
||||||
s = seconds + ' ' + (seconds > 1 ? 'seconds' : 'second');
|
|
||||||
|
|
||||||
if (days) {
|
|
||||||
if (days >= 4 || !hours) {
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
return d + ', ' + h;
|
|
||||||
}
|
|
||||||
if (hours) {
|
|
||||||
if (hours >= 4 || !minutes) {
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
return h + ', ' + m;
|
|
||||||
}
|
|
||||||
if (minutes) {
|
|
||||||
if (minutes >= 4 || !seconds) {
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
return m + ', ' + s;
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
},
|
|
||||||
|
|
||||||
timestamp: function (seconds) {
|
|
||||||
if (!seconds) {
|
|
||||||
return 'N/A';
|
|
||||||
}
|
|
||||||
|
|
||||||
var myDate = new Date(seconds * 1000);
|
|
||||||
var now = new Date();
|
|
||||||
|
|
||||||
var date = "";
|
|
||||||
var time = "";
|
|
||||||
|
|
||||||
var sameYear = now.getFullYear() === myDate.getFullYear();
|
|
||||||
var sameMonth = now.getMonth() === myDate.getMonth();
|
|
||||||
|
|
||||||
var dateDiff = now.getDate() - myDate.getDate();
|
|
||||||
if (sameYear && sameMonth && Math.abs(dateDiff) <= 1) {
|
|
||||||
if (dateDiff === 0) {
|
|
||||||
date = "Today";
|
|
||||||
} else if (dateDiff === 1) {
|
|
||||||
date = "Yesterday";
|
|
||||||
} else {
|
|
||||||
date = "Tomorrow";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
date = myDate.toDateString();
|
|
||||||
}
|
|
||||||
|
|
||||||
var hours = myDate.getHours();
|
|
||||||
var period = "AM";
|
|
||||||
if (hours > 12) {
|
|
||||||
hours = hours - 12;
|
|
||||||
period = "PM";
|
|
||||||
}
|
|
||||||
if (hours === 0) {
|
|
||||||
hours = 12;
|
|
||||||
}
|
|
||||||
if (hours < 10) {
|
|
||||||
hours = "0" + hours;
|
|
||||||
}
|
|
||||||
var minutes = myDate.getMinutes();
|
|
||||||
if (minutes < 10) {
|
|
||||||
minutes = "0" + minutes;
|
|
||||||
}
|
|
||||||
var seconds = myDate.getSeconds();
|
|
||||||
if (seconds < 10) {
|
|
||||||
seconds = "0" + seconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
time = [hours, minutes, seconds].join(':');
|
|
||||||
|
|
||||||
return [date, time, period].join(' ');
|
|
||||||
},
|
|
||||||
|
|
||||||
ngettext: function (msgid, msgid_plural, n) {
|
|
||||||
// TODO(i18n): http://doc.qt.digia.com/4.6/i18n-plural-rules.html
|
|
||||||
return n === 1 ? msgid : msgid_plural;
|
|
||||||
},
|
|
||||||
|
|
||||||
countString: function (msgid, msgid_plural, n) {
|
|
||||||
return [n.toStringWithCommas(), this.ngettext(msgid, msgid_plural, n)].join(' ');
|
|
||||||
},
|
|
||||||
|
|
||||||
peerStatus: function (flagStr) {
|
|
||||||
var formattedFlags = [];
|
|
||||||
for (var i = 0, flag; flag = flagStr[i]; ++i) {
|
|
||||||
var explanation = null;
|
|
||||||
switch (flag) {
|
|
||||||
case "O":
|
|
||||||
explanation = "Optimistic unchoke";
|
|
||||||
break;
|
|
||||||
case "D":
|
|
||||||
explanation = "Downloading from this peer";
|
|
||||||
break;
|
|
||||||
case "d":
|
|
||||||
explanation = "We would download from this peer if they'd let us";
|
|
||||||
break;
|
|
||||||
case "U":
|
|
||||||
explanation = "Uploading to peer";
|
|
||||||
break;
|
|
||||||
case "u":
|
|
||||||
explanation = "We would upload to this peer if they'd ask";
|
|
||||||
break;
|
|
||||||
case "K":
|
|
||||||
explanation = "Peer has unchoked us, but we're not interested";
|
|
||||||
break;
|
|
||||||
case "?":
|
|
||||||
explanation = "We unchoked this peer, but they're not interested";
|
|
||||||
break;
|
|
||||||
case "E":
|
|
||||||
explanation = "Encrypted Connection";
|
|
||||||
break;
|
|
||||||
case "H":
|
|
||||||
explanation = "Peer was discovered through Distributed Hash Table (DHT)";
|
|
||||||
break;
|
|
||||||
case "X":
|
|
||||||
explanation = "Peer was discovered through Peer Exchange (PEX)";
|
|
||||||
break;
|
|
||||||
case "I":
|
|
||||||
explanation = "Peer is an incoming connection";
|
|
||||||
break;
|
|
||||||
case "T":
|
|
||||||
explanation = "Peer is connected via uTP";
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!explanation) {
|
|
||||||
formattedFlags.push(flag);
|
|
||||||
} else {
|
|
||||||
formattedFlags.push("<span title=\"" + flag + ': ' + explanation + "\">" + flag + "</span>");
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
return formattedFlags.join('');
|
|
||||||
}
|
}
|
||||||
};
|
return d + ', ' + h;
|
||||||
|
}
|
||||||
|
if (hours) {
|
||||||
|
if (hours >= 4 || !minutes) {
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
return h + ', ' + m;
|
||||||
|
}
|
||||||
|
if (minutes) {
|
||||||
|
if (minutes >= 4 || !seconds) {
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
return m + ', ' + s;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
},
|
||||||
|
|
||||||
|
timestamp: function (seconds) {
|
||||||
|
if (!seconds) {
|
||||||
|
return 'N/A';
|
||||||
|
}
|
||||||
|
|
||||||
|
const myDate = new Date(seconds * 1000);
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
let date = '';
|
||||||
|
let time = '';
|
||||||
|
|
||||||
|
const sameYear = now.getFullYear() === myDate.getFullYear();
|
||||||
|
const sameMonth = now.getMonth() === myDate.getMonth();
|
||||||
|
|
||||||
|
const dateDiff = now.getDate() - myDate.getDate();
|
||||||
|
if (sameYear && sameMonth && Math.abs(dateDiff) <= 1) {
|
||||||
|
if (dateDiff === 0) {
|
||||||
|
date = 'Today';
|
||||||
|
} else if (dateDiff === 1) {
|
||||||
|
date = 'Yesterday';
|
||||||
|
} else {
|
||||||
|
date = 'Tomorrow';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
date = myDate.toDateString();
|
||||||
|
}
|
||||||
|
|
||||||
|
let hours = myDate.getHours();
|
||||||
|
let period = 'AM';
|
||||||
|
if (hours > 12) {
|
||||||
|
hours = hours - 12;
|
||||||
|
period = 'PM';
|
||||||
|
}
|
||||||
|
if (hours === 0) {
|
||||||
|
hours = 12;
|
||||||
|
}
|
||||||
|
if (hours < 10) {
|
||||||
|
hours = '0' + hours;
|
||||||
|
}
|
||||||
|
let minutes = myDate.getMinutes();
|
||||||
|
if (minutes < 10) {
|
||||||
|
minutes = '0' + minutes;
|
||||||
|
}
|
||||||
|
seconds = myDate.getSeconds();
|
||||||
|
if (seconds < 10) {
|
||||||
|
seconds = '0' + seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
time = [hours, minutes, seconds].join(':');
|
||||||
|
|
||||||
|
return [date, time, period].join(' ');
|
||||||
|
},
|
||||||
|
|
||||||
|
ngettext: function (msgid, msgid_plural, n) {
|
||||||
|
// TODO(i18n): http://doc.qt.digia.com/4.6/i18n-plural-rules.html
|
||||||
|
return n === 1 ? msgid : msgid_plural;
|
||||||
|
},
|
||||||
|
|
||||||
|
countString: function (msgid, msgid_plural, n) {
|
||||||
|
return [n.toStringWithCommas(), this.ngettext(msgid, msgid_plural, n)].join(' ');
|
||||||
|
},
|
||||||
|
|
||||||
|
peerStatus: function (flagStr) {
|
||||||
|
const formattedFlags = [];
|
||||||
|
for (var i = 0, flag; (flag = flagStr[i]); ++i) {
|
||||||
|
let explanation = null;
|
||||||
|
switch (flag) {
|
||||||
|
case 'O':
|
||||||
|
explanation = 'Optimistic unchoke';
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
explanation = 'Downloading from this peer';
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
explanation = "We would download from this peer if they'd let us";
|
||||||
|
break;
|
||||||
|
case 'U':
|
||||||
|
explanation = 'Uploading to peer';
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
explanation = "We would upload to this peer if they'd ask";
|
||||||
|
break;
|
||||||
|
case 'K':
|
||||||
|
explanation = "Peer has unchoked us, but we're not interested";
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
explanation = "We unchoked this peer, but they're not interested";
|
||||||
|
break;
|
||||||
|
case 'E':
|
||||||
|
explanation = 'Encrypted Connection';
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
explanation = 'Peer was discovered through Distributed Hash Table (DHT)';
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
explanation = 'Peer was discovered through Peer Exchange (PEX)';
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
explanation = 'Peer is an incoming connection';
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
explanation = 'Peer is connected via uTP';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!explanation) {
|
||||||
|
formattedFlags.push(flag);
|
||||||
|
} else {
|
||||||
|
formattedFlags.push(
|
||||||
|
'<span title="' + flag + ': ' + explanation + '">' + flag + '</span>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return formattedFlags.join('');
|
||||||
|
},
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -6,46 +6,48 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
// IE specific fixes here
|
// IE specific fixes here
|
||||||
if (jQuery.browser.msie) {
|
if (jQuery.browser.msie) {
|
||||||
try {
|
try {
|
||||||
document.execCommand("BackgroundImageCache", false, true);
|
document.execCommand('BackgroundImageCache', false, true);
|
||||||
} catch (err) {};
|
} catch (err) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jQuery.browser.safari) {
|
||||||
|
// Move search field's margin down for the styled input
|
||||||
|
document.getElementById('torrent_search').style['margin-top'] = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMobileDevice) {
|
||||||
|
window.onload = function () {
|
||||||
|
setTimeout(function () {
|
||||||
|
window.scrollTo(0, 1);
|
||||||
|
}, 500);
|
||||||
};
|
};
|
||||||
|
window.onorientationchange = function () {
|
||||||
if (jQuery.browser.safari) {
|
setTimeout(function () {
|
||||||
// Move search field's margin down for the styled input
|
window.scrollTo(0, 1);
|
||||||
document.getElementById("torrent_search").style["margin-top"] = 3;
|
}, 100);
|
||||||
};
|
};
|
||||||
|
if (window.navigator.standalone) {
|
||||||
|
// Fix min height for isMobileDevice when run in full screen mode from home screen
|
||||||
|
// so the footer appears in the right place
|
||||||
|
document.getElementById('torrent_container').style['min-height'] = '338px';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fix for non-Safari-3 browsers: dark borders to replace shadows.
|
||||||
|
Array.from(document.getElementsByClassName('dialog_window')).forEach(function (e) {
|
||||||
|
e.style['border'] = '1px solid #777';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (isMobileDevice) {
|
// Initialise the dialog controller
|
||||||
window.onload = function () {
|
dialog = new Dialog();
|
||||||
setTimeout(function () {
|
|
||||||
window.scrollTo(0, 1);
|
|
||||||
}, 500);
|
|
||||||
};
|
|
||||||
window.onorientationchange = function () {
|
|
||||||
setTimeout(function () {
|
|
||||||
window.scrollTo(0, 1);
|
|
||||||
}, 100);
|
|
||||||
};
|
|
||||||
if (window.navigator.standalone) {
|
|
||||||
// Fix min height for isMobileDevice when run in full screen mode from home screen
|
|
||||||
// so the footer appears in the right place
|
|
||||||
document.getElementById("torrent_container").style["min-height"] = "338px";
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Fix for non-Safari-3 browsers: dark borders to replace shadows.
|
|
||||||
Array.from(document.getElementsByClassName("dialog_window")).forEach(function (e) {
|
|
||||||
e.style["border"] = "1px solid #777";
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initialise the dialog controller
|
// Initialise the main Transmission controller
|
||||||
dialog = new Dialog();
|
transmission = new Transmission();
|
||||||
|
}
|
||||||
|
|
||||||
// Initialise the main Transmission controller
|
document.addEventListener('DOMContentLoaded', main);
|
||||||
transmission = new Transmission();
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", main);
|
|
||||||
|
|
|
@ -1,42 +1,46 @@
|
||||||
var Notifications = {};
|
const Notifications = {};
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
if (!window.webkitNotifications) {
|
if (!window.webkitNotifications) {
|
||||||
return;
|
return;
|
||||||
};
|
}
|
||||||
|
|
||||||
var notificationsEnabled = (window.webkitNotifications.checkPermission() === 0)
|
let notificationsEnabled = window.webkitNotifications.checkPermission() === 0;
|
||||||
var toggle = $('#toggle_notifications');
|
const toggle = $('#toggle_notifications');
|
||||||
|
|
||||||
toggle.show();
|
toggle.show();
|
||||||
updateMenuTitle();
|
updateMenuTitle();
|
||||||
$(transmission).bind('downloadComplete seedingComplete', function (event, torrent) {
|
$(transmission).bind('downloadComplete seedingComplete', function (event, torrent) {
|
||||||
if (notificationsEnabled) {
|
if (notificationsEnabled) {
|
||||||
var title = (event.type == 'downloadComplete' ? 'Download' : 'Seeding') + ' complete',
|
let title = (event.type == 'downloadComplete' ? 'Download' : 'Seeding') + ' complete',
|
||||||
content = torrent.getName(),
|
content = torrent.getName(),
|
||||||
notification;
|
notification;
|
||||||
|
|
||||||
notification = window.webkitNotifications.createNotification('style/transmission/images/logo.png', title, content);
|
notification = window.webkitNotifications.createNotification(
|
||||||
notification.show();
|
'style/transmission/images/logo.png',
|
||||||
setTimeout(function () {
|
title,
|
||||||
notification.cancel();
|
content
|
||||||
}, 5000);
|
);
|
||||||
};
|
notification.show();
|
||||||
});
|
setTimeout(function () {
|
||||||
|
notification.cancel();
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function updateMenuTitle() {
|
function updateMenuTitle() {
|
||||||
toggle.html((notificationsEnabled ? 'Disable' : 'Enable') + ' Notifications');
|
toggle.html((notificationsEnabled ? 'Disable' : 'Enable') + ' Notifications');
|
||||||
};
|
}
|
||||||
|
|
||||||
Notifications.toggle = function () {
|
Notifications.toggle = function () {
|
||||||
if (window.webkitNotifications.checkPermission() !== 0) {
|
if (window.webkitNotifications.checkPermission() !== 0) {
|
||||||
window.webkitNotifications.requestPermission(function () {
|
window.webkitNotifications.requestPermission(function () {
|
||||||
notificationsEnabled = (window.webkitNotifications.checkPermission() === 0);
|
notificationsEnabled = window.webkitNotifications.checkPermission() === 0;
|
||||||
updateMenuTitle();
|
updateMenuTitle();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
notificationsEnabled = !notificationsEnabled;
|
notificationsEnabled = !notificationsEnabled;
|
||||||
updateMenuTitle();
|
updateMenuTitle();
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,82 +6,82 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (!Array.from) {
|
if (!Array.from) {
|
||||||
Array.from = (function () {
|
Array.from = (function () {
|
||||||
var toStr = Object.prototype.toString;
|
const toStr = Object.prototype.toString;
|
||||||
var isCallable = function (fn) {
|
const isCallable = function (fn) {
|
||||||
return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
|
return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
|
||||||
};
|
};
|
||||||
var toInteger = function (value) {
|
const toInteger = function (value) {
|
||||||
var number = Number(value);
|
const number = Number(value);
|
||||||
if (isNaN(number)) {
|
if (isNaN(number)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (number === 0 || !isFinite(number)) {
|
if (number === 0 || !isFinite(number)) {
|
||||||
return number;
|
return number;
|
||||||
}
|
}
|
||||||
return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
|
return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
|
||||||
};
|
};
|
||||||
var maxSafeInteger = Math.pow(2, 53) - 1;
|
const maxSafeInteger = Math.pow(2, 53) - 1;
|
||||||
var toLength = function (value) {
|
const toLength = function (value) {
|
||||||
var len = toInteger(value);
|
const len = toInteger(value);
|
||||||
return Math.min(Math.max(len, 0), maxSafeInteger);
|
return Math.min(Math.max(len, 0), maxSafeInteger);
|
||||||
};
|
};
|
||||||
|
|
||||||
// The length property of the from method is 1.
|
// The length property of the from method is 1.
|
||||||
return function from(arrayLike /*, mapFn, thisArg */ ) {
|
return function from(arrayLike /*, mapFn, thisArg */) {
|
||||||
// 1. Let C be the this value.
|
// 1. Let C be the this value.
|
||||||
var C = this;
|
const C = this;
|
||||||
|
|
||||||
// 2. Let items be ToObject(arrayLike).
|
// 2. Let items be ToObject(arrayLike).
|
||||||
var items = Object(arrayLike);
|
const items = Object(arrayLike);
|
||||||
|
|
||||||
// 3. ReturnIfAbrupt(items).
|
// 3. ReturnIfAbrupt(items).
|
||||||
if (arrayLike == null) {
|
if (arrayLike == null) {
|
||||||
throw new TypeError("Array.from requires an array-like object - not null or undefined");
|
throw new TypeError('Array.from requires an array-like object - not null or undefined');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. If mapfn is undefined, then let mapping be false.
|
// 4. If mapfn is undefined, then let mapping be false.
|
||||||
var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
|
const mapFn = arguments.length > 1 ? arguments[1] : void undefined;
|
||||||
var T;
|
let T;
|
||||||
if (typeof mapFn !== 'undefined') {
|
if (typeof mapFn !== 'undefined') {
|
||||||
// 5. else
|
// 5. else
|
||||||
// 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
|
// 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
|
||||||
if (!isCallable(mapFn)) {
|
if (!isCallable(mapFn)) {
|
||||||
throw new TypeError('Array.from: when provided, the second argument must be a function');
|
throw new TypeError('Array.from: when provided, the second argument must be a function');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
|
// 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
|
||||||
if (arguments.length > 2) {
|
if (arguments.length > 2) {
|
||||||
T = arguments[2];
|
T = arguments[2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 10. Let lenValue be Get(items, "length").
|
// 10. Let lenValue be Get(items, "length").
|
||||||
// 11. Let len be ToLength(lenValue).
|
// 11. Let len be ToLength(lenValue).
|
||||||
var len = toLength(items.length);
|
const len = toLength(items.length);
|
||||||
|
|
||||||
// 13. If IsConstructor(C) is true, then
|
// 13. If IsConstructor(C) is true, then
|
||||||
// 13. a. Let A be the result of calling the [[Construct]] internal method of C with an argument list containing the single item len.
|
// 13. a. Let A be the result of calling the [[Construct]] internal method of C with an argument list containing the single item len.
|
||||||
// 14. a. Else, Let A be ArrayCreate(len).
|
// 14. a. Else, Let A be ArrayCreate(len).
|
||||||
var A = isCallable(C) ? Object(new C(len)) : new Array(len);
|
const A = isCallable(C) ? Object(new C(len)) : new Array(len);
|
||||||
|
|
||||||
// 16. Let k be 0.
|
// 16. Let k be 0.
|
||||||
var k = 0;
|
let k = 0;
|
||||||
// 17. Repeat, while k < len… (also steps a - h)
|
// 17. Repeat, while k < len… (also steps a - h)
|
||||||
var kValue;
|
let kValue;
|
||||||
while (k < len) {
|
while (k < len) {
|
||||||
kValue = items[k];
|
kValue = items[k];
|
||||||
if (mapFn) {
|
if (mapFn) {
|
||||||
A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
|
A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
|
||||||
} else {
|
} else {
|
||||||
A[k] = kValue;
|
A[k] = kValue;
|
||||||
}
|
}
|
||||||
k += 1;
|
k += 1;
|
||||||
}
|
}
|
||||||
// 18. Let putStatus be Put(A, "length", len, true).
|
// 18. Let putStatus be Put(A, "length", len, true).
|
||||||
A.length = len;
|
A.length = len;
|
||||||
// 20. Return A.
|
// 20. Return A.
|
||||||
return A;
|
return A;
|
||||||
};
|
};
|
||||||
}());
|
})();
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,297 +6,303 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function PrefsDialog(remote) {
|
function PrefsDialog(remote) {
|
||||||
|
const data = {
|
||||||
|
dialog: null,
|
||||||
|
remote: null,
|
||||||
|
elements: {},
|
||||||
|
|
||||||
var data = {
|
// all the RPC session keys that we have gui controls for
|
||||||
dialog: null,
|
keys: [
|
||||||
remote: null,
|
'alt-speed-down',
|
||||||
elements: {},
|
'alt-speed-time-begin',
|
||||||
|
'alt-speed-time-day',
|
||||||
|
'alt-speed-time-enabled',
|
||||||
|
'alt-speed-time-end',
|
||||||
|
'alt-speed-up',
|
||||||
|
'blocklist-enabled',
|
||||||
|
'blocklist-size',
|
||||||
|
'blocklist-url',
|
||||||
|
'dht-enabled',
|
||||||
|
'download-dir',
|
||||||
|
'encryption',
|
||||||
|
'idle-seeding-limit',
|
||||||
|
'idle-seeding-limit-enabled',
|
||||||
|
'lpd-enabled',
|
||||||
|
'peer-limit-global',
|
||||||
|
'peer-limit-per-torrent',
|
||||||
|
'peer-port',
|
||||||
|
'peer-port-random-on-start',
|
||||||
|
'pex-enabled',
|
||||||
|
'port-forwarding-enabled',
|
||||||
|
'rename-partial-files',
|
||||||
|
'seedRatioLimit',
|
||||||
|
'seedRatioLimited',
|
||||||
|
'speed-limit-down',
|
||||||
|
'speed-limit-down-enabled',
|
||||||
|
'speed-limit-up',
|
||||||
|
'speed-limit-up-enabled',
|
||||||
|
'start-added-torrents',
|
||||||
|
'utp-enabled',
|
||||||
|
],
|
||||||
|
|
||||||
// all the RPC session keys that we have gui controls for
|
// map of keys that are enabled only if a 'parent' key is enabled
|
||||||
keys: [
|
groups: {
|
||||||
'alt-speed-down',
|
'alt-speed-time-enabled': [
|
||||||
'alt-speed-time-begin',
|
'alt-speed-time-begin',
|
||||||
'alt-speed-time-day',
|
'alt-speed-time-day',
|
||||||
'alt-speed-time-enabled',
|
'alt-speed-time-end',
|
||||||
'alt-speed-time-end',
|
],
|
||||||
'alt-speed-up',
|
'blocklist-enabled': ['blocklist-url', 'blocklist-update-button'],
|
||||||
'blocklist-enabled',
|
'idle-seeding-limit-enabled': ['idle-seeding-limit'],
|
||||||
'blocklist-size',
|
seedRatioLimited: ['seedRatioLimit'],
|
||||||
'blocklist-url',
|
'speed-limit-down-enabled': ['speed-limit-down'],
|
||||||
'dht-enabled',
|
'speed-limit-up-enabled': ['speed-limit-up'],
|
||||||
'download-dir',
|
},
|
||||||
'encryption',
|
};
|
||||||
'idle-seeding-limit',
|
|
||||||
'idle-seeding-limit-enabled',
|
|
||||||
'lpd-enabled',
|
|
||||||
'peer-limit-global',
|
|
||||||
'peer-limit-per-torrent',
|
|
||||||
'peer-port',
|
|
||||||
'peer-port-random-on-start',
|
|
||||||
'pex-enabled',
|
|
||||||
'port-forwarding-enabled',
|
|
||||||
'rename-partial-files',
|
|
||||||
'seedRatioLimit',
|
|
||||||
'seedRatioLimited',
|
|
||||||
'speed-limit-down',
|
|
||||||
'speed-limit-down-enabled',
|
|
||||||
'speed-limit-up',
|
|
||||||
'speed-limit-up-enabled',
|
|
||||||
'start-added-torrents',
|
|
||||||
'utp-enabled'
|
|
||||||
],
|
|
||||||
|
|
||||||
// map of keys that are enabled only if a 'parent' key is enabled
|
const initTimeDropDown = function (e) {
|
||||||
groups: {
|
let i, hour, mins, value, content;
|
||||||
'alt-speed-time-enabled': ['alt-speed-time-begin',
|
|
||||||
'alt-speed-time-day',
|
for (i = 0; i < 24 * 4; ++i) {
|
||||||
'alt-speed-time-end'
|
hour = parseInt(i / 4, 10);
|
||||||
],
|
mins = (i % 4) * 15;
|
||||||
'blocklist-enabled': ['blocklist-url',
|
value = i * 15;
|
||||||
'blocklist-update-button'
|
content = hour + ':' + (mins || '00');
|
||||||
],
|
e.options[i] = new Option(content, value);
|
||||||
'idle-seeding-limit-enabled': ['idle-seeding-limit'],
|
}
|
||||||
'seedRatioLimited': ['seedRatioLimit'],
|
};
|
||||||
'speed-limit-down-enabled': ['speed-limit-down'],
|
|
||||||
'speed-limit-up-enabled': ['speed-limit-up']
|
const onPortChecked = function (response) {
|
||||||
|
const is_open = response['arguments']['port-is-open'];
|
||||||
|
const text = 'Port is <b>' + (is_open ? 'Open' : 'Closed') + '</b>';
|
||||||
|
const e = data.elements.root.find('#port-label');
|
||||||
|
setInnerHTML(e[0], text);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setGroupEnabled = function (parent_key, enabled) {
|
||||||
|
let i, key, keys, root;
|
||||||
|
|
||||||
|
if (parent_key in data.groups) {
|
||||||
|
root = data.elements.root;
|
||||||
|
keys = data.groups[parent_key];
|
||||||
|
|
||||||
|
for (i = 0; (key = keys[i]); ++i) {
|
||||||
|
root.find('#' + key).attr('disabled', !enabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onBlocklistUpdateClicked = function () {
|
||||||
|
data.remote.updateBlocklist();
|
||||||
|
setBlocklistButtonEnabled(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
var setBlocklistButtonEnabled = function (b) {
|
||||||
|
const e = data.elements.blocklist_button;
|
||||||
|
e.attr('disabled', !b);
|
||||||
|
e.val(b ? 'Update' : 'Updating...');
|
||||||
|
};
|
||||||
|
|
||||||
|
const getValue = function (e) {
|
||||||
|
let str;
|
||||||
|
|
||||||
|
switch (e[0].type) {
|
||||||
|
case 'checkbox':
|
||||||
|
case 'radio':
|
||||||
|
return e.prop('checked');
|
||||||
|
|
||||||
|
case 'text':
|
||||||
|
case 'url':
|
||||||
|
case 'email':
|
||||||
|
case 'number':
|
||||||
|
case 'search':
|
||||||
|
case 'select-one':
|
||||||
|
str = e.val();
|
||||||
|
if (parseInt(str, 10).toString() === str) {
|
||||||
|
return parseInt(str, 10);
|
||||||
}
|
}
|
||||||
};
|
if (parseFloat(str).toString() === str) {
|
||||||
|
return parseFloat(str);
|
||||||
var initTimeDropDown = function (e) {
|
|
||||||
var i, hour, mins, value, content;
|
|
||||||
|
|
||||||
for (i = 0; i < 24 * 4; ++i) {
|
|
||||||
hour = parseInt(i / 4, 10);
|
|
||||||
mins = ((i % 4) * 15);
|
|
||||||
value = i * 15;
|
|
||||||
content = hour + ':' + (mins || '00');
|
|
||||||
e.options[i] = new Option(content, value);
|
|
||||||
}
|
}
|
||||||
|
return str;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* this callback is for controls whose changes can be applied
|
||||||
|
immediately, like checkboxs, radioboxes, and selects */
|
||||||
|
const onControlChanged = function (ev) {
|
||||||
|
const o = {};
|
||||||
|
o[ev.target.id] = getValue($(ev.target));
|
||||||
|
data.remote.savePrefs(o);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* these two callbacks are for controls whose changes can't be applied
|
||||||
|
immediately -- like a text entry field -- because it takes many
|
||||||
|
change events for the user to get to the desired result */
|
||||||
|
const onControlFocused = function (ev) {
|
||||||
|
data.oldValue = getValue($(ev.target));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onControlBlurred = function (ev) {
|
||||||
|
const newValue = getValue($(ev.target));
|
||||||
|
if (newValue !== data.oldValue) {
|
||||||
|
const o = {};
|
||||||
|
o[ev.target.id] = newValue;
|
||||||
|
data.remote.savePrefs(o);
|
||||||
|
delete data.oldValue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDefaultMobileOptions = function () {
|
||||||
|
return {
|
||||||
|
width: $(window).width(),
|
||||||
|
height: $(window).height(),
|
||||||
|
position: ['left', 'top'],
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
var onPortChecked = function (response) {
|
const initialize = function (remote) {
|
||||||
var is_open = response['arguments']['port-is-open'];
|
let i, key, e, o;
|
||||||
var text = 'Port is <b>' + (is_open ? 'Open' : 'Closed') + '</b>';
|
|
||||||
var e = data.elements.root.find('#port-label');
|
|
||||||
setInnerHTML(e[0], text);
|
|
||||||
};
|
|
||||||
|
|
||||||
var setGroupEnabled = function (parent_key, enabled) {
|
data.remote = remote;
|
||||||
var i, key, keys, root;
|
|
||||||
|
|
||||||
if (parent_key in data.groups) {
|
e = $('#prefs-dialog');
|
||||||
root = data.elements.root;
|
data.elements.root = e;
|
||||||
keys = data.groups[parent_key];
|
|
||||||
|
|
||||||
for (i = 0; key = keys[i]; ++i) {
|
initTimeDropDown(e.find('#alt-speed-time-begin')[0]);
|
||||||
root.find('#' + key).attr('disabled', !enabled);
|
initTimeDropDown(e.find('#alt-speed-time-end')[0]);
|
||||||
};
|
|
||||||
|
o = isMobileDevice
|
||||||
|
? getDefaultMobileOptions()
|
||||||
|
: {
|
||||||
|
width: 350,
|
||||||
|
height: 400,
|
||||||
};
|
};
|
||||||
};
|
o.autoOpen = false;
|
||||||
|
o.show = o.hide = 'fade';
|
||||||
|
o.close = onDialogClosed;
|
||||||
|
e.tabbedDialog(o);
|
||||||
|
|
||||||
var onBlocklistUpdateClicked = function () {
|
e = e.find('#blocklist-update-button');
|
||||||
data.remote.updateBlocklist();
|
data.elements.blocklist_button = e;
|
||||||
setBlocklistButtonEnabled(false);
|
e.click(onBlocklistUpdateClicked);
|
||||||
};
|
|
||||||
|
|
||||||
var setBlocklistButtonEnabled = function (b) {
|
// listen for user input
|
||||||
var e = data.elements.blocklist_button;
|
for (i = 0; (key = data.keys[i]); ++i) {
|
||||||
e.attr('disabled', !b);
|
e = data.elements.root.find('#' + key);
|
||||||
e.val(b ? 'Update' : 'Updating...');
|
switch (e[0].type) {
|
||||||
};
|
|
||||||
|
|
||||||
var getValue = function (e) {
|
|
||||||
var str;
|
|
||||||
|
|
||||||
switch (e[0].type) {
|
|
||||||
case 'checkbox':
|
case 'checkbox':
|
||||||
case 'radio':
|
case 'radio':
|
||||||
return e.prop('checked');
|
case 'select-one':
|
||||||
|
e.change(onControlChanged);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'text':
|
case 'text':
|
||||||
case 'url':
|
case 'url':
|
||||||
case 'email':
|
case 'email':
|
||||||
case 'number':
|
case 'number':
|
||||||
case 'search':
|
case 'search':
|
||||||
case 'select-one':
|
e.focus(onControlFocused);
|
||||||
str = e.val();
|
e.blur(onControlBlurred);
|
||||||
if (parseInt(str, 10).toString() === str) {
|
break;
|
||||||
return parseInt(str, 10);
|
|
||||||
};
|
|
||||||
if (parseFloat(str).toString() === str) {
|
|
||||||
return parseFloat(str);
|
|
||||||
};
|
|
||||||
return str;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return null;
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getValues = function () {
|
||||||
|
let i,
|
||||||
|
key,
|
||||||
|
val,
|
||||||
|
o = {},
|
||||||
|
keys = data.keys,
|
||||||
|
root = data.elements.root;
|
||||||
|
|
||||||
|
for (i = 0; (key = keys[i]); ++i) {
|
||||||
|
val = getValue(root.find('#' + key));
|
||||||
|
if (val !== null) {
|
||||||
|
o[key] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return o;
|
||||||
|
};
|
||||||
|
|
||||||
|
var onDialogClosed = function () {
|
||||||
|
transmission.hideMobileAddressbar();
|
||||||
|
|
||||||
|
$(data.dialog).trigger('closed', getValues());
|
||||||
|
};
|
||||||
|
|
||||||
|
/****
|
||||||
|
***** PUBLIC FUNCTIONS
|
||||||
|
****/
|
||||||
|
|
||||||
|
// update the dialog's controls
|
||||||
|
this.set = function (o) {
|
||||||
|
let e, i, key, val;
|
||||||
|
const keys = data.keys;
|
||||||
|
const root = data.elements.root;
|
||||||
|
|
||||||
|
setBlocklistButtonEnabled(true);
|
||||||
|
|
||||||
|
for (i = 0; (key = keys[i]); ++i) {
|
||||||
|
val = o[key];
|
||||||
|
e = root.find('#' + key);
|
||||||
|
|
||||||
|
if (key === 'blocklist-size') {
|
||||||
|
// special case -- regular text area
|
||||||
|
e.text('' + val.toStringWithCommas());
|
||||||
|
} else {
|
||||||
|
switch (e[0].type) {
|
||||||
|
case 'checkbox':
|
||||||
|
case 'radio':
|
||||||
|
e.prop('checked', val);
|
||||||
|
setGroupEnabled(key, val);
|
||||||
|
break;
|
||||||
|
case 'text':
|
||||||
|
case 'url':
|
||||||
|
case 'email':
|
||||||
|
case 'number':
|
||||||
|
case 'search':
|
||||||
|
// don't change the text if the user's editing it.
|
||||||
|
// it's very annoying when that happens!
|
||||||
|
if (e[0] !== document.activeElement) {
|
||||||
|
e.val(val);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'select-one':
|
||||||
|
e.val(val);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/* this callback is for controls whose changes can be applied
|
this.show = function () {
|
||||||
immediately, like checkboxs, radioboxes, and selects */
|
transmission.hideMobileAddressbar();
|
||||||
var onControlChanged = function (ev) {
|
|
||||||
var o = {};
|
|
||||||
o[ev.target.id] = getValue($(ev.target));
|
|
||||||
data.remote.savePrefs(o);
|
|
||||||
};
|
|
||||||
|
|
||||||
/* these two callbacks are for controls whose changes can't be applied
|
setBlocklistButtonEnabled(true);
|
||||||
immediately -- like a text entry field -- because it takes many
|
data.remote.checkPort(onPortChecked, this);
|
||||||
change events for the user to get to the desired result */
|
data.elements.root.dialog('open');
|
||||||
var onControlFocused = function (ev) {
|
};
|
||||||
data.oldValue = getValue($(ev.target));
|
|
||||||
};
|
|
||||||
|
|
||||||
var onControlBlurred = function (ev) {
|
this.close = function () {
|
||||||
var newValue = getValue($(ev.target));
|
transmission.hideMobileAddressbar();
|
||||||
if (newValue !== data.oldValue) {
|
data.elements.root.dialog('close');
|
||||||
var o = {};
|
};
|
||||||
o[ev.target.id] = newValue;
|
|
||||||
data.remote.savePrefs(o);
|
|
||||||
delete data.oldValue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var getDefaultMobileOptions = function () {
|
this.shouldAddedTorrentsStart = function () {
|
||||||
return {
|
return data.elements.root.find('#start-added-torrents')[0].checked;
|
||||||
width: $(window).width(),
|
};
|
||||||
height: $(window).height(),
|
|
||||||
position: ['left', 'top']
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
var initialize = function (remote) {
|
data.dialog = this;
|
||||||
var i, key, e, o;
|
initialize(remote);
|
||||||
|
}
|
||||||
data.remote = remote;
|
|
||||||
|
|
||||||
e = $('#prefs-dialog');
|
|
||||||
data.elements.root = e;
|
|
||||||
|
|
||||||
initTimeDropDown(e.find('#alt-speed-time-begin')[0]);
|
|
||||||
initTimeDropDown(e.find('#alt-speed-time-end')[0]);
|
|
||||||
|
|
||||||
o = isMobileDevice ? getDefaultMobileOptions() : {
|
|
||||||
width: 350,
|
|
||||||
height: 400
|
|
||||||
};
|
|
||||||
o.autoOpen = false;
|
|
||||||
o.show = o.hide = 'fade';
|
|
||||||
o.close = onDialogClosed;
|
|
||||||
e.tabbedDialog(o);
|
|
||||||
|
|
||||||
e = e.find('#blocklist-update-button');
|
|
||||||
data.elements.blocklist_button = e;
|
|
||||||
e.click(onBlocklistUpdateClicked);
|
|
||||||
|
|
||||||
// listen for user input
|
|
||||||
for (i = 0; key = data.keys[i]; ++i) {
|
|
||||||
e = data.elements.root.find('#' + key);
|
|
||||||
switch (e[0].type) {
|
|
||||||
case 'checkbox':
|
|
||||||
case 'radio':
|
|
||||||
case 'select-one':
|
|
||||||
e.change(onControlChanged);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'text':
|
|
||||||
case 'url':
|
|
||||||
case 'email':
|
|
||||||
case 'number':
|
|
||||||
case 'search':
|
|
||||||
e.focus(onControlFocused);
|
|
||||||
e.blur(onControlBlurred);
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
var getValues = function () {
|
|
||||||
var i, key, val, o = {},
|
|
||||||
keys = data.keys,
|
|
||||||
root = data.elements.root;
|
|
||||||
|
|
||||||
for (i = 0; key = keys[i]; ++i) {
|
|
||||||
val = getValue(root.find('#' + key));
|
|
||||||
if (val !== null) {
|
|
||||||
o[key] = val;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
return o;
|
|
||||||
};
|
|
||||||
|
|
||||||
var onDialogClosed = function () {
|
|
||||||
transmission.hideMobileAddressbar();
|
|
||||||
|
|
||||||
$(data.dialog).trigger('closed', getValues());
|
|
||||||
};
|
|
||||||
|
|
||||||
/****
|
|
||||||
***** PUBLIC FUNCTIONS
|
|
||||||
****/
|
|
||||||
|
|
||||||
// update the dialog's controls
|
|
||||||
this.set = function (o) {
|
|
||||||
var e, i, key, val;
|
|
||||||
var keys = data.keys;
|
|
||||||
var root = data.elements.root;
|
|
||||||
|
|
||||||
setBlocklistButtonEnabled(true);
|
|
||||||
|
|
||||||
for (i = 0; key = keys[i]; ++i) {
|
|
||||||
val = o[key];
|
|
||||||
e = root.find('#' + key);
|
|
||||||
|
|
||||||
if (key === 'blocklist-size') {
|
|
||||||
// special case -- regular text area
|
|
||||||
e.text('' + val.toStringWithCommas());
|
|
||||||
} else switch (e[0].type) {
|
|
||||||
case 'checkbox':
|
|
||||||
case 'radio':
|
|
||||||
e.prop('checked', val);
|
|
||||||
setGroupEnabled(key, val);
|
|
||||||
break;
|
|
||||||
case 'text':
|
|
||||||
case 'url':
|
|
||||||
case 'email':
|
|
||||||
case 'number':
|
|
||||||
case 'search':
|
|
||||||
// don't change the text if the user's editing it.
|
|
||||||
// it's very annoying when that happens!
|
|
||||||
if (e[0] !== document.activeElement) {
|
|
||||||
e.val(val);
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case 'select-one':
|
|
||||||
e.val(val);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
this.show = function () {
|
|
||||||
transmission.hideMobileAddressbar();
|
|
||||||
|
|
||||||
setBlocklistButtonEnabled(true);
|
|
||||||
data.remote.checkPort(onPortChecked, this);
|
|
||||||
data.elements.root.dialog('open');
|
|
||||||
};
|
|
||||||
|
|
||||||
this.close = function () {
|
|
||||||
transmission.hideMobileAddressbar();
|
|
||||||
data.elements.root.dialog('close');
|
|
||||||
};
|
|
||||||
|
|
||||||
this.shouldAddedTorrentsStart = function () {
|
|
||||||
return data.elements.root.find('#start-added-torrents')[0].checked;
|
|
||||||
};
|
|
||||||
|
|
||||||
data.dialog = this;
|
|
||||||
initialize(remote);
|
|
||||||
};
|
|
||||||
|
|
|
@ -5,282 +5,298 @@
|
||||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var RPC = {
|
const RPC = {
|
||||||
_DaemonVersion: 'version',
|
_DaemonVersion: 'version',
|
||||||
_DownSpeedLimit: 'speed-limit-down',
|
_DownSpeedLimit: 'speed-limit-down',
|
||||||
_DownSpeedLimited: 'speed-limit-down-enabled',
|
_DownSpeedLimited: 'speed-limit-down-enabled',
|
||||||
_QueueMoveTop: 'queue-move-top',
|
_QueueMoveTop: 'queue-move-top',
|
||||||
_QueueMoveBottom: 'queue-move-bottom',
|
_QueueMoveBottom: 'queue-move-bottom',
|
||||||
_QueueMoveUp: 'queue-move-up',
|
_QueueMoveUp: 'queue-move-up',
|
||||||
_QueueMoveDown: 'queue-move-down',
|
_QueueMoveDown: 'queue-move-down',
|
||||||
_Root: '../rpc',
|
_Root: '../rpc',
|
||||||
_TurtleDownSpeedLimit: 'alt-speed-down',
|
_TurtleDownSpeedLimit: 'alt-speed-down',
|
||||||
_TurtleState: 'alt-speed-enabled',
|
_TurtleState: 'alt-speed-enabled',
|
||||||
_TurtleUpSpeedLimit: 'alt-speed-up',
|
_TurtleUpSpeedLimit: 'alt-speed-up',
|
||||||
_UpSpeedLimit: 'speed-limit-up',
|
_UpSpeedLimit: 'speed-limit-up',
|
||||||
_UpSpeedLimited: 'speed-limit-up-enabled'
|
_UpSpeedLimited: 'speed-limit-up-enabled',
|
||||||
};
|
};
|
||||||
|
|
||||||
function TransmissionRemote(controller) {
|
function TransmissionRemote(controller) {
|
||||||
this.initialize(controller);
|
this.initialize(controller);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
TransmissionRemote.prototype = {
|
TransmissionRemote.prototype = {
|
||||||
/*
|
/*
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
initialize: function (controller) {
|
initialize: function (controller) {
|
||||||
this._controller = controller;
|
this._controller = controller;
|
||||||
this._error = '';
|
this._error = '';
|
||||||
this._token = '';
|
this._token = '';
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Display an error if an ajax request fails, and stop sending requests
|
* Display an error if an ajax request fails, and stop sending requests
|
||||||
* or on a 409, globally set the X-Transmission-Session-Id and resend
|
* or on a 409, globally set the X-Transmission-Session-Id and resend
|
||||||
*/
|
*/
|
||||||
ajaxError: function (request, error_string, exception, ajaxObject) {
|
ajaxError: function (request, error_string, exception, ajaxObject) {
|
||||||
var token;
|
let token;
|
||||||
var remote = this;
|
const remote = this;
|
||||||
|
|
||||||
// set the Transmission-Session-Id on a 409
|
// set the Transmission-Session-Id on a 409
|
||||||
if (request.status === 409 && (token = request.getResponseHeader('X-Transmission-Session-Id'))) {
|
if (
|
||||||
remote._token = token;
|
request.status === 409 &&
|
||||||
$.ajax(ajaxObject);
|
(token = request.getResponseHeader('X-Transmission-Session-Id'))
|
||||||
return;
|
) {
|
||||||
};
|
remote._token = token;
|
||||||
|
$.ajax(ajaxObject);
|
||||||
remote._error = request.responseText ? request.responseText.trim().replace(/(<([^>]+)>)/ig, "") : "";
|
return;
|
||||||
if (!remote._error.length) {
|
|
||||||
remote._error = 'Server not responding';
|
|
||||||
};
|
|
||||||
|
|
||||||
dialog.confirm('Connection Failed',
|
|
||||||
'Could not connect to the server. You may need to reload the page to reconnect.',
|
|
||||||
'Details',
|
|
||||||
function () {
|
|
||||||
alert(remote._error);
|
|
||||||
},
|
|
||||||
'Dismiss');
|
|
||||||
remote._controller.togglePeriodicSessionRefresh(false);
|
|
||||||
},
|
|
||||||
|
|
||||||
appendSessionId: function (XHR) {
|
|
||||||
if (this._token) {
|
|
||||||
XHR.setRequestHeader('X-Transmission-Session-Id', this._token);
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
sendRequest: function (data, callback, context, async) {
|
|
||||||
var remote = this;
|
|
||||||
if (typeof (async) != 'boolean') {
|
|
||||||
async = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
var ajaxSettings = {
|
|
||||||
url: RPC._Root,
|
|
||||||
type: 'POST',
|
|
||||||
contentType: 'json',
|
|
||||||
dataType: 'json',
|
|
||||||
cache: false,
|
|
||||||
data: JSON.stringify(data),
|
|
||||||
beforeSend: function (XHR) {
|
|
||||||
remote.appendSessionId(XHR);
|
|
||||||
},
|
|
||||||
error: function (request, error_string, exception) {
|
|
||||||
remote.ajaxError(request, error_string, exception, ajaxSettings);
|
|
||||||
},
|
|
||||||
success: callback,
|
|
||||||
context: context,
|
|
||||||
async: async
|
|
||||||
};
|
|
||||||
|
|
||||||
$.ajax(ajaxSettings);
|
|
||||||
},
|
|
||||||
|
|
||||||
loadDaemonPrefs: function (callback, context, async) {
|
|
||||||
var o = {
|
|
||||||
method: 'session-get'
|
|
||||||
};
|
|
||||||
this.sendRequest(o, callback, context, async);
|
|
||||||
},
|
|
||||||
|
|
||||||
checkPort: function (callback, context, async) {
|
|
||||||
var o = {
|
|
||||||
method: 'port-test'
|
|
||||||
};
|
|
||||||
this.sendRequest(o, callback, context, async);
|
|
||||||
},
|
|
||||||
|
|
||||||
renameTorrent: function (torrentIds, oldpath, newname, callback, context) {
|
|
||||||
var o = {
|
|
||||||
method: 'torrent-rename-path',
|
|
||||||
arguments: {
|
|
||||||
'ids': torrentIds,
|
|
||||||
'path': oldpath,
|
|
||||||
'name': newname
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.sendRequest(o, callback, context);
|
|
||||||
},
|
|
||||||
|
|
||||||
loadDaemonStats: function (callback, context, async) {
|
|
||||||
var o = {
|
|
||||||
method: 'session-stats'
|
|
||||||
};
|
|
||||||
this.sendRequest(o, callback, context, async);
|
|
||||||
},
|
|
||||||
|
|
||||||
updateTorrents: function (torrentIds, fields, callback, context) {
|
|
||||||
var o = {
|
|
||||||
method: 'torrent-get',
|
|
||||||
arguments: {
|
|
||||||
'fields': fields
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (torrentIds) {
|
|
||||||
o['arguments'].ids = torrentIds;
|
|
||||||
};
|
|
||||||
this.sendRequest(o, function (response) {
|
|
||||||
var args = response['arguments'];
|
|
||||||
callback.call(context, args.torrents, args.removed);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
getFreeSpace: function (dir, callback, context) {
|
|
||||||
var o = {
|
|
||||||
method: 'free-space',
|
|
||||||
arguments: {
|
|
||||||
path: dir
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.sendRequest(o, function (response) {
|
|
||||||
var args = response['arguments'];
|
|
||||||
callback.call(context, args.path, args['size-bytes']);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
changeFileCommand: function (torrentId, fileIndices, command) {
|
|
||||||
var remote = this,
|
|
||||||
args = {
|
|
||||||
ids: [torrentId]
|
|
||||||
};
|
|
||||||
args[command] = fileIndices;
|
|
||||||
this.sendRequest({
|
|
||||||
arguments: args,
|
|
||||||
method: 'torrent-set'
|
|
||||||
}, function () {
|
|
||||||
remote._controller.refreshTorrents([torrentId]);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
sendTorrentSetRequests: function (method, torrent_ids, args, callback, context) {
|
|
||||||
if (!args) {
|
|
||||||
args = {};
|
|
||||||
};
|
|
||||||
args['ids'] = torrent_ids;
|
|
||||||
var o = {
|
|
||||||
method: method,
|
|
||||||
arguments: args
|
|
||||||
};
|
|
||||||
this.sendRequest(o, callback, context);
|
|
||||||
},
|
|
||||||
|
|
||||||
sendTorrentActionRequests: function (method, torrent_ids, callback, context) {
|
|
||||||
this.sendTorrentSetRequests(method, torrent_ids, null, callback, context);
|
|
||||||
},
|
|
||||||
|
|
||||||
startTorrents: function (torrent_ids, noqueue, callback, context) {
|
|
||||||
var name = noqueue ? 'torrent-start-now' : 'torrent-start';
|
|
||||||
this.sendTorrentActionRequests(name, torrent_ids, callback, context);
|
|
||||||
},
|
|
||||||
stopTorrents: function (torrent_ids, callback, context) {
|
|
||||||
this.sendTorrentActionRequests('torrent-stop', torrent_ids, callback, context);
|
|
||||||
},
|
|
||||||
|
|
||||||
moveTorrents: function (torrent_ids, new_location, callback, context) {
|
|
||||||
this.sendTorrentSetRequests('torrent-set-location', torrent_ids, {
|
|
||||||
"move": true,
|
|
||||||
"location": new_location
|
|
||||||
}, callback, context);
|
|
||||||
},
|
|
||||||
|
|
||||||
removeTorrents: function (torrent_ids, callback, context) {
|
|
||||||
this.sendTorrentActionRequests('torrent-remove', torrent_ids, callback, context);
|
|
||||||
},
|
|
||||||
removeTorrentsAndData: function (torrents) {
|
|
||||||
var remote = this;
|
|
||||||
var o = {
|
|
||||||
method: 'torrent-remove',
|
|
||||||
arguments: {
|
|
||||||
'delete-local-data': true,
|
|
||||||
ids: []
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (torrents) {
|
|
||||||
for (var i = 0, len = torrents.length; i < len; ++i) {
|
|
||||||
o.arguments.ids.push(torrents[i].getId());
|
|
||||||
};
|
|
||||||
};
|
|
||||||
this.sendRequest(o, function () {
|
|
||||||
remote._controller.refreshTorrents();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
verifyTorrents: function (torrent_ids, callback, context) {
|
|
||||||
this.sendTorrentActionRequests('torrent-verify', torrent_ids, callback, context);
|
|
||||||
},
|
|
||||||
reannounceTorrents: function (torrent_ids, callback, context) {
|
|
||||||
this.sendTorrentActionRequests('torrent-reannounce', torrent_ids, callback, context);
|
|
||||||
},
|
|
||||||
addTorrentByUrl: function (url, options) {
|
|
||||||
var remote = this;
|
|
||||||
if (url.match(/^[0-9a-f]{40}$/i)) {
|
|
||||||
url = 'magnet:?xt=urn:btih:' + url;
|
|
||||||
}
|
|
||||||
var o = {
|
|
||||||
method: 'torrent-add',
|
|
||||||
arguments: {
|
|
||||||
paused: (options.paused),
|
|
||||||
filename: url
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.sendRequest(o, function () {
|
|
||||||
remote._controller.refreshTorrents();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
savePrefs: function (args) {
|
|
||||||
var remote = this;
|
|
||||||
var o = {
|
|
||||||
method: 'session-set',
|
|
||||||
arguments: args
|
|
||||||
};
|
|
||||||
this.sendRequest(o, function () {
|
|
||||||
remote._controller.loadDaemonPrefs();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
updateBlocklist: function () {
|
|
||||||
var remote = this;
|
|
||||||
var o = {
|
|
||||||
method: 'blocklist-update'
|
|
||||||
};
|
|
||||||
this.sendRequest(o, function () {
|
|
||||||
remote._controller.loadDaemonPrefs();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// Added queue calls
|
|
||||||
moveTorrentsToTop: function (torrent_ids, callback, context) {
|
|
||||||
this.sendTorrentActionRequests(RPC._QueueMoveTop, torrent_ids, callback, context);
|
|
||||||
},
|
|
||||||
moveTorrentsToBottom: function (torrent_ids, callback, context) {
|
|
||||||
this.sendTorrentActionRequests(RPC._QueueMoveBottom, torrent_ids, callback, context);
|
|
||||||
},
|
|
||||||
moveTorrentsUp: function (torrent_ids, callback, context) {
|
|
||||||
this.sendTorrentActionRequests(RPC._QueueMoveUp, torrent_ids, callback, context);
|
|
||||||
},
|
|
||||||
moveTorrentsDown: function (torrent_ids, callback, context) {
|
|
||||||
this.sendTorrentActionRequests(RPC._QueueMoveDown, torrent_ids, callback, context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
remote._error = request.responseText
|
||||||
|
? request.responseText.trim().replace(/(<([^>]+)>)/gi, '')
|
||||||
|
: '';
|
||||||
|
if (!remote._error.length) {
|
||||||
|
remote._error = 'Server not responding';
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.confirm(
|
||||||
|
'Connection Failed',
|
||||||
|
'Could not connect to the server. You may need to reload the page to reconnect.',
|
||||||
|
'Details',
|
||||||
|
function () {
|
||||||
|
alert(remote._error);
|
||||||
|
},
|
||||||
|
'Dismiss'
|
||||||
|
);
|
||||||
|
remote._controller.togglePeriodicSessionRefresh(false);
|
||||||
|
},
|
||||||
|
|
||||||
|
appendSessionId: function (XHR) {
|
||||||
|
if (this._token) {
|
||||||
|
XHR.setRequestHeader('X-Transmission-Session-Id', this._token);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
sendRequest: function (data, callback, context, async) {
|
||||||
|
const remote = this;
|
||||||
|
if (typeof async != 'boolean') {
|
||||||
|
async = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ajaxSettings = {
|
||||||
|
url: RPC._Root,
|
||||||
|
type: 'POST',
|
||||||
|
contentType: 'json',
|
||||||
|
dataType: 'json',
|
||||||
|
cache: false,
|
||||||
|
data: JSON.stringify(data),
|
||||||
|
beforeSend: function (XHR) {
|
||||||
|
remote.appendSessionId(XHR);
|
||||||
|
},
|
||||||
|
error: function (request, error_string, exception) {
|
||||||
|
remote.ajaxError(request, error_string, exception, ajaxSettings);
|
||||||
|
},
|
||||||
|
success: callback,
|
||||||
|
context: context,
|
||||||
|
async: async,
|
||||||
|
};
|
||||||
|
|
||||||
|
$.ajax(ajaxSettings);
|
||||||
|
},
|
||||||
|
|
||||||
|
loadDaemonPrefs: function (callback, context, async) {
|
||||||
|
const o = {
|
||||||
|
method: 'session-get',
|
||||||
|
};
|
||||||
|
this.sendRequest(o, callback, context, async);
|
||||||
|
},
|
||||||
|
|
||||||
|
checkPort: function (callback, context, async) {
|
||||||
|
const o = {
|
||||||
|
method: 'port-test',
|
||||||
|
};
|
||||||
|
this.sendRequest(o, callback, context, async);
|
||||||
|
},
|
||||||
|
|
||||||
|
renameTorrent: function (torrentIds, oldpath, newname, callback, context) {
|
||||||
|
const o = {
|
||||||
|
method: 'torrent-rename-path',
|
||||||
|
arguments: {
|
||||||
|
ids: torrentIds,
|
||||||
|
path: oldpath,
|
||||||
|
name: newname,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
this.sendRequest(o, callback, context);
|
||||||
|
},
|
||||||
|
|
||||||
|
loadDaemonStats: function (callback, context, async) {
|
||||||
|
const o = {
|
||||||
|
method: 'session-stats',
|
||||||
|
};
|
||||||
|
this.sendRequest(o, callback, context, async);
|
||||||
|
},
|
||||||
|
|
||||||
|
updateTorrents: function (torrentIds, fields, callback, context) {
|
||||||
|
const o = {
|
||||||
|
method: 'torrent-get',
|
||||||
|
arguments: {
|
||||||
|
fields: fields,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (torrentIds) {
|
||||||
|
o['arguments'].ids = torrentIds;
|
||||||
|
}
|
||||||
|
this.sendRequest(o, function (response) {
|
||||||
|
const args = response['arguments'];
|
||||||
|
callback.call(context, args.torrents, args.removed);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getFreeSpace: function (dir, callback, context) {
|
||||||
|
const o = {
|
||||||
|
method: 'free-space',
|
||||||
|
arguments: {
|
||||||
|
path: dir,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
this.sendRequest(o, function (response) {
|
||||||
|
const args = response['arguments'];
|
||||||
|
callback.call(context, args.path, args['size-bytes']);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
changeFileCommand: function (torrentId, fileIndices, command) {
|
||||||
|
const remote = this,
|
||||||
|
args = {
|
||||||
|
ids: [torrentId],
|
||||||
|
};
|
||||||
|
args[command] = fileIndices;
|
||||||
|
this.sendRequest(
|
||||||
|
{
|
||||||
|
arguments: args,
|
||||||
|
method: 'torrent-set',
|
||||||
|
},
|
||||||
|
function () {
|
||||||
|
remote._controller.refreshTorrents([torrentId]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
sendTorrentSetRequests: function (method, torrent_ids, args, callback, context) {
|
||||||
|
if (!args) {
|
||||||
|
args = {};
|
||||||
|
}
|
||||||
|
args['ids'] = torrent_ids;
|
||||||
|
const o = {
|
||||||
|
method: method,
|
||||||
|
arguments: args,
|
||||||
|
};
|
||||||
|
this.sendRequest(o, callback, context);
|
||||||
|
},
|
||||||
|
|
||||||
|
sendTorrentActionRequests: function (method, torrent_ids, callback, context) {
|
||||||
|
this.sendTorrentSetRequests(method, torrent_ids, null, callback, context);
|
||||||
|
},
|
||||||
|
|
||||||
|
startTorrents: function (torrent_ids, noqueue, callback, context) {
|
||||||
|
const name = noqueue ? 'torrent-start-now' : 'torrent-start';
|
||||||
|
this.sendTorrentActionRequests(name, torrent_ids, callback, context);
|
||||||
|
},
|
||||||
|
stopTorrents: function (torrent_ids, callback, context) {
|
||||||
|
this.sendTorrentActionRequests('torrent-stop', torrent_ids, callback, context);
|
||||||
|
},
|
||||||
|
|
||||||
|
moveTorrents: function (torrent_ids, new_location, callback, context) {
|
||||||
|
this.sendTorrentSetRequests(
|
||||||
|
'torrent-set-location',
|
||||||
|
torrent_ids,
|
||||||
|
{
|
||||||
|
move: true,
|
||||||
|
location: new_location,
|
||||||
|
},
|
||||||
|
callback,
|
||||||
|
context
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeTorrents: function (torrent_ids, callback, context) {
|
||||||
|
this.sendTorrentActionRequests('torrent-remove', torrent_ids, callback, context);
|
||||||
|
},
|
||||||
|
removeTorrentsAndData: function (torrents) {
|
||||||
|
const remote = this;
|
||||||
|
const o = {
|
||||||
|
method: 'torrent-remove',
|
||||||
|
arguments: {
|
||||||
|
'delete-local-data': true,
|
||||||
|
ids: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (torrents) {
|
||||||
|
for (let i = 0, len = torrents.length; i < len; ++i) {
|
||||||
|
o.arguments.ids.push(torrents[i].getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.sendRequest(o, function () {
|
||||||
|
remote._controller.refreshTorrents();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
verifyTorrents: function (torrent_ids, callback, context) {
|
||||||
|
this.sendTorrentActionRequests('torrent-verify', torrent_ids, callback, context);
|
||||||
|
},
|
||||||
|
reannounceTorrents: function (torrent_ids, callback, context) {
|
||||||
|
this.sendTorrentActionRequests('torrent-reannounce', torrent_ids, callback, context);
|
||||||
|
},
|
||||||
|
addTorrentByUrl: function (url, options) {
|
||||||
|
const remote = this;
|
||||||
|
if (url.match(/^[0-9a-f]{40}$/i)) {
|
||||||
|
url = 'magnet:?xt=urn:btih:' + url;
|
||||||
|
}
|
||||||
|
const o = {
|
||||||
|
method: 'torrent-add',
|
||||||
|
arguments: {
|
||||||
|
paused: options.paused,
|
||||||
|
filename: url,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
this.sendRequest(o, function () {
|
||||||
|
remote._controller.refreshTorrents();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
savePrefs: function (args) {
|
||||||
|
const remote = this;
|
||||||
|
const o = {
|
||||||
|
method: 'session-set',
|
||||||
|
arguments: args,
|
||||||
|
};
|
||||||
|
this.sendRequest(o, function () {
|
||||||
|
remote._controller.loadDaemonPrefs();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
updateBlocklist: function () {
|
||||||
|
const remote = this;
|
||||||
|
const o = {
|
||||||
|
method: 'blocklist-update',
|
||||||
|
};
|
||||||
|
this.sendRequest(o, function () {
|
||||||
|
remote._controller.loadDaemonPrefs();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Added queue calls
|
||||||
|
moveTorrentsToTop: function (torrent_ids, callback, context) {
|
||||||
|
this.sendTorrentActionRequests(RPC._QueueMoveTop, torrent_ids, callback, context);
|
||||||
|
},
|
||||||
|
moveTorrentsToBottom: function (torrent_ids, callback, context) {
|
||||||
|
this.sendTorrentActionRequests(RPC._QueueMoveBottom, torrent_ids, callback, context);
|
||||||
|
},
|
||||||
|
moveTorrentsUp: function (torrent_ids, callback, context) {
|
||||||
|
this.sendTorrentActionRequests(RPC._QueueMoveUp, torrent_ids, callback, context);
|
||||||
|
},
|
||||||
|
moveTorrentsDown: function (torrent_ids, callback, context) {
|
||||||
|
this.sendTorrentActionRequests(RPC._QueueMoveDown, torrent_ids, callback, context);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,111 +8,112 @@
|
||||||
function TorrentRendererHelper() {}
|
function TorrentRendererHelper() {}
|
||||||
|
|
||||||
TorrentRendererHelper.getProgressInfo = function (controller, t) {
|
TorrentRendererHelper.getProgressInfo = function (controller, t) {
|
||||||
var pct, extra;
|
let pct, extra;
|
||||||
var s = t.getStatus();
|
const s = t.getStatus();
|
||||||
var seed_ratio_limit = t.seedRatioLimit(controller);
|
const seed_ratio_limit = t.seedRatioLimit(controller);
|
||||||
|
|
||||||
if (t.needsMetaData()) {
|
if (t.needsMetaData()) {
|
||||||
pct = t.getMetadataPercentComplete() * 100;
|
pct = t.getMetadataPercentComplete() * 100;
|
||||||
} else if (!t.isDone()) {
|
} else if (!t.isDone()) {
|
||||||
pct = Math.round(t.getPercentDone() * 100);
|
pct = Math.round(t.getPercentDone() * 100);
|
||||||
} else if (seed_ratio_limit > 0 && t.isSeeding()) { // don't split up the bar if paused or queued
|
} else if (seed_ratio_limit > 0 && t.isSeeding()) {
|
||||||
pct = Math.round(t.getUploadRatio() * 100 / seed_ratio_limit);
|
// don't split up the bar if paused or queued
|
||||||
} else {
|
pct = Math.round((t.getUploadRatio() * 100) / seed_ratio_limit);
|
||||||
pct = 100;
|
} else {
|
||||||
};
|
pct = 100;
|
||||||
|
}
|
||||||
|
|
||||||
if (s === Torrent._StatusStopped) {
|
if (s === Torrent._StatusStopped) {
|
||||||
extra = 'paused';
|
extra = 'paused';
|
||||||
} else if (s === Torrent._StatusDownloadWait) {
|
} else if (s === Torrent._StatusDownloadWait) {
|
||||||
extra = 'leeching queued';
|
extra = 'leeching queued';
|
||||||
} else if (t.needsMetaData()) {
|
} else if (t.needsMetaData()) {
|
||||||
extra = 'magnet';
|
extra = 'magnet';
|
||||||
} else if (s === Torrent._StatusDownload) {
|
} else if (s === Torrent._StatusDownload) {
|
||||||
extra = 'leeching';
|
extra = 'leeching';
|
||||||
} else if (s === Torrent._StatusSeedWait) {
|
} else if (s === Torrent._StatusSeedWait) {
|
||||||
extra = 'seeding queued';
|
extra = 'seeding queued';
|
||||||
} else if (s === Torrent._StatusSeed) {
|
} else if (s === Torrent._StatusSeed) {
|
||||||
extra = 'seeding';
|
extra = 'seeding';
|
||||||
} else {
|
} else {
|
||||||
extra = '';
|
extra = '';
|
||||||
};
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
percent: pct,
|
percent: pct,
|
||||||
complete: ['torrent_progress_bar', 'complete', extra].join(' '),
|
complete: ['torrent_progress_bar', 'complete', extra].join(' '),
|
||||||
incomplete: ['torrent_progress_bar', 'incomplete', extra].join(' ')
|
incomplete: ['torrent_progress_bar', 'incomplete', extra].join(' '),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
TorrentRendererHelper.createProgressbar = function (classes) {
|
TorrentRendererHelper.createProgressbar = function (classes) {
|
||||||
var complete, incomplete, progressbar;
|
let complete, incomplete, progressbar;
|
||||||
|
|
||||||
complete = document.createElement('div');
|
complete = document.createElement('div');
|
||||||
complete.className = 'torrent_progress_bar complete';
|
complete.className = 'torrent_progress_bar complete';
|
||||||
|
|
||||||
incomplete = document.createElement('div');
|
incomplete = document.createElement('div');
|
||||||
incomplete.className = 'torrent_progress_bar incomplete';
|
incomplete.className = 'torrent_progress_bar incomplete';
|
||||||
|
|
||||||
progressbar = document.createElement('div');
|
progressbar = document.createElement('div');
|
||||||
progressbar.className = 'torrent_progress_bar_container ' + classes;
|
progressbar.className = 'torrent_progress_bar_container ' + classes;
|
||||||
progressbar.appendChild(complete);
|
progressbar.appendChild(complete);
|
||||||
progressbar.appendChild(incomplete);
|
progressbar.appendChild(incomplete);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'element': progressbar,
|
element: progressbar,
|
||||||
'complete': complete,
|
complete: complete,
|
||||||
'incomplete': incomplete
|
incomplete: incomplete,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
TorrentRendererHelper.renderProgressbar = function (controller, t, progressbar) {
|
TorrentRendererHelper.renderProgressbar = function (controller, t, progressbar) {
|
||||||
var e, style, width, display;
|
let e, style, width, display;
|
||||||
var info = TorrentRendererHelper.getProgressInfo(controller, t);
|
const info = TorrentRendererHelper.getProgressInfo(controller, t);
|
||||||
|
|
||||||
// update the complete progressbar
|
// update the complete progressbar
|
||||||
e = progressbar.complete;
|
e = progressbar.complete;
|
||||||
style = e.style;
|
style = e.style;
|
||||||
width = '' + info.percent + '%';
|
width = '' + info.percent + '%';
|
||||||
display = info.percent > 0 ? 'block' : 'none';
|
display = info.percent > 0 ? 'block' : 'none';
|
||||||
if (style.width !== width || style.display !== display) {
|
if (style.width !== width || style.display !== display) {
|
||||||
$(e).css({
|
$(e).css({
|
||||||
width: '' + info.percent + '%',
|
width: '' + info.percent + '%',
|
||||||
display: display
|
display: display,
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
if (e.className !== info.complete) {
|
if (e.className !== info.complete) {
|
||||||
e.className = info.complete;
|
e.className = info.complete;
|
||||||
};
|
}
|
||||||
|
|
||||||
// update the incomplete progressbar
|
// update the incomplete progressbar
|
||||||
e = progressbar.incomplete;
|
e = progressbar.incomplete;
|
||||||
display = (info.percent < 100) ? 'block' : 'none';
|
display = info.percent < 100 ? 'block' : 'none';
|
||||||
|
|
||||||
if (e.style.display !== display) {
|
if (e.style.display !== display) {
|
||||||
e.style.display = display;
|
e.style.display = display;
|
||||||
};
|
}
|
||||||
|
|
||||||
if (e.className !== info.incomplete) {
|
if (e.className !== info.incomplete) {
|
||||||
e.className = info.incomplete;
|
e.className = info.incomplete;
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TorrentRendererHelper.formatUL = function (t) {
|
TorrentRendererHelper.formatUL = function (t) {
|
||||||
return '▲' + Transmission.fmt.speedBps(t.getUploadSpeed());
|
return '▲' + Transmission.fmt.speedBps(t.getUploadSpeed());
|
||||||
};
|
};
|
||||||
|
|
||||||
TorrentRendererHelper.formatDL = function (t) {
|
TorrentRendererHelper.formatDL = function (t) {
|
||||||
return '▼' + Transmission.fmt.speedBps(t.getDownloadSpeed());
|
return '▼' + Transmission.fmt.speedBps(t.getDownloadSpeed());
|
||||||
};
|
};
|
||||||
|
|
||||||
TorrentRendererHelper.formatETA = function (t) {
|
TorrentRendererHelper.formatETA = function (t) {
|
||||||
var eta = t.getETA();
|
const eta = t.getETA();
|
||||||
if (eta < 0 || eta >= (999 * 60 * 60)) {
|
if (eta < 0 || eta >= 999 * 60 * 60) {
|
||||||
return "";
|
return '';
|
||||||
};
|
}
|
||||||
return "ETA: " + Transmission.fmt.timeInterval(eta);
|
return 'ETA: ' + Transmission.fmt.timeInterval(eta);
|
||||||
};
|
};
|
||||||
|
|
||||||
/****
|
/****
|
||||||
|
@ -120,178 +121,208 @@ TorrentRendererHelper.formatETA = function (t) {
|
||||||
*****
|
*****
|
||||||
****/
|
****/
|
||||||
|
|
||||||
function TorrentRendererFull() {};
|
function TorrentRendererFull() {}
|
||||||
TorrentRendererFull.prototype = {
|
TorrentRendererFull.prototype = {
|
||||||
createRow: function () {
|
createRow: function () {
|
||||||
var root, name, peers, progressbar, details, image, button;
|
let root, name, peers, progressbar, details, image, button;
|
||||||
|
|
||||||
root = document.createElement('li');
|
root = document.createElement('li');
|
||||||
root.className = 'torrent';
|
root.className = 'torrent';
|
||||||
|
|
||||||
name = document.createElement('div');
|
name = document.createElement('div');
|
||||||
name.className = 'torrent_name';
|
name.className = 'torrent_name';
|
||||||
|
|
||||||
peers = document.createElement('div');
|
peers = document.createElement('div');
|
||||||
peers.className = 'torrent_peer_details';
|
peers.className = 'torrent_peer_details';
|
||||||
|
|
||||||
progressbar = TorrentRendererHelper.createProgressbar('full');
|
progressbar = TorrentRendererHelper.createProgressbar('full');
|
||||||
|
|
||||||
details = document.createElement('div');
|
details = document.createElement('div');
|
||||||
details.className = 'torrent_progress_details';
|
details.className = 'torrent_progress_details';
|
||||||
|
|
||||||
image = document.createElement('div');
|
image = document.createElement('div');
|
||||||
button = document.createElement('a');
|
button = document.createElement('a');
|
||||||
button.appendChild(image);
|
button.appendChild(image);
|
||||||
|
|
||||||
root.appendChild(name);
|
root.appendChild(name);
|
||||||
root.appendChild(peers);
|
root.appendChild(peers);
|
||||||
root.appendChild(button);
|
root.appendChild(button);
|
||||||
root.appendChild(progressbar.element);
|
root.appendChild(progressbar.element);
|
||||||
root.appendChild(details);
|
root.appendChild(details);
|
||||||
|
|
||||||
root._name_container = name;
|
root._name_container = name;
|
||||||
root._peer_details_container = peers;
|
root._peer_details_container = peers;
|
||||||
root._progress_details_container = details;
|
root._progress_details_container = details;
|
||||||
root._progressbar = progressbar;
|
root._progressbar = progressbar;
|
||||||
root._pause_resume_button_image = image;
|
root._pause_resume_button_image = image;
|
||||||
root._toggle_running_button = button;
|
root._toggle_running_button = button;
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
},
|
},
|
||||||
|
|
||||||
getPeerDetails: function (t) {
|
getPeerDetails: function (t) {
|
||||||
var err,
|
let err,
|
||||||
peer_count,
|
peer_count,
|
||||||
webseed_count,
|
webseed_count,
|
||||||
fmt = Transmission.fmt;
|
fmt = Transmission.fmt;
|
||||||
|
|
||||||
if ((err = t.getErrorMessage())) {
|
if ((err = t.getErrorMessage())) {
|
||||||
return err;
|
return err;
|
||||||
};
|
|
||||||
|
|
||||||
if (t.isDownloading()) {
|
|
||||||
peer_count = t.getPeersConnected();
|
|
||||||
webseed_count = t.getWebseedsSendingToUs();
|
|
||||||
|
|
||||||
if (webseed_count && peer_count) {
|
|
||||||
// Downloading from 2 of 3 peer(s) and 2 webseed(s)
|
|
||||||
return ['Downloading from',
|
|
||||||
t.getPeersSendingToUs(),
|
|
||||||
'of',
|
|
||||||
fmt.countString('peer', 'peers', peer_count),
|
|
||||||
'and',
|
|
||||||
fmt.countString('web seed', 'web seeds', webseed_count),
|
|
||||||
'–',
|
|
||||||
TorrentRendererHelper.formatDL(t),
|
|
||||||
TorrentRendererHelper.formatUL(t)
|
|
||||||
].join(' ');
|
|
||||||
} else if (webseed_count) {
|
|
||||||
// Downloading from 2 webseed(s)
|
|
||||||
return ['Downloading from',
|
|
||||||
fmt.countString('web seed', 'web seeds', webseed_count),
|
|
||||||
'–',
|
|
||||||
TorrentRendererHelper.formatDL(t),
|
|
||||||
TorrentRendererHelper.formatUL(t)
|
|
||||||
].join(' ');
|
|
||||||
} else {
|
|
||||||
// Downloading from 2 of 3 peer(s)
|
|
||||||
return ['Downloading from',
|
|
||||||
t.getPeersSendingToUs(),
|
|
||||||
'of',
|
|
||||||
fmt.countString('peer', 'peers', peer_count),
|
|
||||||
'–',
|
|
||||||
TorrentRendererHelper.formatDL(t),
|
|
||||||
TorrentRendererHelper.formatUL(t)
|
|
||||||
].join(' ');
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
if (t.isSeeding()) {
|
|
||||||
return ['Seeding to', t.getPeersGettingFromUs(), 'of', fmt.countString('peer', 'peers', t.getPeersConnected()), '-', TorrentRendererHelper.formatUL(t)].join(' ');
|
|
||||||
};
|
|
||||||
|
|
||||||
if (t.isChecking()) {
|
|
||||||
return ['Verifying local data (', Transmission.fmt.percentString(100.0 * t.getRecheckProgress()), '% tested)'].join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
return t.getStateString();
|
|
||||||
},
|
|
||||||
|
|
||||||
getProgressDetails: function (controller, t) {
|
|
||||||
if (t.needsMetaData()) {
|
|
||||||
var MetaDataStatus = "retrieving";
|
|
||||||
if (t.isStopped()) {
|
|
||||||
MetaDataStatus = "needs";
|
|
||||||
};
|
|
||||||
var percent = 100 * t.getMetadataPercentComplete();
|
|
||||||
return ["Magnetized transfer - " + MetaDataStatus + " metadata (",
|
|
||||||
Transmission.fmt.percentString(percent),
|
|
||||||
"%)"
|
|
||||||
].join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
var c;
|
|
||||||
var sizeWhenDone = t.getSizeWhenDone();
|
|
||||||
var totalSize = t.getTotalSize();
|
|
||||||
var is_done = t.isDone() || t.isSeeding();
|
|
||||||
|
|
||||||
if (is_done) {
|
|
||||||
if (totalSize === sizeWhenDone) {
|
|
||||||
// seed: '698.05 MiB'
|
|
||||||
c = [Transmission.fmt.size(totalSize)];
|
|
||||||
} else { // partial seed: '127.21 MiB of 698.05 MiB (18.2%)'
|
|
||||||
c = [Transmission.fmt.size(sizeWhenDone), ' of ', Transmission.fmt.size(t.getTotalSize()), ' (', t.getPercentDoneStr(), '%)'];
|
|
||||||
};
|
|
||||||
// append UL stats: ', uploaded 8.59 GiB (Ratio: 12.3)'
|
|
||||||
c.push(', uploaded ',
|
|
||||||
Transmission.fmt.size(t.getUploadedEver()),
|
|
||||||
' (Ratio ',
|
|
||||||
Transmission.fmt.ratioString(t.getUploadRatio()),
|
|
||||||
')');
|
|
||||||
} else { // not done yet
|
|
||||||
c = [Transmission.fmt.size(sizeWhenDone - t.getLeftUntilDone()),
|
|
||||||
' of ', Transmission.fmt.size(sizeWhenDone),
|
|
||||||
' (', t.getPercentDoneStr(), '%)'
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
// maybe append eta
|
|
||||||
if (!t.isStopped() && (!is_done || t.seedRatioLimit(controller) > 0)) {
|
|
||||||
c.push(' - ');
|
|
||||||
var eta = t.getETA();
|
|
||||||
if (eta < 0 || eta >= (999 * 60 * 60) /* arbitrary */ ) {
|
|
||||||
c.push('remaining time unknown');
|
|
||||||
} else {
|
|
||||||
c.push(Transmission.fmt.timeInterval(t.getETA()), ' remaining');
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
return c.join('');
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function (controller, t, root) {
|
|
||||||
// name
|
|
||||||
setTextContent(root._name_container, t.getName());
|
|
||||||
|
|
||||||
// progressbar
|
|
||||||
TorrentRendererHelper.renderProgressbar(controller, t, root._progressbar);
|
|
||||||
|
|
||||||
// peer details
|
|
||||||
var has_error = t.getError() !== Torrent._ErrNone;
|
|
||||||
var e = root._peer_details_container;
|
|
||||||
$(e).toggleClass('error', has_error);
|
|
||||||
setTextContent(e, this.getPeerDetails(t));
|
|
||||||
|
|
||||||
// progress details
|
|
||||||
e = root._progress_details_container;
|
|
||||||
setTextContent(e, this.getProgressDetails(controller, t));
|
|
||||||
|
|
||||||
// pause/resume button
|
|
||||||
var is_stopped = t.isStopped();
|
|
||||||
e = root._pause_resume_button_image;
|
|
||||||
e.alt = is_stopped ? 'Resume' : 'Pause';
|
|
||||||
e.className = is_stopped ? 'torrent_resume' : 'torrent_pause';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (t.isDownloading()) {
|
||||||
|
peer_count = t.getPeersConnected();
|
||||||
|
webseed_count = t.getWebseedsSendingToUs();
|
||||||
|
|
||||||
|
if (webseed_count && peer_count) {
|
||||||
|
// Downloading from 2 of 3 peer(s) and 2 webseed(s)
|
||||||
|
return [
|
||||||
|
'Downloading from',
|
||||||
|
t.getPeersSendingToUs(),
|
||||||
|
'of',
|
||||||
|
fmt.countString('peer', 'peers', peer_count),
|
||||||
|
'and',
|
||||||
|
fmt.countString('web seed', 'web seeds', webseed_count),
|
||||||
|
'–',
|
||||||
|
TorrentRendererHelper.formatDL(t),
|
||||||
|
TorrentRendererHelper.formatUL(t),
|
||||||
|
].join(' ');
|
||||||
|
} else if (webseed_count) {
|
||||||
|
// Downloading from 2 webseed(s)
|
||||||
|
return [
|
||||||
|
'Downloading from',
|
||||||
|
fmt.countString('web seed', 'web seeds', webseed_count),
|
||||||
|
'–',
|
||||||
|
TorrentRendererHelper.formatDL(t),
|
||||||
|
TorrentRendererHelper.formatUL(t),
|
||||||
|
].join(' ');
|
||||||
|
} else {
|
||||||
|
// Downloading from 2 of 3 peer(s)
|
||||||
|
return [
|
||||||
|
'Downloading from',
|
||||||
|
t.getPeersSendingToUs(),
|
||||||
|
'of',
|
||||||
|
fmt.countString('peer', 'peers', peer_count),
|
||||||
|
'–',
|
||||||
|
TorrentRendererHelper.formatDL(t),
|
||||||
|
TorrentRendererHelper.formatUL(t),
|
||||||
|
].join(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.isSeeding()) {
|
||||||
|
return [
|
||||||
|
'Seeding to',
|
||||||
|
t.getPeersGettingFromUs(),
|
||||||
|
'of',
|
||||||
|
fmt.countString('peer', 'peers', t.getPeersConnected()),
|
||||||
|
'-',
|
||||||
|
TorrentRendererHelper.formatUL(t),
|
||||||
|
].join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.isChecking()) {
|
||||||
|
return [
|
||||||
|
'Verifying local data (',
|
||||||
|
Transmission.fmt.percentString(100.0 * t.getRecheckProgress()),
|
||||||
|
'% tested)',
|
||||||
|
].join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.getStateString();
|
||||||
|
},
|
||||||
|
|
||||||
|
getProgressDetails: function (controller, t) {
|
||||||
|
if (t.needsMetaData()) {
|
||||||
|
let MetaDataStatus = 'retrieving';
|
||||||
|
if (t.isStopped()) {
|
||||||
|
MetaDataStatus = 'needs';
|
||||||
|
}
|
||||||
|
const percent = 100 * t.getMetadataPercentComplete();
|
||||||
|
return [
|
||||||
|
'Magnetized transfer - ' + MetaDataStatus + ' metadata (',
|
||||||
|
Transmission.fmt.percentString(percent),
|
||||||
|
'%)',
|
||||||
|
].join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
let c;
|
||||||
|
const sizeWhenDone = t.getSizeWhenDone();
|
||||||
|
const totalSize = t.getTotalSize();
|
||||||
|
const is_done = t.isDone() || t.isSeeding();
|
||||||
|
|
||||||
|
if (is_done) {
|
||||||
|
if (totalSize === sizeWhenDone) {
|
||||||
|
// seed: '698.05 MiB'
|
||||||
|
c = [Transmission.fmt.size(totalSize)];
|
||||||
|
} else {
|
||||||
|
// partial seed: '127.21 MiB of 698.05 MiB (18.2%)'
|
||||||
|
c = [
|
||||||
|
Transmission.fmt.size(sizeWhenDone),
|
||||||
|
' of ',
|
||||||
|
Transmission.fmt.size(t.getTotalSize()),
|
||||||
|
' (',
|
||||||
|
t.getPercentDoneStr(),
|
||||||
|
'%)',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
// append UL stats: ', uploaded 8.59 GiB (Ratio: 12.3)'
|
||||||
|
c.push(
|
||||||
|
', uploaded ',
|
||||||
|
Transmission.fmt.size(t.getUploadedEver()),
|
||||||
|
' (Ratio ',
|
||||||
|
Transmission.fmt.ratioString(t.getUploadRatio()),
|
||||||
|
')'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// not done yet
|
||||||
|
c = [
|
||||||
|
Transmission.fmt.size(sizeWhenDone - t.getLeftUntilDone()),
|
||||||
|
' of ',
|
||||||
|
Transmission.fmt.size(sizeWhenDone),
|
||||||
|
' (',
|
||||||
|
t.getPercentDoneStr(),
|
||||||
|
'%)',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// maybe append eta
|
||||||
|
if (!t.isStopped() && (!is_done || t.seedRatioLimit(controller) > 0)) {
|
||||||
|
c.push(' - ');
|
||||||
|
const eta = t.getETA();
|
||||||
|
if (eta < 0 || eta >= 999 * 60 * 60 /* arbitrary */) {
|
||||||
|
c.push('remaining time unknown');
|
||||||
|
} else {
|
||||||
|
c.push(Transmission.fmt.timeInterval(t.getETA()), ' remaining');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.join('');
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function (controller, t, root) {
|
||||||
|
// name
|
||||||
|
setTextContent(root._name_container, t.getName());
|
||||||
|
|
||||||
|
// progressbar
|
||||||
|
TorrentRendererHelper.renderProgressbar(controller, t, root._progressbar);
|
||||||
|
|
||||||
|
// peer details
|
||||||
|
const has_error = t.getError() !== Torrent._ErrNone;
|
||||||
|
let e = root._peer_details_container;
|
||||||
|
$(e).toggleClass('error', has_error);
|
||||||
|
setTextContent(e, this.getPeerDetails(t));
|
||||||
|
|
||||||
|
// progress details
|
||||||
|
e = root._progress_details_container;
|
||||||
|
setTextContent(e, this.getProgressDetails(controller, t));
|
||||||
|
|
||||||
|
// pause/resume button
|
||||||
|
const is_stopped = t.isStopped();
|
||||||
|
e = root._pause_resume_button_image;
|
||||||
|
e.alt = is_stopped ? 'Resume' : 'Pause';
|
||||||
|
e.className = is_stopped ? 'torrent_resume' : 'torrent_pause';
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/****
|
/****
|
||||||
|
@ -299,79 +330,84 @@ TorrentRendererFull.prototype = {
|
||||||
*****
|
*****
|
||||||
****/
|
****/
|
||||||
|
|
||||||
function TorrentRendererCompact() {};
|
function TorrentRendererCompact() {}
|
||||||
TorrentRendererCompact.prototype = {
|
TorrentRendererCompact.prototype = {
|
||||||
createRow: function () {
|
createRow: function () {
|
||||||
var progressbar, details, name, root;
|
let progressbar, details, name, root;
|
||||||
|
|
||||||
progressbar = TorrentRendererHelper.createProgressbar('compact');
|
progressbar = TorrentRendererHelper.createProgressbar('compact');
|
||||||
|
|
||||||
details = document.createElement('div');
|
details = document.createElement('div');
|
||||||
details.className = 'torrent_peer_details compact';
|
details.className = 'torrent_peer_details compact';
|
||||||
|
|
||||||
name = document.createElement('div');
|
name = document.createElement('div');
|
||||||
name.className = 'torrent_name compact';
|
name.className = 'torrent_name compact';
|
||||||
|
|
||||||
root = document.createElement('li');
|
root = document.createElement('li');
|
||||||
root.appendChild(progressbar.element);
|
root.appendChild(progressbar.element);
|
||||||
root.appendChild(details);
|
root.appendChild(details);
|
||||||
root.appendChild(name);
|
root.appendChild(name);
|
||||||
root.className = 'torrent compact';
|
root.className = 'torrent compact';
|
||||||
root._progressbar = progressbar;
|
root._progressbar = progressbar;
|
||||||
root._details_container = details;
|
root._details_container = details;
|
||||||
root._name_container = name;
|
root._name_container = name;
|
||||||
return root;
|
return root;
|
||||||
},
|
},
|
||||||
|
|
||||||
getPeerDetails: function (t) {
|
getPeerDetails: function (t) {
|
||||||
var c;
|
let c;
|
||||||
if ((c = t.getErrorMessage())) {
|
if ((c = t.getErrorMessage())) {
|
||||||
return c;
|
return c;
|
||||||
};
|
|
||||||
if (t.isDownloading()) {
|
|
||||||
var have_dn = t.getDownloadSpeed() > 0;
|
|
||||||
var have_up = t.getUploadSpeed() > 0;
|
|
||||||
|
|
||||||
if (!have_up && !have_dn) {
|
|
||||||
return 'Idle';
|
|
||||||
};
|
|
||||||
var s = '';
|
|
||||||
if (!isMobileDevice) {
|
|
||||||
s = TorrentRendererHelper.formatETA(t) + ' ';
|
|
||||||
};
|
|
||||||
if (have_dn) {
|
|
||||||
s += TorrentRendererHelper.formatDL(t);
|
|
||||||
};
|
|
||||||
if (have_dn && have_up) {
|
|
||||||
s += ' ';
|
|
||||||
};
|
|
||||||
if (have_up) {
|
|
||||||
s += TorrentRendererHelper.formatUL(t);
|
|
||||||
};
|
|
||||||
return s;
|
|
||||||
};
|
|
||||||
if (t.isSeeding()) {
|
|
||||||
return ['Ratio: ', Transmission.fmt.ratioString(t.getUploadRatio()), ', ', TorrentRendererHelper.formatUL(t)].join('');
|
|
||||||
};
|
|
||||||
return t.getStateString();
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function (controller, t, root) {
|
|
||||||
// name
|
|
||||||
var is_stopped = t.isStopped();
|
|
||||||
var e = root._name_container;
|
|
||||||
$(e).toggleClass('paused', is_stopped);
|
|
||||||
setTextContent(e, t.getName());
|
|
||||||
|
|
||||||
// peer details
|
|
||||||
var has_error = t.getError() !== Torrent._ErrNone;
|
|
||||||
e = root._details_container;
|
|
||||||
$(e).toggleClass('error', has_error);
|
|
||||||
setTextContent(e, this.getPeerDetails(t));
|
|
||||||
|
|
||||||
// progressbar
|
|
||||||
TorrentRendererHelper.renderProgressbar(controller, t, root._progressbar);
|
|
||||||
}
|
}
|
||||||
|
if (t.isDownloading()) {
|
||||||
|
const have_dn = t.getDownloadSpeed() > 0;
|
||||||
|
const have_up = t.getUploadSpeed() > 0;
|
||||||
|
|
||||||
|
if (!have_up && !have_dn) {
|
||||||
|
return 'Idle';
|
||||||
|
}
|
||||||
|
let s = '';
|
||||||
|
if (!isMobileDevice) {
|
||||||
|
s = TorrentRendererHelper.formatETA(t) + ' ';
|
||||||
|
}
|
||||||
|
if (have_dn) {
|
||||||
|
s += TorrentRendererHelper.formatDL(t);
|
||||||
|
}
|
||||||
|
if (have_dn && have_up) {
|
||||||
|
s += ' ';
|
||||||
|
}
|
||||||
|
if (have_up) {
|
||||||
|
s += TorrentRendererHelper.formatUL(t);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
if (t.isSeeding()) {
|
||||||
|
return [
|
||||||
|
'Ratio: ',
|
||||||
|
Transmission.fmt.ratioString(t.getUploadRatio()),
|
||||||
|
', ',
|
||||||
|
TorrentRendererHelper.formatUL(t),
|
||||||
|
].join('');
|
||||||
|
}
|
||||||
|
return t.getStateString();
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function (controller, t, root) {
|
||||||
|
// name
|
||||||
|
const is_stopped = t.isStopped();
|
||||||
|
let e = root._name_container;
|
||||||
|
$(e).toggleClass('paused', is_stopped);
|
||||||
|
setTextContent(e, t.getName());
|
||||||
|
|
||||||
|
// peer details
|
||||||
|
const has_error = t.getError() !== Torrent._ErrNone;
|
||||||
|
e = root._details_container;
|
||||||
|
$(e).toggleClass('error', has_error);
|
||||||
|
setTextContent(e, this.getPeerDetails(t));
|
||||||
|
|
||||||
|
// progressbar
|
||||||
|
TorrentRendererHelper.renderProgressbar(controller, t, root._progressbar);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/****
|
/****
|
||||||
|
@ -380,37 +416,36 @@ TorrentRendererCompact.prototype = {
|
||||||
****/
|
****/
|
||||||
|
|
||||||
function TorrentRow(view, controller, torrent) {
|
function TorrentRow(view, controller, torrent) {
|
||||||
this.initialize(view, controller, torrent);
|
this.initialize(view, controller, torrent);
|
||||||
};
|
}
|
||||||
TorrentRow.prototype = {
|
TorrentRow.prototype = {
|
||||||
initialize: function (view, controller, torrent) {
|
initialize: function (view, controller, torrent) {
|
||||||
var row = this;
|
const row = this;
|
||||||
this._view = view;
|
this._view = view;
|
||||||
this._torrent = torrent;
|
this._torrent = torrent;
|
||||||
this._element = view.createRow();
|
this._element = view.createRow();
|
||||||
this.render(controller);
|
this.render(controller);
|
||||||
$(this._torrent).bind('dataChanged.torrentRowListener', function () {
|
$(this._torrent).bind('dataChanged.torrentRowListener', function () {
|
||||||
row.render(controller);
|
row.render(controller);
|
||||||
});
|
});
|
||||||
|
},
|
||||||
},
|
getElement: function () {
|
||||||
getElement: function () {
|
return this._element;
|
||||||
return this._element;
|
},
|
||||||
},
|
render: function (controller) {
|
||||||
render: function (controller) {
|
const tor = this.getTorrent();
|
||||||
var tor = this.getTorrent();
|
if (tor) {
|
||||||
if (tor) {
|
this._view.render(controller, tor, this.getElement());
|
||||||
this._view.render(controller, tor, this.getElement());
|
|
||||||
};
|
|
||||||
},
|
|
||||||
isSelected: function () {
|
|
||||||
return this.getElement().className.indexOf('selected') !== -1;
|
|
||||||
},
|
|
||||||
|
|
||||||
getTorrent: function () {
|
|
||||||
return this._torrent;
|
|
||||||
},
|
|
||||||
getTorrentId: function () {
|
|
||||||
return this.getTorrent().getId();
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
isSelected: function () {
|
||||||
|
return this.getElement().className.indexOf('selected') !== -1;
|
||||||
|
},
|
||||||
|
|
||||||
|
getTorrent: function () {
|
||||||
|
return this._torrent;
|
||||||
|
},
|
||||||
|
getTorrentId: function () {
|
||||||
|
return this.getTorrent().getId();
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue