From 94ed23e74b79938ae299151e0b76f76381df7dd5 Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 20 Mar 2017 16:51:17 +0000 Subject: [PATCH] Initial checkin, v0.1 --- CHANGES.txt | 2 + LICENSE.txt | 23 + README.txt | 13 + build.xml | 69 + plugin/licenses/JSON.txt | 157 + plugin/licenses/LICENSE-GPLv2.txt | 340 + plugin/licenses/Transmission.txt | 19 + scripts/makeplugin.sh | 138 + scripts/plugin.config | 12 + src/build.xml | 51 + .../plugins/xmwebui/TransmissionVars.java | 376 ++ .../plugins/download/DownloadException.java | 49 + src/java/org/json/simple/ItemList.java | 205 + src/java/org/json/simple/JSONArray.java | 72 + src/java/org/json/simple/JSONObject.java | 207 + src/java/org/json/simple/JSONValue.java | 46 + .../org/json/simple/parser/JSONParser.java | 190 + src/java/org/json/simple/parser/Yylex.java | 428 ++ src/java/org/json/simple/parser/Yytoken.java | 31 + .../rpc/DownloadWillBeAddedListener.java | 36 + src/java/org/klomp/snark/rpc/JSONUtils.java | 201 + src/java/org/klomp/snark/rpc/MapUtils.java | 238 + src/java/org/klomp/snark/rpc/RPCServlet.java | 66 + .../org/klomp/snark/rpc/TextualException.java | 30 + src/java/org/klomp/snark/rpc/UIUtil.java | 87 + src/java/org/klomp/snark/rpc/WebPlugin.java | 111 + .../org/klomp/snark/rpc/XMWebUIPlugin.java | 5654 +++++++++++++++++ src/jsp/WEB-INF/web.xml | 30 + src/jsp/index.html | 29 + src/jsp/licenses.html | 45 + src/jsp/web/easyXDM/hash.html | 25 + src/jsp/web/images/favicon.ico | Bin 0 -> 1150 bytes src/jsp/web/images/favicon.png | Bin 0 -> 1660 bytes src/jsp/web/images/webclip-icon.png | Bin 0 -> 9310 bytes src/jsp/web/index.html | 461 ++ src/jsp/web/javascript/common.js | 358 ++ src/jsp/web/javascript/dialog.js | 126 + .../web/javascript/easyXDM-2.4.18.4.min.js | 24 + src/jsp/web/javascript/easyxdm-2.4.18.4.swf | Bin 0 -> 1770 bytes src/jsp/web/javascript/file-row.js | 213 + src/jsp/web/javascript/formatter.js | 428 ++ src/jsp/web/javascript/inspector.js | 883 +++ .../javascript/jquery/jquery-1.10.2.min.js | 6 + .../jquery/jquery-ui-1.10.3.custom.min.js | 6 + .../javascript/jquery/jquery.mobile.custom.js | 944 +++ src/jsp/web/javascript/jquery/json2.min.js | 1 + src/jsp/web/javascript/jquery/purl.js | 267 + src/jsp/web/javascript/menu.js | 45 + src/jsp/web/javascript/notifications.js | 42 + src/jsp/web/javascript/prefs-dialog.js | 313 + src/jsp/web/javascript/remote.js | 451 ++ src/jsp/web/javascript/torrent-row.js | 429 ++ src/jsp/web/javascript/torrent.js | 529 ++ src/jsp/web/javascript/transmission.js | 2156 +++++++ src/jsp/web/javascript/vuze.js | 409 ++ .../jqueryui/images/animated-overlay.gif | Bin 0 -> 1738 bytes .../images/ui-bg_flat_0_aaaaaa_40x100.png | Bin 0 -> 212 bytes .../images/ui-bg_flat_75_ffffff_40x100.png | Bin 0 -> 208 bytes .../images/ui-bg_glass_55_fbf9ee_1x400.png | Bin 0 -> 335 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 0 -> 207 bytes .../images/ui-bg_glass_75_dadada_1x400.png | Bin 0 -> 262 bytes .../images/ui-bg_glass_75_e6e6e6_1x400.png | Bin 0 -> 262 bytes .../images/ui-bg_glass_95_fef1ec_1x400.png | Bin 0 -> 332 bytes .../ui-bg_highlight-soft_75_cccccc_1x100.png | Bin 0 -> 280 bytes .../images/ui-icons_222222_256x240.png | Bin 0 -> 6922 bytes .../images/ui-icons_2e83ff_256x240.png | Bin 0 -> 4549 bytes .../images/ui-icons_454545_256x240.png | Bin 0 -> 6992 bytes .../images/ui-icons_888888_256x240.png | Bin 0 -> 6999 bytes .../images/ui-icons_cd0a0a_256x240.png | Bin 0 -> 4549 bytes .../web/style/jqueryui/jquery-ui-1.10.3.css | 1188 ++++ .../jqueryui/jquery-ui-1.10.3.custom.min.css | 7 + src/jsp/web/style/transmission/common.css | 294 + src/jsp/web/style/transmission/common.scss | 1057 +++ .../style/transmission/images/arrow-down.png | Bin 0 -> 179 bytes .../style/transmission/images/arrow-up.png | Bin 0 -> 179 bytes .../web/style/transmission/images/blank.gif | Bin 0 -> 49 bytes .../transmission/images/buttons/cancel.png | Bin 0 -> 426 bytes .../images/buttons/file_wanted_buttons.png | Bin 0 -> 1488 bytes .../images/buttons/toolbar_buttons.png | Bin 0 -> 18859 bytes .../images/buttons/toolbar_buttons_vuze.png | Bin 0 -> 40216 bytes .../images/buttons/torrent_buttons.png | Bin 0 -> 1390 bytes .../images/buttons/torrent_buttons_vuze.png | Bin 0 -> 6686 bytes .../images/file-priority-high.png | Bin 0 -> 340 bytes .../transmission/images/file-priority-low.png | Bin 0 -> 335 bytes .../images/file-priority-normal.png | Bin 0 -> 185 bytes .../style/transmission/images/filter_icon.png | Bin 0 -> 446 bytes .../images/graphics/reset_link_btn.png | Bin 0 -> 709 bytes .../images/graphics/vuze_remote_logo.png | Bin 0 -> 2831 bytes .../images/inspector-activity.png | Bin 0 -> 1137 bytes .../transmission/images/inspector-files.png | Bin 0 -> 358 bytes .../transmission/images/inspector-info.png | Bin 0 -> 1370 bytes .../transmission/images/inspector-peers.png | Bin 0 -> 425 bytes .../images/inspector-trackers.png | Bin 0 -> 515 bytes .../style/transmission/images/lock_icon.png | Bin 0 -> 456 bytes .../web/style/transmission/images/logo.png | Bin 0 -> 1875 bytes .../style/transmission/images/progress.png | Bin 0 -> 938 bytes .../transmission/images/row-info-active.png | Bin 0 -> 2379 bytes .../style/transmission/images/row-info.png | Bin 0 -> 1838 bytes .../style/transmission/images/settings.png | Bin 0 -> 319 bytes .../style/transmission/images/toolbar-add.png | Bin 0 -> 231 bytes .../transmission/images/toolbar-close.png | Bin 0 -> 1539 bytes .../transmission/images/toolbar-folder.png | Bin 0 -> 1777 bytes .../transmission/images/toolbar-info.png | Bin 0 -> 1525 bytes .../transmission/images/toolbar-pause-all.png | Bin 0 -> 932 bytes .../transmission/images/toolbar-pause.png | Bin 0 -> 689 bytes .../transmission/images/toolbar-pointer.png | Bin 0 -> 364 bytes .../images/toolbar-remote-search.png | Bin 0 -> 1402 bytes .../transmission/images/toolbar-remote.png | Bin 0 -> 587 bytes .../transmission/images/toolbar-remove.png | Bin 0 -> 428 bytes .../transmission/images/toolbar-search.png | Bin 0 -> 658 bytes .../transmission/images/toolbar-start-all.png | Bin 0 -> 984 bytes .../transmission/images/toolbar-start.png | Bin 0 -> 534 bytes .../transmission/images/toolbar-stop-all.png | Bin 0 -> 1010 bytes .../transmission/images/toolbar-stop.png | Bin 0 -> 776 bytes .../web/style/transmission/vuze-mw-420.css | 38 + .../web/style/transmission/vuze-mw-900.css | 3 + src/jsp/web/style/transmission/vuze.css | 505 ++ .../web/style/transmission/vuzeandroid.css | 20 + 118 files changed, 20183 insertions(+) create mode 100644 CHANGES.txt create mode 100644 LICENSE.txt create mode 100644 README.txt create mode 100644 build.xml create mode 100644 plugin/licenses/JSON.txt create mode 100644 plugin/licenses/LICENSE-GPLv2.txt create mode 100644 plugin/licenses/Transmission.txt create mode 100755 scripts/makeplugin.sh create mode 100644 scripts/plugin.config create mode 100644 src/build.xml create mode 100644 src/java/com/aelitis/azureus/plugins/xmwebui/TransmissionVars.java create mode 100644 src/java/org/gudy/azureus2/plugins/download/DownloadException.java create mode 100644 src/java/org/json/simple/ItemList.java create mode 100644 src/java/org/json/simple/JSONArray.java create mode 100644 src/java/org/json/simple/JSONObject.java create mode 100644 src/java/org/json/simple/JSONValue.java create mode 100644 src/java/org/json/simple/parser/JSONParser.java create mode 100644 src/java/org/json/simple/parser/Yylex.java create mode 100644 src/java/org/json/simple/parser/Yytoken.java create mode 100644 src/java/org/klomp/snark/rpc/DownloadWillBeAddedListener.java create mode 100644 src/java/org/klomp/snark/rpc/JSONUtils.java create mode 100644 src/java/org/klomp/snark/rpc/MapUtils.java create mode 100644 src/java/org/klomp/snark/rpc/RPCServlet.java create mode 100644 src/java/org/klomp/snark/rpc/TextualException.java create mode 100644 src/java/org/klomp/snark/rpc/UIUtil.java create mode 100644 src/java/org/klomp/snark/rpc/WebPlugin.java create mode 100644 src/java/org/klomp/snark/rpc/XMWebUIPlugin.java create mode 100644 src/jsp/WEB-INF/web.xml create mode 100644 src/jsp/index.html create mode 100644 src/jsp/licenses.html create mode 100644 src/jsp/web/easyXDM/hash.html create mode 100644 src/jsp/web/images/favicon.ico create mode 100644 src/jsp/web/images/favicon.png create mode 100644 src/jsp/web/images/webclip-icon.png create mode 100644 src/jsp/web/index.html create mode 100644 src/jsp/web/javascript/common.js create mode 100644 src/jsp/web/javascript/dialog.js create mode 100644 src/jsp/web/javascript/easyXDM-2.4.18.4.min.js create mode 100644 src/jsp/web/javascript/easyxdm-2.4.18.4.swf create mode 100644 src/jsp/web/javascript/file-row.js create mode 100644 src/jsp/web/javascript/formatter.js create mode 100644 src/jsp/web/javascript/inspector.js create mode 100644 src/jsp/web/javascript/jquery/jquery-1.10.2.min.js create mode 100644 src/jsp/web/javascript/jquery/jquery-ui-1.10.3.custom.min.js create mode 100644 src/jsp/web/javascript/jquery/jquery.mobile.custom.js create mode 100644 src/jsp/web/javascript/jquery/json2.min.js create mode 100644 src/jsp/web/javascript/jquery/purl.js create mode 100644 src/jsp/web/javascript/menu.js create mode 100644 src/jsp/web/javascript/notifications.js create mode 100644 src/jsp/web/javascript/prefs-dialog.js create mode 100644 src/jsp/web/javascript/remote.js create mode 100644 src/jsp/web/javascript/torrent-row.js create mode 100644 src/jsp/web/javascript/torrent.js create mode 100644 src/jsp/web/javascript/transmission.js create mode 100644 src/jsp/web/javascript/vuze.js create mode 100644 src/jsp/web/style/jqueryui/images/animated-overlay.gif create mode 100644 src/jsp/web/style/jqueryui/images/ui-bg_flat_0_aaaaaa_40x100.png create mode 100644 src/jsp/web/style/jqueryui/images/ui-bg_flat_75_ffffff_40x100.png create mode 100644 src/jsp/web/style/jqueryui/images/ui-bg_glass_55_fbf9ee_1x400.png create mode 100644 src/jsp/web/style/jqueryui/images/ui-bg_glass_65_ffffff_1x400.png create mode 100644 src/jsp/web/style/jqueryui/images/ui-bg_glass_75_dadada_1x400.png create mode 100644 src/jsp/web/style/jqueryui/images/ui-bg_glass_75_e6e6e6_1x400.png create mode 100644 src/jsp/web/style/jqueryui/images/ui-bg_glass_95_fef1ec_1x400.png create mode 100644 src/jsp/web/style/jqueryui/images/ui-bg_highlight-soft_75_cccccc_1x100.png create mode 100644 src/jsp/web/style/jqueryui/images/ui-icons_222222_256x240.png create mode 100644 src/jsp/web/style/jqueryui/images/ui-icons_2e83ff_256x240.png create mode 100644 src/jsp/web/style/jqueryui/images/ui-icons_454545_256x240.png create mode 100644 src/jsp/web/style/jqueryui/images/ui-icons_888888_256x240.png create mode 100644 src/jsp/web/style/jqueryui/images/ui-icons_cd0a0a_256x240.png create mode 100644 src/jsp/web/style/jqueryui/jquery-ui-1.10.3.css create mode 100644 src/jsp/web/style/jqueryui/jquery-ui-1.10.3.custom.min.css create mode 100644 src/jsp/web/style/transmission/common.css create mode 100644 src/jsp/web/style/transmission/common.scss create mode 100644 src/jsp/web/style/transmission/images/arrow-down.png create mode 100644 src/jsp/web/style/transmission/images/arrow-up.png create mode 100644 src/jsp/web/style/transmission/images/blank.gif create mode 100644 src/jsp/web/style/transmission/images/buttons/cancel.png create mode 100644 src/jsp/web/style/transmission/images/buttons/file_wanted_buttons.png create mode 100644 src/jsp/web/style/transmission/images/buttons/toolbar_buttons.png create mode 100644 src/jsp/web/style/transmission/images/buttons/toolbar_buttons_vuze.png create mode 100644 src/jsp/web/style/transmission/images/buttons/torrent_buttons.png create mode 100644 src/jsp/web/style/transmission/images/buttons/torrent_buttons_vuze.png create mode 100644 src/jsp/web/style/transmission/images/file-priority-high.png create mode 100644 src/jsp/web/style/transmission/images/file-priority-low.png create mode 100644 src/jsp/web/style/transmission/images/file-priority-normal.png create mode 100644 src/jsp/web/style/transmission/images/filter_icon.png create mode 100644 src/jsp/web/style/transmission/images/graphics/reset_link_btn.png create mode 100644 src/jsp/web/style/transmission/images/graphics/vuze_remote_logo.png create mode 100644 src/jsp/web/style/transmission/images/inspector-activity.png create mode 100644 src/jsp/web/style/transmission/images/inspector-files.png create mode 100644 src/jsp/web/style/transmission/images/inspector-info.png create mode 100644 src/jsp/web/style/transmission/images/inspector-peers.png create mode 100644 src/jsp/web/style/transmission/images/inspector-trackers.png create mode 100644 src/jsp/web/style/transmission/images/lock_icon.png create mode 100644 src/jsp/web/style/transmission/images/logo.png create mode 100644 src/jsp/web/style/transmission/images/progress.png create mode 100644 src/jsp/web/style/transmission/images/row-info-active.png create mode 100644 src/jsp/web/style/transmission/images/row-info.png create mode 100644 src/jsp/web/style/transmission/images/settings.png create mode 100644 src/jsp/web/style/transmission/images/toolbar-add.png create mode 100644 src/jsp/web/style/transmission/images/toolbar-close.png create mode 100644 src/jsp/web/style/transmission/images/toolbar-folder.png create mode 100644 src/jsp/web/style/transmission/images/toolbar-info.png create mode 100644 src/jsp/web/style/transmission/images/toolbar-pause-all.png create mode 100644 src/jsp/web/style/transmission/images/toolbar-pause.png create mode 100644 src/jsp/web/style/transmission/images/toolbar-pointer.png create mode 100644 src/jsp/web/style/transmission/images/toolbar-remote-search.png create mode 100644 src/jsp/web/style/transmission/images/toolbar-remote.png create mode 100644 src/jsp/web/style/transmission/images/toolbar-remove.png create mode 100644 src/jsp/web/style/transmission/images/toolbar-search.png create mode 100644 src/jsp/web/style/transmission/images/toolbar-start-all.png create mode 100644 src/jsp/web/style/transmission/images/toolbar-start.png create mode 100644 src/jsp/web/style/transmission/images/toolbar-stop-all.png create mode 100644 src/jsp/web/style/transmission/images/toolbar-stop.png create mode 100644 src/jsp/web/style/transmission/vuze-mw-420.css create mode 100644 src/jsp/web/style/transmission/vuze-mw-900.css create mode 100644 src/jsp/web/style/transmission/vuze.css create mode 100644 src/jsp/web/style/transmission/vuzeandroid.css diff --git a/CHANGES.txt b/CHANGES.txt new file mode 100644 index 0000000..5796ca0 --- /dev/null +++ b/CHANGES.txt @@ -0,0 +1,2 @@ +* 2017-03-20 0.1 + - Initial version diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..793b493 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,23 @@ +New code: GPLv2 +See licenses/LICENSE-GPLv2.txt + +Contains code from Vuze trunk and from xmwebui plugin, +which includes code from Transmission and JSON-simple. + +Vuze Web Remote Plugin (xmwebui): GPLv2 +Modified from v0.6.5 from SVN +Copyright 2009 Vuze, Inc. All rights reserved. +See licenses/LICENSE-GPLv2.txt + +Vuze: GPLv2 +Modified from v5.7.5.1 from SVN +Copyright 2009 Vuze, Inc. All rights reserved. +See licenses/LICENSE-GPLv2.txt + +Transmission: +Copyright (c) Transmission authors and contributors +See licenses/Transmission.txt + +JSON: +LGPLv2.1 +See licenses/JSON.txt diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..03a729b --- /dev/null +++ b/README.txt @@ -0,0 +1,13 @@ +This is a Transmission- and Vuze- compatible RPC server for i2psnark, +with the Vuze-modified Transmission web UI. + +To access the web UI, go to /transmission/web/ in your router console. + +To use any compatible RPC client software, such as transmission-remote, +specify port 7657. For example, to list the torrents: + +transmission-remote 7657 -l + +Most basic features are supported. Several advanced features are not supported. +Some may be added in a future release. +Please report bugs on zzz.i2p. diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..2c53991 --- /dev/null +++ b/build.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugin/licenses/JSON.txt b/plugin/licenses/JSON.txt new file mode 100644 index 0000000..be28767 --- /dev/null +++ b/plugin/licenses/JSON.txt @@ -0,0 +1,157 @@ +**** LICENSING NOTE FROM PARG **** + +The version included within Vuze in LGPL version 2.1 and is an old version of the software available here: + +https://code.google.com/p/json-simple/downloads/detail?name=json_simple.zip&can=2&q= + +(archived here: https://web.archive.org/web/20140328054522/https://json-simple.googlecode.com/files/json_simple.zip ) + +The license was subsequently updated to Apache but the Vuze copy remains LGPL. + +*********************************** + + +Simple Java toolkit for JSON (JSON.simple) +========================================== + +1.Why the Simple Java toolkit (also named as JSON.simple) for JSON? + + When I use JSON as the data exchange format between the AJAX client and JSP + for the first time, what worry me mostly is how to encode Java strings and + numbers correctly in the server side so the AJAX client will receive a well + formed JSON data. When I looked into the 'JSON in Java' directory in JSON + website,I found that wrappers to JSONObject and JSONArray can be simpler, + due to the simplicity of JSON itself. So I wrote the JSON.simple package. + +2.Is it simple,really? + + I think so. Take an example: + + import org.json.simple.JSONObject; + + JSONObject obj=new JSONObject(); + obj.put("name","foo"); + obj.put("num",new Integer(100)); + obj.put("balance",new Double(1000.21)); + obj.put("is_vip",new Boolean(true)); + obj.put("nickname",null); + System.out.print(obj); + + Result: + {"nickname":null,"num":100,"balance":1000.21,"is_vip":true,"name":"foo"} + + The JSONObject.toString() will escape controls and specials correctly. + +3.How to use JSON.simple in JSP? + + Take an example in JSP: + + <%@page contentType="text/html; charset=UTF-8"%> + <%@page import="org.json.simple.JSONObject"%> + <% + JSONObject obj=new JSONObject(); + obj.put("name","foo"); + obj.put("num",new Integer(100)); + obj.put("balance",new Double(1000.21)); + obj.put("is_vip",new Boolean(true)); + obj.put("nickname",null); + out.print(obj); + out.flush(); + %> + + So the AJAX client will get the responseText. + +4.Some details about JSONObject? + + JSONObject inherits java.util.HashMap,so it don't have to worry about the + mapping things between keys and values. Feel free to use the Map methods + like get(), put(), and remove() and others. JSONObject.toString() will + combine key value pairs to get the JSON data string. Values will be escaped + into JSON quote string format if it's an instance of java.lang.String. Other + type of instance like java.lang.Number,java.lang.Boolean,null,JSONObject and + JSONArray will NOT escape, just take their java.lang.String.valueOf() result. + null value will be the JSON 'null' in the result. + + It's still correct if you put an instance of JSONObject or JSONArray into an + instance of JSONObject or JSONArray. Take the example about: + + JSONObject obj2=new JSONObject(); + obj2.put("phone","123456"); + obj2.put("zip","7890"); + obj.put("contact",obj2); + System.out.print(obj); + + Result: + {"nickname":null,"num":100,"contact":{"phone":"123456","zip":"7890"},"balance":1000.21,"is_vip":true,"name":"foo"} + + The method JSONObject.escape() is used to escape Java string into JSON quote + string. Controls and specials will be escaped correctly into \b,\f,\r,\n,\t, + \",\\,\/,\uhhhh. + +5.Some detail about JSONArray? + + org.json.simple.JSONArray inherits java.util.ArrayList. Feel free to use the + List methods like get(),add(),remove(),iterator() and so on. The rules of + JSONArray.toString() is similar to JSONObject.toString(). Here's the example: + + import org.json.simple.JSONArray; + + JSONArray array=new JSONArray(); + array.add("hello"); + array.add(new Integer(123)); + array.add(new Boolean(false)); + array.add(null); + array.add(new Double(123.45)); + array.add(obj2);//see above + System.out.print(array); + + Result: + ["hello",123,false,null,123.45,{"phone":"123456","zip":"7890"}] + +6.What is JSONValue for? + + org.json.simple.JSONValue is use to parse JSON data into Java Object. + In JSON, the topmost entity is JSON value, not the JSON object. But + it's not necessary to wrap JSON string,boolean,number and null again, + for the Java has already had the according classes: java.lang.String, + java.lang.Boolean,java.lang.Number and null. The mapping is: + + JSON Java + ------------------------------------------------ + string <=> java.lang.String + number <=> java.lang.Number + true|false <=> java.lang.Boolean + null <=> null + array <=> org.json.simple.JSONArray + object <=> org.json.simple.JSONObject + ------------------------------------------------ + + JSONValue has only one kind of method, JSONValue.parse(), which receives + a java.io.Reader or java.lang.String. Return type of JSONValue.parse() + is according to the mapping above. If the input is incorrect in syntax or + there's exceptions during the parsing, I choose to return null, ignoring + the exception: I have no idea if it's a serious implementaion, but I think + it's convenient to the user. + + Here's the example: + + String s="[0,{\"1\":{\"2\":{\"3\":{\"4\":[5,{\"6\":7}]}}}}]"; + Object obj=JSONValue.parse(s); + JSONArray array=(JSONArray)obj; + System.out.println(array.get(1)); + JSONObject obj2=(JSONObject)array.get(1); + System.out.println(obj2.get("1")); + + Result: + {"1":{"2":{"3":{"4":[5,{"6":7}]}}}} + {"2":{"3":{"4":[5,{"6":7}]}}} + +7.About the author. + + I'm a Java EE developer on Linux. + I'm working on web systems and information retrieval systems. + I also develop 3D games and Flash games. + + You can contact me through: + Fang Yidong + Fang Yidong diff --git a/plugin/licenses/LICENSE-GPLv2.txt b/plugin/licenses/LICENSE-GPLv2.txt new file mode 100644 index 0000000..14db8fc --- /dev/null +++ b/plugin/licenses/LICENSE-GPLv2.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/plugin/licenses/Transmission.txt b/plugin/licenses/Transmission.txt new file mode 100644 index 0000000..643f8be --- /dev/null +++ b/plugin/licenses/Transmission.txt @@ -0,0 +1,19 @@ + * Copyright (c) Transmission authors and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. diff --git a/scripts/makeplugin.sh b/scripts/makeplugin.sh new file mode 100755 index 0000000..9320c31 --- /dev/null +++ b/scripts/makeplugin.sh @@ -0,0 +1,138 @@ +#!/bin/sh +# +# basic packaging up of a plugin +# +# usage: makeplugin.sh plugindir +# +# zzz 2010-02 +# zzz 2014-08 added support for su3 files +# +CPATH=$I2P/lib/i2p.jar:/usr/share/java/gnu-getopt.jar +PUBKEYDIR=$HOME/.i2p-plugin-keys +PUBKEYFILE=$PUBKEYDIR/plugin-public-signing.key +PRIVKEYFILE=$PUBKEYDIR/plugin-private-signing.key +B64KEYFILE=$PUBKEYDIR/plugin-public-signing.txt +PUBKEYSTORE=$PUBKEYDIR/plugin-su3-public-signing.crt +PRIVKEYSTORE=$PUBKEYDIR/plugin-su3-keystore.ks +KEYTYPE=RSA_SHA512_4096 + +# put your files in here +PLUGINDIR=${1:-plugin} + +PC=plugin.config +PCT=${PC}.tmp + +if [ ! -d $PLUGINDIR ] +then + echo "You must have a $PLUGINDIR directory" + exit 1 +fi + +if [ ! -f $PLUGINDIR/$PC ] +then + echo "You must have a $PLUGINDIR/$PC file" + exit 1 +fi + +SIGNER=`grep '^signer=' $PLUGINDIR/$PC` +if [ "$?" -ne "0" ] +then + echo "You must have a signer name in $PC" + echo 'For example name=foo' + exit 1 +fi +SIGNER=`echo $SIGNER | cut -f 2 -d '='` + +if [ ! -f $PRIVKEYFILE ] +then + echo "Creating new XPI2P DSA keys" + mkdir -p $PUBKEYDIR || exit 1 + java -cp $CPATH net.i2p.crypto.TrustedUpdate keygen $PUBKEYFILE $PRIVKEYFILE || exit 1 + java -cp $CPATH net.i2p.data.Base64 encode $PUBKEYFILE $B64KEYFILE || exit 1 + rm -rf logs/ + chmod 444 $PUBKEYFILE $B64KEYFILE + chmod 400 $PRIVKEYFILE + echo "Created new XPI2P keys: $PUBKEYFILE $PRIVKEYFILE" +fi + +if [ ! -f $PRIVKEYSTORE ] +then + echo "Creating new SU3 $KEYTYPE keys for $SIGNER" + java -cp $CPATH net.i2p.crypto.SU3File keygen -t $KEYTYPE $PUBKEYSTORE $PRIVKEYSTORE $SIGNER || exit 1 + echo '*** Save your password in a safe place!!! ***' + rm -rf logs/ + # copy to the router dir so verify will work + CDIR=$I2P/certificates/plugin + mkdir -p $CDIR || exit 1 + CFILE=$CDIR/`echo $SIGNER | sed s/@/_at_/`.crt + cp $PUBKEYSTORE $CFILE + chmod 444 $PUBKEYSTORE + chmod 400 $PRIVKEYSTORE + chmod 644 $CFILE + echo "Created new SU3 keys: $PUBKEYSTORE $PRIVKEYSTORE" + echo "Copied public key to $CFILE for testing" +fi + +rm -f plugin.zip + +OPWD=$PWD +cd $PLUGINDIR + +grep -q '^name=' $PC +if [ "$?" -ne "0" ] +then + echo "You must have a plugin name in $PC" + echo 'For example name=foo' + exit 1 +fi + +grep -q '^version=' $PC +if [ "$?" -ne "0" ] +then + echo "You must have a version in $PC" + echo 'For example version=0.1.2' + exit 1 +fi + +# update the date +grep -v '^date=' $PC > $PCT +DATE=`date '+%s000'` +echo "date=$DATE" >> $PCT || exit 1 +mv $PCT $PC || exit 1 + +# add our Base64 key +grep -v '^key=' $PC > $PCT +B64KEY=`cat $B64KEYFILE` +echo "key=$B64KEY" >> $PCT || exit 1 +mv $PCT $PC || exit 1 + +# zip it +zip -r $OPWD/plugin.zip * || exit 1 + +# get the version and use it for the sud header +VERSION=`grep '^version=' $PC | cut -f 2 -d '='` +# get the name and use it for the file name +NAME=`grep '^name=' $PC | cut -f 2 -d '='` +XPI2P=${NAME}.xpi2p +SU3=${NAME}.su3 +cd $OPWD + +# sign it +echo 'Signing. ...' +java -cp $CPATH net.i2p.crypto.TrustedUpdate sign plugin.zip $XPI2P $PRIVKEYFILE $VERSION || exit 1 +java -cp $CPATH net.i2p.crypto.SU3File sign -c PLUGIN -t $KEYTYPE plugin.zip $SU3 $PRIVKEYSTORE $VERSION $SIGNER || exit 1 +rm -f plugin.zip + +# verify +echo 'Verifying. ...' +java -cp $CPATH net.i2p.crypto.TrustedUpdate showversion $XPI2P || exit 1 +java -cp $CPATH -Drouter.trustedUpdateKeys=$B64KEY net.i2p.crypto.TrustedUpdate verifysig $XPI2P || exit 1 +java -cp $CPATH net.i2p.crypto.SU3File showversion $SU3 || exit 1 +java -cp $CPATH net.i2p.crypto.SU3File verifysig -k $PUBKEYSTORE $SU3 || exit 1 +rm -rf logs/ + +echo 'Plugin files created: ' +wc -c $XPI2P +wc -c $SU3 + +exit 0 diff --git a/scripts/plugin.config b/scripts/plugin.config new file mode 100644 index 0000000..5dff453 --- /dev/null +++ b/scripts/plugin.config @@ -0,0 +1,12 @@ +name=i2psnark-rpc +signer=zzz-plugin@mail.i2p +consoleLinkName=Transmission +consoleLinkURL=/transmission/web/ +description=RPC and Web UI for i2psnark +author=zzz@mail.i2p +updateURL.su3=http://stats.i2p/i2p/plugins/i2psnark-rpc-update.su3 +websiteURL=http://zzz.i2p/forums/16 +license=GPLv2 +min-java-version=1.7 +min-jetty-version=9 +min-i2p-version=0.9.29 diff --git a/src/build.xml b/src/build.xml new file mode 100644 index 0000000..1c9ab9b --- /dev/null +++ b/src/build.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java/com/aelitis/azureus/plugins/xmwebui/TransmissionVars.java b/src/java/com/aelitis/azureus/plugins/xmwebui/TransmissionVars.java new file mode 100644 index 0000000..f5749ac --- /dev/null +++ b/src/java/com/aelitis/azureus/plugins/xmwebui/TransmissionVars.java @@ -0,0 +1,376 @@ +package com.aelitis.azureus.plugins.xmwebui; + +/** + * Variables converted from https://trac.transmissionbt.com/browser/trunk/libtransmission/transmission.h + * Comments also from .h file + * + * Not sure if I need it, but here's the header of transmission.h: + * + * Copyright (c) Transmission authors and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +public class TransmissionVars +{ + /** we won't (announce,scrape) this torrent to this tracker because + * the torrent is stopped, or because of an error, or whatever */ + public static final int TR_TRACKER_INACTIVE = 0; + + /** we will (announce,scrape) this torrent to this tracker, and are + * waiting for enough time to pass to satisfy the tracker's interval */ + public static final int TR_TRACKER_WAITING = 1; + + /** it's time to (announce,scrape) this torrent, and we're waiting on a + * a free slot to open up in the announce manager */ + public static final int TR_TRACKER_QUEUED = 2; + + /** we're (announcing,scraping) this torrent right now */ + public static final int TR_TRACKER_ACTIVE = 3; + + ///////////////////////////////////////////////////////////////////////////// + + public static final String TR_PREFS_KEY_ALT_SPEED_ENABLED = "alt-speed-enabled"; + + public static final String TR_PREFS_KEY_ALT_SPEED_UP_KBps = "alt-speed-up"; + + public static final String TR_PREFS_KEY_ALT_SPEED_DOWN_KBps = "alt-speed-down"; + + public static final String TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN = "alt-speed-time-begin"; + + public static final String TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED = "alt-speed-time-enabled"; + + public static final String TR_PREFS_KEY_ALT_SPEED_TIME_END = "alt-speed-time-end"; + + public static final String TR_PREFS_KEY_ALT_SPEED_TIME_DAY = "alt-speed-time-day"; + + public static final String TR_PREFS_KEY_BIND_ADDRESS_IPV4 = "bind-address-ipv4"; + + public static final String TR_PREFS_KEY_BIND_ADDRESS_IPV6 = "bind-address-ipv6"; + + public static final String TR_PREFS_KEY_BLOCKLIST_ENABLED = "blocklist-enabled"; + + public static final String TR_PREFS_KEY_BLOCKLIST_URL = "blocklist-url"; + + public static final String TR_PREFS_KEY_MAX_CACHE_SIZE_MB = "cache-size-mb"; + + public static final String TR_PREFS_KEY_DHT_ENABLED = "dht-enabled"; + + public static final String TR_PREFS_KEY_UTP_ENABLED = "utp-enabled"; + + public static final String TR_PREFS_KEY_LPD_ENABLED = "lpd-enabled"; + + public static final String TR_PREFS_KEY_DOWNLOAD_QUEUE_SIZE = "download-queue-size"; + + public static final String TR_PREFS_KEY_DOWNLOAD_QUEUE_ENABLED = "download-queue-enabled"; + + public static final String TR_PREFS_KEY_PREFETCH_ENABLED = "prefetch-enabled"; + + public static final String TR_PREFS_KEY_DOWNLOAD_DIR = "download-dir"; + + public static final String TR_PREFS_KEY_ENCRYPTION = "encryption"; + + public static final String TR_PREFS_KEY_IDLE_LIMIT = "idle-seeding-limit"; + + public static final String TR_PREFS_KEY_IDLE_LIMIT_ENABLED = "idle-seeding-limit-enabled"; + + public static final String TR_PREFS_KEY_INCOMPLETE_DIR = "incomplete-dir"; + + public static final String TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED = "incomplete-dir-enabled"; + + public static final String TR_PREFS_KEY_MSGLEVEL = "message-level"; + + public static final String TR_PREFS_KEY_PEER_LIMIT_GLOBAL = "peer-limit-global"; + + public static final String TR_PREFS_KEY_PEER_LIMIT_TORRENT = "peer-limit-per-torrent"; + + public static final String TR_PREFS_KEY_PEER_PORT = "peer-port"; + + public static final String TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START = "peer-port-random-on-start"; + + public static final String TR_PREFS_KEY_PEER_PORT_RANDOM_LOW = "peer-port-random-low"; + + public static final String TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH = "peer-port-random-high"; + + public static final String TR_PREFS_KEY_PEER_SOCKET_TOS = "peer-socket-tos"; + + public static final String TR_PREFS_KEY_PEER_CONGESTION_ALGORITHM = "peer-congestion-algorithm"; + + public static final String TR_PREFS_KEY_PEX_ENABLED = "pex-enabled"; + + public static final String TR_PREFS_KEY_PORT_FORWARDING = "port-forwarding-enabled"; + + public static final String TR_PREFS_KEY_PREALLOCATION = "preallocation"; + + public static final String TR_PREFS_KEY_RATIO = "ratio-limit"; + + public static final String TR_PREFS_KEY_RATIO_ENABLED = "ratio-limit-enabled"; + + public static final String TR_PREFS_KEY_RENAME_PARTIAL_FILES = "rename-partial-files"; + + public static final String TR_PREFS_KEY_RPC_AUTH_REQUIRED = "rpc-authentication-required"; + + public static final String TR_PREFS_KEY_RPC_BIND_ADDRESS = "rpc-bind-address"; + + public static final String TR_PREFS_KEY_RPC_ENABLED = "rpc-enabled"; + + public static final String TR_PREFS_KEY_RPC_PASSWORD = "rpc-password"; + + public static final String TR_PREFS_KEY_RPC_PORT = "rpc-port"; + + public static final String TR_PREFS_KEY_RPC_USERNAME = "rpc-username"; + + public static final String TR_PREFS_KEY_RPC_URL = "rpc-url"; + + public static final String TR_PREFS_KEY_RPC_WHITELIST_ENABLED = "rpc-whitelist-enabled"; + + public static final String TR_PREFS_KEY_SCRAPE_PAUSED_TORRENTS = "scrape-paused-torrents-enabled"; + + public static final String TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME = "script-torrent-done-filename"; + + public static final String TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED = "script-torrent-done-enabled"; + + public static final String TR_PREFS_KEY_SEED_QUEUE_SIZE = "seed-queue-size"; + + public static final String TR_PREFS_KEY_SEED_QUEUE_ENABLED = "seed-queue-enabled"; + + public static final String TR_PREFS_KEY_RPC_WHITELIST = "rpc-whitelist"; + + public static final String TR_PREFS_KEY_QUEUE_STALLED_ENABLED = "queue-stalled-enabled"; + + public static final String TR_PREFS_KEY_QUEUE_STALLED_MINUTES = "queue-stalled-minutes"; + + public static final String TR_PREFS_KEY_DSPEED_KBps = "speed-limit-down"; + + public static final String TR_PREFS_KEY_DSPEED_ENABLED = "speed-limit-down-enabled"; + + public static final String TR_PREFS_KEY_USPEED_KBps = "speed-limit-up"; + + public static final String TR_PREFS_KEY_USPEED_ENABLED = "speed-limit-up-enabled"; + + public static final String TR_PREFS_KEY_UMASK = "umask"; + + public static final String TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT = "upload-slots-per-torrent"; + + public static final String TR_PREFS_KEY_START = "start-added-torrents"; + + public static final String TR_PREFS_KEY_TRASH_ORIGINAL = "trash-original-torrent-files"; + + ///////////////////////////////////////////////////////////////////////////// + + public static final long TR_SCHED_SUN = (1 << 0); + + public static final long TR_SCHED_MON = (1 << 1); + + public static final long TR_SCHED_TUES = (1 << 2); + + public static final long TR_SCHED_WED = (1 << 3); + + public static final long TR_SCHED_THURS = (1 << 4); + + public static final long TR_SCHED_FRI = (1 << 5); + + public static final long TR_SCHED_SAT = (1 << 6); + + public static final long TR_SCHED_WEEKDAY = (TR_SCHED_MON | TR_SCHED_TUES + | TR_SCHED_WED | TR_SCHED_THURS | TR_SCHED_FRI); + + public static final long TR_SCHED_WEEKEND = (TR_SCHED_SUN | TR_SCHED_SAT); + + public static final long TR_SCHED_ALL = (TR_SCHED_WEEKDAY | TR_SCHED_WEEKEND); + + ////////////////////////////////////////////////////////////////////////////// + + public static final long TR_PRI_LOW = -1; + + public static final long TR_PRI_NORMAL = 0; /* since NORMAL is 0, memset initializes nicely */ + + public static final long TR_PRI_HIGH = 1; + + ////////////////////////////////////////////////////////////////////////////// + //tr_stat_errtype; + + /* everything's fine */ + public static final long TR_STAT_OK = 0; + + /* when we anounced to the tracker, we got a warning in the response */ + public static final long TR_STAT_TRACKER_WARNING = 1; + + /* when we anounced to the tracker, we got an error in the response */ + public static final long TR_STAT_TRACKER_ERROR = 2; + + /* local trouble, such as disk full or permissions error */ + public static final long TR_STAT_LOCAL_ERROR = 3; + + ////////////////////////////////////////////////////////////////////////////// + //tr_idlelimit; + /* follow the global settings */ + public static final long TR_IDLELIMIT_GLOBAL = 0; + /* override the global settings, seeding until a certain idle time */ + public static final long TR_IDLELIMIT_SINGLE = 1; + /* override the global settings, seeding regardless of activity */ + public static final long TR_IDLELIMIT_UNLIMITED = 2; + + ////////////////////////////////////////////////////////////////////////////// + //tr_ratiolimit; + /* follow the global settings */ + public static final long TR_RATIOLIMIT_GLOBAL = 0; + /* override the global settings, seeding until a certain ratio */ + public static final long TR_RATIOLIMIT_SINGLE = 1; + /* override the global settings, seeding regardless of ratio */ + public static final long TR_RATIOLIMIT_UNLIMITED = 2; + + + public static final long TR_ETA_NOT_AVAIL = -1; + public static final long TR_ETA_UNKNOWN = -2; + + ////////////////////////////////////////////////////////////////////////////// + + public static final String FIELD_TORRENT_WANTED = "wanted"; + + public static final String FIELD_TORRENT_PRIORITIES = "priorities"; + + public static final String FIELD_TORRENT_FILE_COUNT = "fileCount"; + + public static final String FIELD_TORRENT_ETA = "eta"; + + public static final String FIELD_TORRENT_ERROR_STRING = "errorString"; + + public static final String FIELD_TORRENT_ERROR = "error"; + + public static final String FIELD_TORRENT_STATUS = "status"; + + public static final String FIELD_TORRENT_RATE_DOWNLOAD = "rateDownload"; + + public static final String FIELD_TORRENT_RATE_UPLOAD = "rateUpload"; + + public static final String FIELD_TORRENT_SIZE_WHEN_DONE = "sizeWhenDone"; + + public static final String FIELD_TORRENT_PERCENT_DONE = "percentDone"; + + public static final String FIELD_TORRENT_NAME = "name"; + + public static final String FIELD_TORRENT_ID = "id"; + + public static final String FIELD_TORRENT_FILES_PRIORITY = "priority"; + + public static final String FIELD_TORRENT_POSITION = "queuePosition"; + + public static final String FIELD_TORRENT_UPLOAD_RATIO = "uploadRatio"; + + public static final String FIELD_TORRENT_DATE_ADDED = "addedDate"; + + public static final String FIELD_TORRENT_HASH = "hashString"; + + ////////////////////////////////////////////////////////////////////////////// + + public static final String TR_SESSION_STATS_ACTIVE_TORRENT_COUNT = "activeTorrentCount"; + + public static final String TR_SESSION_STATS_PAUSED_TORRENT_COUNT = "pausedTorrentCount"; + + public static final String TR_SESSION_STATS_DOWNLOAD_SPEED = "downloadSpeed"; + + public static final String TR_SESSION_STATS_UPLOAD_SPEED = "uploadSpeed"; + + public static final String TR_SESSION_STATS_TORRENT_COUNT = "torrentCount"; + + public static final String TR_SESSION_STATS_CURRENT = "current-stats"; + + public static final String TR_SESSION_STATS_CUMULATIVE = "cumulative-stats"; + + ////////////////////////////////////////////////////////////////////////////// + + public static final String FIELD_FILESTATS_BYTES_COMPLETED = "bytesCompleted"; + public static final String FIELD_FILESTATS_WANTED = "wanted"; + public static final String FIELD_FILESTATS_PRIORITY = "priority"; + public static final String FIELD_FILES_LENGTH = "length"; + public static final String FIELD_FILES_NAME = "name"; + public static final String FIELD_FILES_CONTENT_URL = "contentURL"; + public static final String FIELD_FILES_FULL_PATH = "fullPath"; + + ////////////////////////////////////////////////////////////////////////////// + + public static long convertVuzePriority(int priority) { + return priority == 0 ? TransmissionVars.TR_PRI_NORMAL + : priority < 0 ? TransmissionVars.TR_PRI_LOW + : TransmissionVars.TR_PRI_HIGH; + } + + ////////////////////////////////////////////////////////////////////////////// + + public static final int TR_STATUS_STOPPED = 0; /* Torrent is stopped */ + public static final int TR_STATUS_CHECK_WAIT = 1; /* Queued to check files */ + public static final int TR_STATUS_CHECK = 2; /* Checking files */ + public static final int TR_STATUS_DOWNLOAD_WAIT = 3; /* Queued to download */ + public static final int TR_STATUS_DOWNLOAD = 4; /* Downloading */ + public static final int TR_STATUS_SEED_WAIT = 5; /* Queued to seed */ + public static final int TR_STATUS_SEED = 6; /* Seeding */ + + ////////////////////////////////////////////////////////////////////////////// + + public static final String FIELD_SUBSCRIPTION_NAME = "name"; + public static final String FIELD_SUBSCRIPTION_ADDEDON = "addedDate"; + public static final String FIELD_SUBSCRIPTION_ASSOCIATION_COUNT = "associationCount"; + public static final String FIELD_SUBSCRIPTION_POPULARITY = "popularity"; + public static final String FIELD_SUBSCRIPTION_CATEGORY = "category"; + public static final String FIELD_SUBSCRIPTION_CREATOR = "creator"; + public static final String FIELD_SUBSCRIPTION_ENGINE_NAME = "engineName"; + public static final String FIELD_SUBSCRIPTION_ENGINE_TYPE = "engineType"; + public static final String FIELD_SUBSCRIPTION_HIGHEST_VERSION = "highestVersion"; + public static final String FIELD_SUBSCRIPTION_NAME_EX = "nameEx"; + public static final String FIELD_SUBSCRIPTION_QUERY_KEY = "queryKey"; + public static final String FIELD_SUBSCRIPTION_REFERER = "referer"; + public static final String FIELD_SUBSCRIPTION_TAG_UID = "tagUID"; + public static final String FIELD_SUBSCRIPTION_URI = "uri"; + public static final String FIELD_SUBSCRIPTION_ANONYMOUS = "anonymous"; + public static final String FIELD_SUBSCRIPTION_AUTO_DL_SUPPORTED = "autoDLSupported"; + public static final String FIELD_SUBSCRIPTION_AUTO_DOWNLOAD = "autoDownlaod"; + public static final String FIELD_SUBSCRIPTION_MINE = "mine"; + public static final String FIELD_SUBSCRIPTION_PUBLIC = "public"; + public static final String FIELD_SUBSCRIPTION_IS_SEARCH_TEMPLATE = "isSearchTemplate"; + public static final String FIELD_SUBSCRIPTION_SUBSCRIBED = "subscribed"; + public static final String FIELD_SUBSCRIPTION_UPDATEABLE = "updateable"; + public static final String FIELD_SUBSCRIPTION_SHAREABLE = "shareable"; + public static final String FIELD_SUBSCRIPTION_RESULTS_COUNT = "resultsCount"; + public static final String FIELD_SUBSCRIPTION_RESULTS = "results"; + public static final String FIELD_SUBSCRIPTION_ENGINE = "engine"; + public static final String FIELD_SUBSCRIPTION_ENGINE_URL = "url"; + public static final String FIELD_SUBSCRIPTION_ENGINE_NAMEX = "nameEx"; + public static final String FIELD_SUBSCRIPTION_ENGINE_AUTHMETHOD = "authMethod"; + public static final String FIELD_SUBSCRIPTION_ENGINE_LASTUPDATED = "lastUpdated"; + public static final String FIELD_SUBSCRIPTION_ENGINE_SOURCE = "source"; + + public static final String FIELD_SUBSCRIPTION_RESULT_UID = "u"; + public static final String FIELD_SUBSCRIPTION_RESULT_ISREAD = "subs_is_read"; + + public static final String FIELD_TAG_NAME = "name"; + public static final String FIELD_TAG_COUNT = "count"; + public static final String FIELD_TAG_TYPE = "type"; + public static final String FIELD_TAG_TYPENAME = "type-name"; + public static final String FIELD_TAG_CATEGORY_TYPE = "category-type"; + public static final String FIELD_TAG_UID = "uid"; + public static final String FIELD_TAG_ID = "id"; + public static final String FIELD_TAG_COLOR = "color"; + public static final String FIELD_TAG_CANBEPUBLIC = "canBePublic"; + public static final String FIELD_TAG_PUBLIC = "public"; + public static final String FIELD_TAG_VISIBLE = "visible"; + public static final String FIELD_TAG_GROUP = "group"; + public static final String FIELD_TAG_AUTO_ADD = "auto_add"; + public static final String FIELD_TAG_AUTO_REMOVE = "auto_remove"; + +} diff --git a/src/java/org/gudy/azureus2/plugins/download/DownloadException.java b/src/java/org/gudy/azureus2/plugins/download/DownloadException.java new file mode 100644 index 0000000..1b90a5d --- /dev/null +++ b/src/java/org/gudy/azureus2/plugins/download/DownloadException.java @@ -0,0 +1,49 @@ +/* + * File : DownloadException.java + * Created : 08-Jan-2004 + * By : parg + * + * Azureus - a Java Bittorrent client + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details ( see the LICENSE file ). + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.gudy.azureus2.plugins.download; + +/** Throws by various Download methods to indicate errors + * + * @author parg + * @since 2.0.7.0 + */ + +public class +DownloadException + extends Exception +{ + public + DownloadException( + String str ) + { + super(str); + } + + public + DownloadException( + String str, + Throwable cause ) + { + super(str,cause); + } +} diff --git a/src/java/org/json/simple/ItemList.java b/src/java/org/json/simple/ItemList.java new file mode 100644 index 0000000..109f97d --- /dev/null +++ b/src/java/org/json/simple/ItemList.java @@ -0,0 +1,205 @@ +/* + * $Id: ItemList.java,v 1.2 2009-03-15 22:12:18 parg Exp $ + * Created on 2006-3-24 + */ +package org.json.simple; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +/** + * �����÷ָ����ֿ���һ��item.�ָ�������һ����һ��item.ÿ��item���߲����ǿհ׷�. + * ���磺 + * |a:b:c| => |a|,|b|,|c| + * |:| => ||,|| + * |a:| => |a|,|| + * @author FangYidong + */ +public class ItemList { + private final static String sp=","; + List items=new ArrayList(); + + + public ItemList(){} + + /** + * + * @param s �ָ���������һ���ַ������� + */ + public ItemList(String s){ + this.split(s,sp,items); + } + /** + * + * @param s �ָ���������һ���ַ������� + * @param sp �ָ��� + */ + //public ItemList(String s,String sp){ + // this.sp=s; + // this.split(s,sp,items); + //} + + /** + * + * @param s + * @param sp + * @param isMultiToken sp�Ƿ�Ϊ��ָ��� + */ + public ItemList(String s,String sp,boolean isMultiToken){ + split(s,sp,items,isMultiToken); + } + + public List getItems(){ + return this.items; + } + + public String[] getArray(){ + return (String[])this.items.toArray(new String[items.size()]); + } + + public void split(String s,String sp,List append,boolean isMultiToken){ + if(s==null || sp==null) + return; + if(isMultiToken){ + StringTokenizer tokens=new StringTokenizer(s,sp); + while(tokens.hasMoreTokens()){ + append.add(tokens.nextToken().trim()); + } + } + else{ + this.split(s,sp,append); + } + } + + public void split(String s,String sp,List append){ + if(s==null || sp==null) + return; + int pos=0; + int prevPos=0; + do{ + prevPos=pos; + pos=s.indexOf(sp,pos); + if(pos==-1) + break; + append.add(s.substring(prevPos,pos).trim()); + pos+=sp.length(); + }while(pos!=-1); + append.add(s.substring(prevPos).trim()); + } + + /** + * ���÷ָ���. + * @param sp �ָ��� + */ + //public void setSP(String sp){ + // this.sp=sp; + //} + + /** + * ���뵥��item. + * @param i �����λ��(֮ǰ) + * @param item + */ + public void add(int i,String item){ + if(item==null) + return; + items.add(i,item.trim()); + } + /** + * ���뵥��item. + * @param item + */ + public void add(String item){ + if(item==null) + return; + items.add(item.trim()); + } + + /** + * ��һ��item. + * @param list �����list + */ + public void addAll(ItemList list){ + items.addAll(list.items); + } + + /** + * ��һ��item. + * @param s �ָ���������һ���ַ������� + */ + public void addAll(String s){ + this.split(s,sp,items); + } + + /** + * ��һ��item. + * @param s �ָ���������һ���ַ������� + * @param sp �ָ��� + */ + public void addAll(String s,String sp){ + this.split(s,sp,items); + } + + public void addAll(String s,String sp,boolean isMultiToken){ + this.split(s,sp,items,isMultiToken); + } + + /** + * ��õ�i��item. 0-based. + * @param i + * @return + */ + public String get(int i){ + return (String)items.get(i); + } + + /** + * ���item��. + * @return + */ + public int size(){ + return items.size(); + } + /** + * �÷ָ����ָ��ı�ʾ. + */ + public String toString(){ + return toString(sp); + } + + /** + * �÷ָ����ָ��ı�ʾ. + * @param sp ����ø÷ָ����ָ�. + * @return + */ + public String toString(String sp){ + StringBuilder sb=new StringBuilder(); + + for(int i=0;i + */ +public class JSONArray extends ArrayList { + public JSONArray() { + super(); + } + + public JSONArray(Collection arg0) { + super(arg0); + } + + public JSONArray(int initialCapacity) { + super(initialCapacity); + } + + public String toString(){ + ItemList list=new ItemList(); + + Iterator iter=iterator(); + + while(iter.hasNext()){ + Object value=iter.next(); + if(value instanceof String){ + list.add("\""+JSONObject.escape((String)value)+"\""); + } + else + list.add(String.valueOf(value)); + } + return "["+list.toString()+"]"; + } + + public void toString( StringBuilder sb ){ + sb.append( "[" ); + + Iterator iter=iterator(); + + boolean first = true; + while(iter.hasNext()){ + if ( first ){ + first = false; + }else{ + sb.append( "," ); + } + Object value=iter.next(); + if(value instanceof String){ + sb.append( "\"" ); + JSONObject.escape(sb, (String)value); + sb.append( "\""); + }else if ( value instanceof JSONObject ){ + ((JSONObject)value).toString( sb ); + }else if ( value instanceof JSONArray ){ + ((JSONArray)value).toString( sb ); + }else{ + sb.append(String.valueOf(value)); + } + } + + sb.append( "]" ); + } +} diff --git a/src/java/org/json/simple/JSONObject.java b/src/java/org/json/simple/JSONObject.java new file mode 100644 index 0000000..7cba7f5 --- /dev/null +++ b/src/java/org/json/simple/JSONObject.java @@ -0,0 +1,207 @@ +/* + * $Id: JSONObject.java,v 1.2 2008-08-07 01:18:54 parg Exp $ + * Created on 2006-4-10 + */ +package org.json.simple; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * @author FangYidong + */ +public class JSONObject extends HashMap{ + + public JSONObject() { + super(); + } + + public JSONObject(int initialCapacity, float loadFactor) { + super(initialCapacity, loadFactor); + } + + public JSONObject(int initialCapacity) { + super(initialCapacity); + } + + public JSONObject(Map arg0) { + super(arg0); + } + + public String toString(){ + ItemList list=new ItemList(); + Iterator> iter=entrySet().iterator(); + + while(iter.hasNext()){ + Map.Entry entry=iter.next(); + list.add(toString(entry.getKey().toString(),entry.getValue())); + } + return "{"+list.toString()+"}"; + } + + public void toString( StringBuilder sb ){ + + sb.append( "{" ); + + Iterator iter=entrySet().iterator(); + + boolean first = true; + + while(iter.hasNext()){ + if ( first ){ + first = false; + }else{ + sb.append( "," ); + } + Map.Entry entry=(Map.Entry)iter.next(); + toString(sb, entry.getKey().toString(),entry.getValue()); + } + + sb.append( "}" ); + } + + public static String toString(String key,Object value){ + StringBuilder sb=new StringBuilder(); + + sb.append("\""); + sb.append(escape(key)); + sb.append("\":"); + if(value==null){ + sb.append("null"); + return sb.toString(); + } + + if(value instanceof String){ + sb.append("\""); + sb.append(escape((String)value)); + sb.append("\""); + } + else + sb.append(value); + return sb.toString(); + } + + public static void toString(StringBuilder sb, String key,Object value){ + sb.append("\""); + escape(sb,key); + sb.append("\":"); + if(value==null){ + sb.append("null"); + return; + } + + if(value instanceof String){ + sb.append("\""); + escape(sb,(String)value); + sb.append("\""); + }else if ( value instanceof JSONObject ){ + ((JSONObject)value).toString( sb ); + }else if ( value instanceof JSONArray ){ + ((JSONArray)value).toString( sb ); + }else{ + sb.append(String.valueOf( value )); + } + } + + /** + * " => \" , \ => \\ + * @param s + * @return + */ + public static String escape(String s){ + if(s==null) + return null; + StringBuilder sb=new StringBuilder(); + for(int i=0;i='\u0000' && ch<='\u001F'){ + String ss=Integer.toHexString(ch); + sb.append("\\u"); + for(int k=0;k<4-ss.length();k++){ + sb.append('0'); + } + sb.append(ss.toUpperCase()); + } + else{ + sb.append(ch); + } + } + }//for + return sb.toString(); + } + + public static void escape(StringBuilder sb, String s){ + if(s==null){ + sb.append((String)null); + }else{ + for(int i=0;i='\u0000' && ch<='\u001F'){ + String ss=Integer.toHexString(ch); + sb.append("\\u"); + for(int k=0;k<4-ss.length();k++){ + sb.append('0'); + } + sb.append(ss.toUpperCase()); + } + else{ + sb.append(ch); + } + } + }//for + } + } +} diff --git a/src/java/org/json/simple/JSONValue.java b/src/java/org/json/simple/JSONValue.java new file mode 100644 index 0000000..d2c9888 --- /dev/null +++ b/src/java/org/json/simple/JSONValue.java @@ -0,0 +1,46 @@ +/* + * $Id: JSONValue.java,v 1.1 2007-06-05 00:43:56 tuxpaper Exp $ + * Created on 2006-4-15 + */ +package org.json.simple; + +import java.io.Reader; +import java.io.StringReader; +import java.util.Map; + +import org.json.simple.parser.JSONParser; + +import org.klomp.snark.rpc.JSONUtils; + + +/** + * @author FangYidong + */ +public class JSONValue { + /** + * parse into java object from input source. + * @param in + * @return instance of : JSONObject,JSONArray,String,Boolean,Long,Double or null + */ + public static Object parse(Reader in){ + try{ + JSONParser parser=new JSONParser(); + return parser.parse(in); + } + catch(Exception e){ + return null; + } + } + + public static Object parse(String s){ + StringReader in=new StringReader(s); + return parse(in); + } + + public static String toJSONString(Object value) { + if (value instanceof Map) { + return JSONUtils.encodeToJSON((Map) value); + } + return ""; + } +} diff --git a/src/java/org/json/simple/parser/JSONParser.java b/src/java/org/json/simple/parser/JSONParser.java new file mode 100644 index 0000000..aae8e26 --- /dev/null +++ b/src/java/org/json/simple/parser/JSONParser.java @@ -0,0 +1,190 @@ +/* + * $Id: JSONParser.java,v 1.2 2008-08-07 01:18:55 parg Exp $ + * Created on 2006-4-15 + */ +package org.json.simple.parser; + +import java.io.Reader; +import java.util.Stack; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + + +/** + * @author FangYidong + */ +public class JSONParser { + public static final int S_INIT=0; + public static final int S_IN_FINISHED_VALUE=1;//string,number,boolean,null,object,array + public static final int S_IN_OBJECT=2; + public static final int S_IN_ARRAY=3; + public static final int S_PASSED_PAIR_KEY=4; + public static final int S_IN_ERROR=-1; + + private int peekStatus(Stack statusStack){ + if(statusStack.size()==0) + return -1; + Integer status=(Integer)statusStack.peek(); + return status.intValue(); + } + + public Object parse(Reader in) throws Exception{ + Stack statusStack=new Stack(); + Stack valueStack=new Stack(); + Yylex lexer=new Yylex(in); + Yytoken token=null; + int status=S_INIT; + + try{ + do{ + token=lexer.yylex(); + if(token==null) + token=new Yytoken(Yytoken.TYPE_EOF,null); + switch(status){ + case S_INIT: + switch(token.type){ + case Yytoken.TYPE_VALUE: + status=S_IN_FINISHED_VALUE; + statusStack.push(new Integer(status)); + valueStack.push(token.value); + break; + case Yytoken.TYPE_LEFT_BRACE: + status=S_IN_OBJECT; + statusStack.push(new Integer(status)); + valueStack.push(new JSONObject()); + break; + case Yytoken.TYPE_LEFT_SQUARE: + status=S_IN_ARRAY; + statusStack.push(new Integer(status)); + valueStack.push(new JSONArray()); + break; + default: + status=S_IN_ERROR; + }//inner switch + break; + + case S_IN_FINISHED_VALUE: + if(token.type==Yytoken.TYPE_EOF) + return valueStack.pop(); + else + return null; + + case S_IN_OBJECT: + switch(token.type){ + case Yytoken.TYPE_COMMA: + break; + case Yytoken.TYPE_VALUE: + if(token.value instanceof String){ + String key=(String)token.value; + valueStack.push(key); + status=S_PASSED_PAIR_KEY; + statusStack.push(new Integer(status)); + } + else{ + status=S_IN_ERROR; + } + break; + case Yytoken.TYPE_RIGHT_BRACE: + if(valueStack.size()>1){ + statusStack.pop(); + JSONObject map = (JSONObject)valueStack.pop(); + status=peekStatus(statusStack); + } + else{ + status=S_IN_FINISHED_VALUE; + } + break; + default: + status=S_IN_ERROR; + break; + }//inner switch + break; + + case S_PASSED_PAIR_KEY: + switch(token.type){ + case Yytoken.TYPE_COLON: + break; + case Yytoken.TYPE_VALUE: + statusStack.pop(); + String key=(String)valueStack.pop(); + JSONObject parent=(JSONObject)valueStack.peek(); + parent.put(key,token.value); + status=peekStatus(statusStack); + break; + case Yytoken.TYPE_LEFT_SQUARE: + statusStack.pop(); + key=(String)valueStack.pop(); + parent=(JSONObject)valueStack.peek(); + JSONArray newArray=new JSONArray(); + parent.put(key,newArray); + status=S_IN_ARRAY; + statusStack.push(new Integer(status)); + valueStack.push(newArray); + break; + case Yytoken.TYPE_LEFT_BRACE: + statusStack.pop(); + key=(String)valueStack.pop(); + parent=(JSONObject)valueStack.peek(); + JSONObject newObject=new JSONObject(); + parent.put(key,newObject); + status=S_IN_OBJECT; + statusStack.push(new Integer(status)); + valueStack.push(newObject); + break; + default: + status=S_IN_ERROR; + } + break; + + case S_IN_ARRAY: + switch(token.type){ + case Yytoken.TYPE_COMMA: + break; + case Yytoken.TYPE_VALUE: + JSONArray val=(JSONArray)valueStack.peek(); + val.add(token.value); + break; + case Yytoken.TYPE_RIGHT_SQUARE: + if(valueStack.size()>1){ + statusStack.pop(); + valueStack.pop(); + status=peekStatus(statusStack); + } + else{ + status=S_IN_FINISHED_VALUE; + } + break; + case Yytoken.TYPE_LEFT_BRACE: + val=(JSONArray)valueStack.peek(); + JSONObject newObject=new JSONObject(); + val.add(newObject); + status=S_IN_OBJECT; + statusStack.push(new Integer(status)); + valueStack.push(newObject); + break; + case Yytoken.TYPE_LEFT_SQUARE: + val=(JSONArray)valueStack.peek(); + JSONArray newArray=new JSONArray(); + val.add(newArray); + status=S_IN_ARRAY; + statusStack.push(new Integer(status)); + valueStack.push(newArray); + break; + default: + status=S_IN_ERROR; + }//inner switch + break; + case S_IN_ERROR: + return null; + }//switch + if(status==S_IN_ERROR) + return null; + }while(token.type!=Yytoken.TYPE_EOF); + } + catch(Exception e){ + throw e; + } + return null; + } +} diff --git a/src/java/org/json/simple/parser/Yylex.java b/src/java/org/json/simple/parser/Yylex.java new file mode 100644 index 0000000..cf79958 --- /dev/null +++ b/src/java/org/json/simple/parser/Yylex.java @@ -0,0 +1,428 @@ +package org.json.simple.parser; + + +class Yylex { + private final static int YY_BUFFER_SIZE = 512; + private final static int YY_F = -1; + private final static int YY_NO_STATE = -1; + private final static int YY_NOT_ACCEPT = 0; + //private final static int YY_START = 1; + private final static int YY_END = 2; + private final static int YY_NO_ANCHOR = 4; + private final static int YY_BOL = 65536; + private final static int YY_EOF = 65537; + +private StringBuffer sb=new StringBuffer(); + private java.io.BufferedReader yy_reader; + private int yy_buffer_index; + private int yy_buffer_read; + private int yy_buffer_start; + private int yy_buffer_end; + private char yy_buffer[]; + private boolean yy_at_bol; + private int yy_lexical_state; + + Yylex (java.io.Reader reader) { + this (); + if (null == reader) { + throw (new Error("Error: Bad input stream initializer.")); + } + yy_reader = new java.io.BufferedReader(reader); + } + + Yylex (java.io.InputStream instream) { + this (); + if (null == instream) { + throw (new Error("Error: Bad input stream initializer.")); + } + yy_reader = new java.io.BufferedReader(new java.io.InputStreamReader(instream)); + } + + private Yylex () { + yy_buffer = new char[YY_BUFFER_SIZE]; + yy_buffer_read = 0; + yy_buffer_index = 0; + yy_buffer_start = 0; + yy_buffer_end = 0; + yy_at_bol = true; + yy_lexical_state = YYINITIAL; + } + + //private boolean yy_eof_done = false; + private static final int YYINITIAL = 0; + private static final int STRING_BEGIN = 1; + private static final int yy_state_dtrans[] = { + 0, + 39 + }; + private void yybegin (int state) { + yy_lexical_state = state; + } + private int yy_advance () + throws java.io.IOException { + int next_read; + int i; + int j; + + if (yy_buffer_index < yy_buffer_read) { + return yy_buffer[yy_buffer_index++]; + } + + if (0 != yy_buffer_start) { + i = yy_buffer_start; + j = 0; + while (i < yy_buffer_read) { + yy_buffer[j] = yy_buffer[i]; + ++i; + ++j; + } + yy_buffer_end = yy_buffer_end - yy_buffer_start; + yy_buffer_start = 0; + yy_buffer_read = j; + yy_buffer_index = j; + next_read = yy_reader.read(yy_buffer, + yy_buffer_read, + yy_buffer.length - yy_buffer_read); + if (-1 == next_read) { + return YY_EOF; + } + yy_buffer_read = yy_buffer_read + next_read; + } + + while (yy_buffer_index >= yy_buffer_read) { + if (yy_buffer_index >= yy_buffer.length) { + yy_buffer = yy_double(yy_buffer); + } + next_read = yy_reader.read(yy_buffer, + yy_buffer_read, + yy_buffer.length - yy_buffer_read); + if (-1 == next_read) { + return YY_EOF; + } + yy_buffer_read = yy_buffer_read + next_read; + } + return yy_buffer[yy_buffer_index++]; + } + private void yy_move_end () { + if (yy_buffer_end > yy_buffer_start && + '\n' == yy_buffer[yy_buffer_end-1]) + yy_buffer_end--; + if (yy_buffer_end > yy_buffer_start && + '\r' == yy_buffer[yy_buffer_end-1]) + yy_buffer_end--; + } + //private boolean yy_last_was_cr=false; + private void yy_mark_start () { + yy_buffer_start = yy_buffer_index; + } + private void yy_mark_end () { + yy_buffer_end = yy_buffer_index; + } + private void yy_to_mark () { + yy_buffer_index = yy_buffer_end; + yy_at_bol = (yy_buffer_end > yy_buffer_start) && + ('\r' == yy_buffer[yy_buffer_end-1] || + '\n' == yy_buffer[yy_buffer_end-1] || + 2028/*LS*/ == yy_buffer[yy_buffer_end-1] || + 2029/*PS*/ == yy_buffer[yy_buffer_end-1]); + } + private java.lang.String yytext () { + return (new java.lang.String(yy_buffer, + yy_buffer_start, + yy_buffer_end - yy_buffer_start)); + } + //private int yylength () { + // return yy_buffer_end - yy_buffer_start; + //} + private char[] yy_double (char buf[]) { + int i; + char newbuf[]; + newbuf = new char[2*buf.length]; + for (i = 0; i < buf.length; ++i) { + newbuf[i] = buf[i]; + } + return newbuf; + } + private static final int YY_E_INTERNAL = 0; + //private final int YY_E_MATCH = 1; + private java.lang.String yy_error_string[] = { + "Error: Internal error.\n", + "Error: Unmatched input.\n" + }; + private void yy_error (int code,boolean fatal) { + java.lang.System.out.print(yy_error_string[code]); + java.lang.System.out.flush(); + if (fatal) { + throw new Error("Fatal Error.\n"); + } + } + private static int[][] unpackFromString(int size1, int size2, String st) { + int colonIndex = -1; + String lengthString; + int sequenceLength = 0; + int sequenceInteger = 0; + + int commaIndex; + String workString; + + int res[][] = new int[size1][size2]; + for (int i= 0; i < size1; i++) { + for (int j= 0; j < size2; j++) { + if (sequenceLength != 0) { + res[i][j] = sequenceInteger; + sequenceLength--; + continue; + } + commaIndex = st.indexOf(','); + workString = (commaIndex==-1) ? st : + st.substring(0, commaIndex); + st = st.substring(commaIndex+1); + colonIndex = workString.indexOf(':'); + if (colonIndex == -1) { + res[i][j]=Integer.parseInt(workString); + continue; + } + lengthString = + workString.substring(colonIndex+1); + sequenceLength=Integer.parseInt(lengthString); + workString=workString.substring(0,colonIndex); + sequenceInteger=Integer.parseInt(workString); + res[i][j] = sequenceInteger; + sequenceLength--; + } + } + return res; + } + private static final int yy_acpt[] = { + /* 0 */ YY_NOT_ACCEPT, + /* 1 */ YY_NO_ANCHOR, + /* 2 */ YY_NO_ANCHOR, + /* 3 */ YY_NO_ANCHOR, + /* 4 */ YY_NO_ANCHOR, + /* 5 */ YY_NO_ANCHOR, + /* 6 */ YY_NO_ANCHOR, + /* 7 */ YY_NO_ANCHOR, + /* 8 */ YY_NO_ANCHOR, + /* 9 */ YY_NO_ANCHOR, + /* 10 */ YY_NO_ANCHOR, + /* 11 */ YY_NO_ANCHOR, + /* 12 */ YY_NO_ANCHOR, + /* 13 */ YY_NO_ANCHOR, + /* 14 */ YY_NO_ANCHOR, + /* 15 */ YY_NO_ANCHOR, + /* 16 */ YY_NO_ANCHOR, + /* 17 */ YY_NO_ANCHOR, + /* 18 */ YY_NO_ANCHOR, + /* 19 */ YY_NO_ANCHOR, + /* 20 */ YY_NO_ANCHOR, + /* 21 */ YY_NO_ANCHOR, + /* 22 */ YY_NO_ANCHOR, + /* 23 */ YY_NO_ANCHOR, + /* 24 */ YY_NO_ANCHOR, + /* 25 */ YY_NOT_ACCEPT, + /* 26 */ YY_NO_ANCHOR, + /* 27 */ YY_NO_ANCHOR, + /* 28 */ YY_NOT_ACCEPT, + /* 29 */ YY_NOT_ACCEPT, + /* 30 */ YY_NOT_ACCEPT, + /* 31 */ YY_NOT_ACCEPT, + /* 32 */ YY_NOT_ACCEPT, + /* 33 */ YY_NOT_ACCEPT, + /* 34 */ YY_NOT_ACCEPT, + /* 35 */ YY_NOT_ACCEPT, + /* 36 */ YY_NOT_ACCEPT, + /* 37 */ YY_NOT_ACCEPT, + /* 38 */ YY_NOT_ACCEPT, + /* 39 */ YY_NOT_ACCEPT, + /* 40 */ YY_NOT_ACCEPT, + /* 41 */ YY_NOT_ACCEPT, + /* 42 */ YY_NOT_ACCEPT, + /* 43 */ YY_NOT_ACCEPT, + /* 44 */ YY_NOT_ACCEPT + }; + private static final int yy_cmap[] = unpackFromString(1,65538, +"11:8,27:2,28,11,27,28,11:18,27,11,2,11:8,16,25,12,14,3,13:10,26,11:6,10:4,1" + +"5,10,11:20,23,1,24,11:3,18,4,10:2,17,5,11:5,19,11,6,11:3,7,20,8,9,11:5,21,1" + +"1,22,11:65410,0:2")[0]; + + private static final int yy_rmap[] = unpackFromString(1,45, +"0,1:2,2,1:7,3,1:2,4,1:10,5,6,1,7,8,9,10,11,12,13,14,15,16,6,17,18,19,20,21," + +"22")[0]; + + private static final int yy_nxt[][] = unpackFromString(23,29, +"1,-1,2,-1:2,25,28,-1,29,-1:3,30,3,-1:7,4,5,6,7,8,9,10:2,-1:42,3,33,34,-1,34" + +",-1:24,11,-1,34,-1,34,-1:12,16,17,18,19,20,21,22,23,40,-1:37,31,-1:23,26,-1" + +":24,42,-1:26,32,-1:34,3,-1:34,35,-1:18,37,-1:32,11,-1:27,38,26,-1:2,38,-1:3" + +"2,37,-1:27,12,-1:26,13,-1:11,1,14,15,27:25,-1:5,44:2,-1:4,44,-1:2,44,-1,44," + +"-1,44:2,-1:14,24:2,-1:4,24,-1:2,24,-1,24,-1,24:2,-1:29,36,-1:13,41:2,-1:4,4" + +"1,-1:2,41,-1,41,-1,41:2,-1:14,43:2,-1:4,43,-1:2,43,-1,43,-1,43:2,-1:10"); + + public Yytoken yylex () + throws java.io.IOException { + int yy_lookahead; + int yy_anchor = YY_NO_ANCHOR; + int yy_state = yy_state_dtrans[yy_lexical_state]; + int yy_next_state = YY_NO_STATE; + int yy_last_accept_state = YY_NO_STATE; + boolean yy_initial = true; + int yy_this_accept; + + yy_mark_start(); + yy_this_accept = yy_acpt[yy_state]; + if (YY_NOT_ACCEPT != yy_this_accept) { + yy_last_accept_state = yy_state; + yy_mark_end(); + } + while (true) { + if (yy_initial && yy_at_bol) yy_lookahead = YY_BOL; + else yy_lookahead = yy_advance(); + yy_next_state = YY_F; + yy_next_state = yy_nxt[yy_rmap[yy_state]][yy_cmap[yy_lookahead]]; + if (YY_EOF == yy_lookahead && true == yy_initial) { + return null; + } + if (YY_F != yy_next_state) { + yy_state = yy_next_state; + yy_initial = false; + yy_this_accept = yy_acpt[yy_state]; + if (YY_NOT_ACCEPT != yy_this_accept) { + yy_last_accept_state = yy_state; + yy_mark_end(); + } + } + else { + if (YY_NO_STATE == yy_last_accept_state) { + throw (new Error("Lexical Error: Unmatched Input.")); + } + else { + yy_anchor = yy_acpt[yy_last_accept_state]; + if (0 != (YY_END & yy_anchor)) { + yy_move_end(); + } + yy_to_mark(); + switch (yy_last_accept_state) { + case 1: + + case -2: + break; + case 2: + { sb.delete(0,sb.length());yybegin(STRING_BEGIN);} + case -3: + break; + case 3: + { Long val=Long.valueOf(yytext()); return new Yytoken(Yytoken.TYPE_VALUE,val);} + case -4: + break; + case 4: + { return new Yytoken(Yytoken.TYPE_LEFT_BRACE,null);} + case -5: + break; + case 5: + { return new Yytoken(Yytoken.TYPE_RIGHT_BRACE,null);} + case -6: + break; + case 6: + { return new Yytoken(Yytoken.TYPE_LEFT_SQUARE,null);} + case -7: + break; + case 7: + { return new Yytoken(Yytoken.TYPE_RIGHT_SQUARE,null);} + case -8: + break; + case 8: + { return new Yytoken(Yytoken.TYPE_COMMA,null);} + case -9: + break; + case 9: + { return new Yytoken(Yytoken.TYPE_COLON,null);} + case -10: + break; + case 10: + {} + case -11: + break; + case 11: + { Double val=Double.valueOf(yytext()); return new Yytoken(Yytoken.TYPE_VALUE,val);} + case -12: + break; + case 12: + { return new Yytoken(Yytoken.TYPE_VALUE,null);} + case -13: + break; + case 13: + { Boolean val=Boolean.valueOf(yytext()); return new Yytoken(Yytoken.TYPE_VALUE,val);} + case -14: + break; + case 14: + { sb.append(yytext());} + case -15: + break; + case 15: + { yybegin(YYINITIAL);return new Yytoken(Yytoken.TYPE_VALUE,sb.toString());} + case -16: + break; + case 16: + {sb.append('\\');} + case -17: + break; + case 17: + {sb.append('"');} + case -18: + break; + case 18: + {sb.append('/');} + case -19: + break; + case 19: + {sb.append('\b');} + case -20: + break; + case 20: + {sb.append('\f');} + case -21: + break; + case 21: + {sb.append('\n');} + case -22: + break; + case 22: + {sb.append('\r');} + case -23: + break; + case 23: + {sb.append('\t');} + case -24: + break; + case 24: + { int ch=Integer.parseInt(yytext().substring(2),16); + sb.append((char)ch); + } + case -25: + break; + case 26: + { Double val=Double.valueOf(yytext()); return new Yytoken(Yytoken.TYPE_VALUE,val);} + case -26: + break; + case 27: + { sb.append(yytext());} + case -27: + break; + default: + yy_error(YY_E_INTERNAL,false); + case -1: + } + yy_initial = true; + yy_state = yy_state_dtrans[yy_lexical_state]; + yy_next_state = YY_NO_STATE; + yy_last_accept_state = YY_NO_STATE; + yy_mark_start(); + yy_this_accept = yy_acpt[yy_state]; + if (YY_NOT_ACCEPT != yy_this_accept) { + yy_last_accept_state = yy_state; + yy_mark_end(); + } + } + } + } + } +} diff --git a/src/java/org/json/simple/parser/Yytoken.java b/src/java/org/json/simple/parser/Yytoken.java new file mode 100644 index 0000000..4c90001 --- /dev/null +++ b/src/java/org/json/simple/parser/Yytoken.java @@ -0,0 +1,31 @@ +/* + * $Id: Yytoken.java,v 1.1 2007-06-05 00:43:56 tuxpaper Exp $ + * Created on 2006-4-15 + */ +package org.json.simple.parser; + +/** + * @author FangYidong + */ +public class Yytoken { + public static final int TYPE_VALUE=0;//JSON primitive value: string,number,boolean,null + public static final int TYPE_LEFT_BRACE=1; + public static final int TYPE_RIGHT_BRACE=2; + public static final int TYPE_LEFT_SQUARE=3; + public static final int TYPE_RIGHT_SQUARE=4; + public static final int TYPE_COMMA=5; + public static final int TYPE_COLON=6; + public static final int TYPE_EOF=-1;//end of file + + public int type=0; + public Object value=null; + + public Yytoken(int type,Object value){ + this.type=type; + this.value=value; + } + + public String toString(){ + return String.valueOf(type+"=>|"+value+"|"); + } +} diff --git a/src/java/org/klomp/snark/rpc/DownloadWillBeAddedListener.java b/src/java/org/klomp/snark/rpc/DownloadWillBeAddedListener.java new file mode 100644 index 0000000..44603ca --- /dev/null +++ b/src/java/org/klomp/snark/rpc/DownloadWillBeAddedListener.java @@ -0,0 +1,36 @@ +/* + * Created on 21-Mar-2006 + * Created by Paul Gardner + * Copyright (C) Azureus Software, Inc, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +package org.klomp.snark.rpc; + +import org.klomp.snark.Snark; + +public interface +DownloadWillBeAddedListener +{ + /** + * this will be called early during download initialisation and allows the initial + * file state to be set before allocation occurs (for example) + * @param the file initialised + */ + + public void + initialised( + Snark download ); +} diff --git a/src/java/org/klomp/snark/rpc/JSONUtils.java b/src/java/org/klomp/snark/rpc/JSONUtils.java new file mode 100644 index 0000000..c3914d8 --- /dev/null +++ b/src/java/org/klomp/snark/rpc/JSONUtils.java @@ -0,0 +1,201 @@ +/** + * Copyright (C) Azureus Software, Inc, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +package org.klomp.snark.rpc; + +import java.io.UnsupportedEncodingException; +import java.util.*; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; + +import net.i2p.I2PAppContext; +import net.i2p.data.Base64; + + +/** + * @author TuxPaper + * @created Feb 14, 2007 + * + */ +public class JSONUtils +{ + /** + * decodes JSON formatted text into a map. + * + * @return Map parsed from a JSON formatted string + *

+ * If the json text is not a map, a map with the key "value" will be returned. + * the value of "value" will either be an List, String, Number, Boolean, or null + *

+ * if the String is formatted badly, null is returned + */ + public static Map decodeJSON(String json) { + try { + Object object = JSONValue.parse(json); + if (object instanceof Map) { + return (Map) object; + } + // could be : ArrayList, String, Number, Boolean + Map map = new HashMap(); + map.put("value", object); + return map; + } catch (Throwable t) { + I2PAppContext.getGlobalContext().logManager().getLog(JSONUtils.class).error("Warning: Bad JSON String: " + json, t); + return null; + } + } + + /** + * encodes a map into a JSONObject. + *

+ * It's recommended that you use {@link #encodeToJSON(Map)} instead + * + * @param map + * @return + * + * @since 3.0.1.5 + */ + public static JSONObject encodeToJSONObject(Map map) { + JSONObject newMap = new JSONObject((int)(map.size()*1.5)); + + for (Map.Entry entry: ((Map)map).entrySet()){ + String key = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof byte[]) { + key += ".B64"; + value = Base64.encode((byte[]) value); + } + value = coerce(value); + newMap.put(key, value); + } + return newMap; + } + + /** + * Encodes a map into a JSON formatted string. + *

+ * Handles multiple layers of Maps and Lists. Handls String, Number, + * Boolean, and null values. + * + * @param map Map to change into a JSON formatted string + * @return JSON formatted string + * + * @since 3.0.1.5 + */ + public static String encodeToJSON(Map map) { + JSONObject jobj = encodeToJSONObject(map); + StringBuilder sb = new StringBuilder(8192); + jobj.toString( sb ); + return( sb.toString()); + } + + public static String encodeToJSON(Collection list) { + return encodeToJSONArray(list).toString(); + } + + private static Object coerce(Object value) { + if (value instanceof Map) { + value = encodeToJSONObject((Map) value); + } else if (value instanceof List) { + value = encodeToJSONArray((List) value); + } else if (value instanceof Object[]) { + Object[] array = (Object[]) value; + value = encodeToJSONArray(Arrays.asList(array)); + } else if (value instanceof byte[]) { + try { + value = new String((byte[]) value, "utf-8"); + } catch (UnsupportedEncodingException e) { + } + } else if (value instanceof boolean[]) { + boolean[] array = (boolean[]) value; + ArrayList list = new ArrayList(); + for (boolean b : array) { + list.add(b); + } + value = encodeToJSONArray(list); + } else if (value instanceof long[]) { + long[] array = (long[]) value; + ArrayList list = new ArrayList(); + for (long b : array) { + list.add(b); + } + value = encodeToJSONArray(list); + } else if (value instanceof int[]) { + int[] array = (int[]) value; + ArrayList list = new ArrayList(); + for (int b : array) { + list.add(b); + } + value = encodeToJSONArray(list); + } + return value; + } + + /** + * @param value + * @return + * + * @since 3.0.1.5 + */ + private static JSONArray encodeToJSONArray(Collection list) { + JSONArray newList = new JSONArray(list.size()); + for ( Object value: list ){ + newList.add(coerce(value)); + } + return newList; + } + + public static void main(String[] args) { + + Map mapBefore = new HashMap(); + byte[] b = { + 0, + 1, + 2 + }; + mapBefore.put("Hi", b); + String jsonByteArray = JSONUtils.encodeToJSON(mapBefore); + System.out.println(jsonByteArray); + Map mapAfter = JSONUtils.decodeJSON(jsonByteArray); + b = MapUtils.getMapByteArray(mapAfter, "Hi", null); + System.out.println(b.length); + for (int i = 0; i < b.length; i++) { + byte c = b[i]; + System.out.println("--" + c); + } + + Map map = new HashMap(); + map.put("Test", "TestValue"); + Map map2 = new HashMap(); + map2.put("Test2", "test2value"); + map.put("TestMap", map2); + + List list = new ArrayList(); + list.add(new Long(5)); + list.add("five"); + map2.put("ListTest", list); + + Map map3 = new HashMap(); + map3.put("Test3", "test3value"); + list.add(map3); + + System.out.println(encodeToJSON(map)); + System.out.println(encodeToJSON(list)); + } +} diff --git a/src/java/org/klomp/snark/rpc/MapUtils.java b/src/java/org/klomp/snark/rpc/MapUtils.java new file mode 100644 index 0000000..c102867 --- /dev/null +++ b/src/java/org/klomp/snark/rpc/MapUtils.java @@ -0,0 +1,238 @@ +/** + * Copyright (C) Azureus Software, Inc, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +package org.klomp.snark.rpc; + +import java.util.*; + +import net.i2p.data.Base32; +import net.i2p.data.Base64; + +/** + * @author TuxPaper + * @created Jun 1, 2007 + * + */ +public class MapUtils +{ + public static int getMapInt(Map map, String key, int def) { + if (map == null) { + return def; + } + try { + Number n = (Number) map.get(key); + if ( n == null ){ + return( def ); + } + return n.intValue(); + } catch (Throwable e) { + //Debug.out(e); + return def; + } + } + + public static long getMapLong(Map map, String key, long def) { + if (map == null) { + return def; + } + try { + Number n = (Number) map.get(key); + if ( n == null ){ + return( def ); + } + return n.longValue(); + } catch (Throwable e) { + //Debug.out(e); + return def; + } + } + + public static String getMapString(Map map, String key, String def) { + if (map == null) { + return def; + } + try { + Object o = map.get(key); + if (o == null && !map.containsKey(key)) { + return def; + } + // NOTE: The above returns def when map doesn't contain the key, + // which suggests below we would return the null when o is null. + // But we don't! And now, some callers rely on this :( + + if (o instanceof String) { + return (String) o; + } + if (o instanceof byte[]) { + return new String((byte[]) o, "utf-8"); + } + return def; + } catch (Throwable t) { + //Debug.out(t); + return def; + } + } + + public static String[] + getMapStringArray( + Map map, + String key, + String[] def ) + { + Object o = map.get( key ); + if (!(o instanceof List)) { + return def; + } + List list = (List) o; + String[] result = new String[list.size()]; + for (int i=0;i 9) + val = id[i] - 'A'; + if (i != 6 || val != 0) { + if (i != 3) + buf.append('.'); + buf.append(val); + } + } + return buf.toString(); + } + + /** + * Get version from bytes 3-5 + * @return " w.x.y" or "" + * @since 0.9.14 + */ + private static String getRobtVersion(byte[] id) { + StringBuilder buf = new StringBuilder(8); + buf.append(' '); + for (int i = 3; i <= 5; i++) { + int val = id[i]; + if (val < 0) + return ""; + if (i != 3) + buf.append('.'); + buf.append(val); + } + return buf.toString(); + } +} diff --git a/src/java/org/klomp/snark/rpc/WebPlugin.java b/src/java/org/klomp/snark/rpc/WebPlugin.java new file mode 100644 index 0000000..3db6277 --- /dev/null +++ b/src/java/org/klomp/snark/rpc/WebPlugin.java @@ -0,0 +1,111 @@ +/* + * File : WebPlugin.java + * Created : 23-Jan-2004 + * By : parg + * + * Azureus - a Java Bittorrent client + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details ( see the LICENSE file ). + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package org.klomp.snark.rpc; + +/** + * @author parg + * + */ + +public class +WebPlugin +{ + public static final String PR_ENABLE = "Enable"; // Boolean + public static final String PR_DISABLABLE = "Disablable"; // Boolean + public static final String PR_PORT = "Port"; // Integer + public static final String PR_BIND_IP = "Bind IP"; // String + public static final String PR_ROOT_RESOURCE = "Root Resource"; // String + public static final String PR_HOME_PAGE = "Home Page"; // String + public static final String PR_ROOT_DIR = "Root Dir"; // String + public static final String PR_ACCESS = "Access"; // String + public static final String PR_LOG = "DefaultLoggerChannel"; // LoggerChannel + public static final String PR_CONFIG_MODEL_PARAMS = "DefaultConfigModelParams"; // String[] params to use when creating config model + public static final String PR_CONFIG_MODEL = "DefaultConfigModel"; // BasicPluginConfigModel + public static final String PR_VIEW_MODEL = "DefaultViewModel"; // BasicPluginViewModel + public static final String PR_HIDE_RESOURCE_CONFIG = "DefaultHideResourceConfig"; // Boolean + public static final String PR_ENABLE_KEEP_ALIVE = "DefaultEnableKeepAlive"; // Boolean + public static final String PR_PAIRING_SID = "PairingSID"; // String + public static final String PR_NON_BLOCKING = "NonBlocking"; // Boolean + public static final String PR_ENABLE_PAIRING = "EnablePairing"; // Boolean + public static final String PR_ENABLE_I2P = "EnableI2P"; // Boolean + public static final String PR_ENABLE_TOR = "EnableTor"; // Boolean + public static final String PR_ENABLE_UPNP = "EnableUPNP"; // Boolean + + public static final String PROPERTIES_MIGRATED = "Properties Migrated"; + public static final String CONFIG_MIGRATED = "Config Migrated"; + public static final String PAIRING_MIGRATED = "Pairing Migrated"; + public static final String PAIRING_SESSION_KEY = "Pairing Session Key"; + + public static final String CONFIG_PASSWORD_ENABLE = "Password Enable"; + public static final boolean CONFIG_PASSWORD_ENABLE_DEFAULT = false; + + public static final String CONFIG_PAIRING_ENABLE = "Pairing Enable"; + public static final boolean CONFIG_PAIRING_ENABLE_DEFAULT = true; + + public static final String CONFIG_PORT_OVERRIDE = "Port Override"; + + public static final String CONFIG_PAIRING_AUTO_AUTH = "Pairing Auto Auth"; + public static final boolean CONFIG_PAIRING_AUTO_AUTH_DEFAULT = true; + + + public static final String CONFIG_ENABLE = PR_ENABLE; + public boolean CONFIG_ENABLE_DEFAULT = true; + + public static final String CONFIG_USER = "User"; + public static final String CONFIG_USER_DEFAULT = ""; + + public static final String CONFIG_PASSWORD = "Password"; + public static final byte[] CONFIG_PASSWORD_DEFAULT = {}; + + public static final String CONFIG_PORT = PR_PORT; + public int CONFIG_PORT_DEFAULT = 8089; + + public static final String CONFIG_BIND_IP = PR_BIND_IP; + public String CONFIG_BIND_IP_DEFAULT = ""; + + public static final String CONFIG_PROTOCOL = "Protocol"; + public static final String CONFIG_PROTOCOL_DEFAULT = "HTTP"; + + public static final String CONFIG_UPNP_ENABLE = "UPnP Enable"; + public boolean CONFIG_UPNP_ENABLE_DEFAULT = true; + + public static final String CONFIG_HOME_PAGE = PR_HOME_PAGE; + public String CONFIG_HOME_PAGE_DEFAULT = "index.html"; + + public static final String CONFIG_ROOT_DIR = PR_ROOT_DIR; + public String CONFIG_ROOT_DIR_DEFAULT = ""; + + public static final String CONFIG_ROOT_RESOURCE = PR_ROOT_RESOURCE; + public String CONFIG_ROOT_RESOURCE_DEFAULT = ""; + + public static final String CONFIG_MODE = "Mode"; + public static final String CONFIG_MODE_FULL = "full"; + public static final String CONFIG_MODE_DEFAULT = CONFIG_MODE_FULL; + + public static final String CONFIG_ACCESS = PR_ACCESS; + public String CONFIG_ACCESS_DEFAULT = "all"; + + protected static final String NL = "\r\n"; + + protected static final String[] welcome_pages = { "index.html", "index.htm", "index.php", "index.tmpl" }; +} diff --git a/src/java/org/klomp/snark/rpc/XMWebUIPlugin.java b/src/java/org/klomp/snark/rpc/XMWebUIPlugin.java new file mode 100644 index 0000000..59c0503 --- /dev/null +++ b/src/java/org/klomp/snark/rpc/XMWebUIPlugin.java @@ -0,0 +1,5654 @@ +/* + * Created on Sep 16, 2009 + * Created by Paul Gardner + * + * Copyright 2009 Vuze, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ +package org.klomp.snark.rpc; + +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.net.URL; +import java.text.DateFormat; +import java.text.NumberFormat; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.GZIPInputStream; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import static com.aelitis.azureus.plugins.xmwebui.TransmissionVars.*; + +import net.i2p.CoreVersion; +import net.i2p.I2PAppContext; +import net.i2p.data.Base32; +import net.i2p.data.Base64; +import net.i2p.data.Destination; +import net.i2p.servlet.RequestWrapper; +import net.i2p.util.Log; + +import org.gudy.azureus2.plugins.download.DownloadException; + +import org.klomp.snark.BitField; +import org.klomp.snark.I2PSnarkUtil; +import org.klomp.snark.MetaInfo; +import org.klomp.snark.Peer; +import org.klomp.snark.PeerID; +import org.klomp.snark.Snark; +import org.klomp.snark.SnarkManager; +import org.klomp.snark.Storage; +import org.klomp.snark.TrackerClient; +import org.klomp.snark.bencode.BEncoder; + +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; + +@SuppressWarnings({ + "unchecked", + "rawtypes" +}) + +/** + * Mapping of Vuze to i2psnark classes: + * Torrent :: MetaInfo + * Download :: Snark + * DownloadManager :: SnarkManager + * + * + * + */ +public class +XMWebUIPlugin { + private static final boolean IS_5101_PLUS = true; + /** + * 5: No longer xml escapes strings when user agent does not start with "Mozilla/" + */ + private static final int VUZE_RPC_VERSION = 5; + + public static final int DEFAULT_PORT = 9091; + private static final Properties defaults = new Properties(); + + static { + //System.setProperty( "az.xmwebui.skip.ssl.hack", "true" ); + defaults.put( WebPlugin.PR_DISABLABLE, Boolean.TRUE); + defaults.put( WebPlugin.PR_ENABLE, Boolean.TRUE); + defaults.put( WebPlugin.PR_PORT, Integer.valueOf(DEFAULT_PORT)); + defaults.put( WebPlugin.PR_ROOT_DIR, "transmission/web" ); + defaults.put( WebPlugin.PR_ENABLE_KEEP_ALIVE, Boolean.TRUE); + defaults.put( WebPlugin.PR_HIDE_RESOURCE_CONFIG, Boolean.TRUE); + defaults.put( WebPlugin.PR_PAIRING_SID, "xmwebui" ); + } + + private static final String SEARCH_PREFIX = "/psearch"; + private static final int SEARCH_TIMEOUT = 60*1000; + private static final long SEARCH_AUTOREMOVE_TIMEOUT = 60 * 1000 * 60l; + private boolean view_mode; + private final boolean trace_param; + private final boolean hide_ln_param = true; + //private TorrentAttribute t_id; + private Map recently_removed = new HashMap(); + //private Set stubbifying = new HashSet(); + private Map ip_to_session_id = new HashMap(); + private final String az_mode = "core"; + private static final String az_version = "5.7.5.1"; + private boolean check_ids_outstanding = true; + private Map> session_torrent_info_cache = new HashMap>(); + //private Object lifecycle_lock = new Object(); + //private int lifecycle_state = 0; + //private boolean update_in_progress; + private Object json_rpc_client; // Object during transition to core support + private Object json_server_method_lock = new Object(); + private transient Map json_server_methods = new HashMap(); // Object during transition to core support + private final boolean logtofile_param = false; + //private LoggerChannel log; + //private long lastVerserverCheck; + + private final I2PAppContext _context; + private final Log _log; + private final SnarkManager _manager; + private final I2PSnarkUtil _util; + + + public + XMWebUIPlugin(I2PAppContext ctx, SnarkManager mgr) { + _context = ctx; + _manager = mgr; + _util = mgr.util(); + _log = ctx.logManager().getLog(getClass()); + // must be set before start + trace_param = _log.shouldDebug(); + initialize(); + } + + private void + initialize() + { +/**** + log = plugin_interface.getLogger().getChannel( "xmwebui" ); + defaults.put(PR_LOG, log); + plugin_interface.getUtilities().getLocaleUtilities().integrateLocalisedMessageBundle( + "com.aelitis.azureus.plugins.xmwebui.internat.Messages" ); + t_id = plugin_interface.getTorrentManager().getPluginAttribute( "xmui.dl.id" ); + BasicPluginConfigModel config = getConfigModel(); + int port = plugin_interface.getPluginconfig().getPluginIntParameter( WebPlugin.CONFIG_PORT, CONFIG_PORT_DEFAULT ); + config.addLabelParameter2( "xmwebui.blank" ); + config.addHyperlinkParameter2( "xmwebui.openui", "http://127.0.0.1:" + port + "/" ); + config.addLabelParameter2( "xmwebui.blank" ); + //hide_ln_param = config.addBooleanParameter2( "xmwebui.hidelownoise", "xmwebui.hidelownoise", true ); + //trace_param = config.addBooleanParameter2( "xmwebui.trace", "xmwebui.trace", false ); + //logtofile_param = config.addBooleanParameter2( "xmwebui.logtofile", "xmwebui.logtofile", false ); + changeLogToFile(logtofile_param.getValue()); + logtofile_param.addConfigParameterListener(new ConfigParameterListener() { + public void configParameterChanged(ConfigParameter param) { + changeLogToFile(logtofile_param.getValue()); + } + }); + ConfigParameter mode_parameter = plugin_interface.getPluginconfig().getPluginParameter( WebPlugin.CONFIG_MODE ); + if ( mode_parameter == null ) { + view_mode = true; + checkViewMode(); + } else { + mode_parameter.addConfigParameterListener( + new ConfigParameterListener() + { + public void + configParameterChanged( + ConfigParameter param ) + { + setViewMode(); + } + }); + setViewMode(); + } + _manager.addListener( this ); + _manager.addSnarkListener( + new SnarkListener() + { + public void + downloadStubEventOccurred( + SnarkEvent event ) + throws DownloadException + { + int event_type = event.getEventType(); + List stubs = event.getSnarks(); + synchronized( recently_removed ) { + if ( event_type == SnarkEvent.DSE_STUB_WILL_BE_ADDED ) { + for ( Snark stub: stubs ) { + try { + long id = destubbify( stub ).getLongAttribute( t_id ); + stubbifying.add( id ); + stub.setLongAttribute( t_id, id ); + } catch( Throwable e ) { + Debug.out( e ); + } + } + } else if ( event_type == SnarkEvent.DSE_STUB_ADDED || event_type == SnarkEvent.DSE_STUB_WILL_BE_REMOVED ) { + for ( Snark stub: stubs ) { + long id = stub.getLongAttribute( t_id ); + stubbifying.remove( id ); + } + } + } + } + }, false ); + if ( IS_5101_PLUS ) { + json_rpc_client = + new Utilities.JSONClient() { + public void + serverRegistered( + JSONServer server ) + { + List methods = server.getSupportedMethods(); + //System.out.println( "Registering methods: " + server.getName() + " -> " + methods ); + synchronized( json_server_method_lock ) { + Map new_methods = new HashMap( json_server_methods ); + for ( String method: methods ) { + new_methods.put( method, server ); + } + json_server_methods = new_methods; + } + } + public void + serverUnregistered( + JSONServer server ) + { + List methods = server.getSupportedMethods(); + //System.out.println( "Unregistering methods: " + server.getName() + " -> " + methods ); + synchronized( json_server_method_lock ) { + Map new_methods = new HashMap( json_server_methods ); + for ( String method: methods ) { + new_methods.remove( method ); + } + json_server_methods = new_methods; + } + } + }; + plugin_interface.getUtilities().registerJSONRPCClient((Utilities.JSONClient)json_rpc_client ); + } +****/ + } + +/**** + protected void changeLogToFile(boolean logToFile) { + if (log != null) { + if (logToFile) { + log.setDiagnostic(1024l * 1024l, true); + } else { + // no way of turning off :( + } + } + } +****/ + + private void + checkViewMode() + { + if ( view_mode ) { + return; + } +/**** + PluginConfig pc = plugin_interface.getPluginconfig(); + { + String data_dir = pc.getCoreStringParameter( PluginConfig.CORE_PARAM_STRING_DEFAULT_SAVE_PATH ); + boolean data_bad = false; + if ( data_dir == null || data_dir.length() == 0 ) { + data_bad = true; + } else { + File dir = new File( data_dir ); + if ( !dir.exists()) { + dir.mkdirs(); + } + data_bad = !dir.canWrite(); + } + if ( data_bad ) { + _log.error(_t( "xmwebui.error.data_path" )); + } + } + if ( !pc.getUnsafeBooleanParameter( "Save Snark Files" )) { + _log.error(_t( "xmwebui.error.torrent_path" )); + } else { + String torrent_dir = pc.getUnsafeStringParameter( "General_sDefaultTorrent_Directory" ); + boolean torrent_bad = false; + if ( torrent_dir == null || torrent_dir.length() == 0 ) { + torrent_bad = true; + } else { + File dir = new File( torrent_dir ); + if ( !dir.exists()) { + dir.mkdirs(); + } + torrent_bad = !dir.canWrite(); + } + if ( torrent_bad ) { + _log.error(_t( "xmwebui.error.torrent_path" )); + } + } +****/ + } + + protected void + setupServer() + { + } + + public void + unload() + { + //_manager.removeListener( this ); + if ( IS_5101_PLUS ) { + if ( json_rpc_client != null ) { + //plugin_interface.getUtilities().unregisterJSONRPCClient((Utilities.JSONClient)json_rpc_client); + json_rpc_client = null; + } + json_server_methods.clear(); + } + } + + protected void + setViewMode() + { +/**** + String mode_str = plugin_interface.getPluginconfig().getPluginStringParameter( WebPlugin.CONFIG_MODE, WebPlugin.CONFIG_MODE_DEFAULT ); + view_mode = !mode_str.equalsIgnoreCase( WebPlugin.CONFIG_MODE_FULL ); +****/ + checkViewMode(); + } + + public File + getResourceDir() + { + return( new File( _manager.getDataDir(), "transmission" + File.separator + "web" )); + } + + public void + downloadAdded( + Snark download ) + { + } + + // @see org.gudy.azureus2.plugins.download.SnarkManagerListener#downloadRemoved(org.gudy.azureus2.plugins.download.Download) + public void + downloadRemoved( + Snark download ) + { + addRecentlyRemoved( download ); + } + + private void + addRecentlyRemoved( + Snark download ) + { + synchronized( recently_removed ) { + long id = getID( download, false ); + if ( id > 0 /* && !stubbifying.contains( id ) */ ) { + if ( !recently_removed.containsKey( id )) { + recently_removed.put( id, new RecentlyRemovedData( id )); + } + } + } + } + + private boolean + handleRecentlyRemoved( + String session_id, + Map args, + Map result ) + { + Object ids = args.get( "ids" ); + if ( ids != null && ids instanceof String && ((String)ids).equals( "recently-active" )) { + synchronized( recently_removed ) { + if ( recently_removed.size() > 0 ) { + long now = _context.clock().now(); + Iterator it = recently_removed.values().iterator(); + List removed = new ArrayList(); + while( it.hasNext()) { + RecentlyRemovedData rrd = it.next(); + if ( !rrd.hasSession( session_id )) { + removed.add( rrd.getID()); + } + if ( now - rrd.getCreateTime() > 60*1000 ) { + it.remove(); + } + } + if ( removed.size() > 0 ) { + //System.out.println( "Reporting removed to " + session_id + ": " + removed ); + result.put( "removed", removed ); + } + } + } + return( true ); + } else { + return( false ); + } + } + + public boolean + generateSupport(HttpServletRequest request, HttpServletResponse response) throws IOException { + boolean logit = trace_param; + if (logit) { + log("-> " + request.getServletPath()); + String qs = request.getQueryString(); + if (qs != null) + log( "-> query: " + qs); + for (Enumeration en = request.getHeaderNames(); en.hasMoreElements(); ) { + // cgi params overwrite JSON POST data map values + String hdr = en.nextElement(); + String val = request.getHeader(hdr); + log( "-> header: " + hdr + " = " + val); + } + } + if (/*request.getInputStream().available() == 0 && */ "chunked".equals(request.getHeader("transfer-encoding"))) { + response.setStatus( 415 ); + return true; + } + if (!request.getMethod().equals("POST")) { + response.setStatus( 405 ); + return true; + } + try { + String session_id = getSessionID( request ); + // Set cookie just in case client is looking for one.. + response.setHeader( "Set-Cookie", "X-Transmission-Session-Id=" + session_id + "; path=/; HttpOnly" ); + // This is the actual spec for massing session-id + response.setHeader("X-Transmission-Session-Id", session_id ); + if (!isSessionValid(request)) { + //log("Header:\n" + request.getHeader()); + log("409: " + request.getServletPath()); + response.setContentType( "text/plain; charset=UTF-8" ); + response.setStatus( 409 ); + response.getOutputStream().write("You_didn_t_set_the_X-Transmission-Session-Id".getBytes()); + return true; + } + String session_id_plus = session_id; + // XXX getHeaders() keys are lowercase.. this line always null? + String tid = request.getHeader( "X-XMRPC-Tunnel-ID" ); + if ( tid != null ) { + session_id_plus += "/" + tid; + } + String url = request.getServletPath(); + if (url.endsWith("/")) + url = url.substring(0, url.length() - 1); + //System.out.println( "Header: " + request.getHeader() ); + if (url.equals("/web/transmission/rpc") || url.equals("/rpc")) { + LineNumberReader lnr; + String enc = request.getHeader("content-encoding"); + if (enc != null && logit) + log( "-> encoding: " + enc ); + Map request_json = null; + // http://www.jsonrpc.org/historical/json-rpc-over-http.html + // Content-Type SHOULD be 'application/json-rpc' but MAY be 'application/json' or 'application/jsonrequest' + if (!"application/x-www-form-urlencoded".equals(request.getHeader("content-type"))) { + if ("gzip".equals(enc)) { + GZIPInputStream gzipIS = new GZIPInputStream(request.getInputStream()); + lnr = new LineNumberReader( new InputStreamReader( gzipIS, "UTF-8" )); + } else { + lnr = new LineNumberReader( new InputStreamReader( request.getInputStream(), "UTF-8" )); + } + StringBuilder request_json_str = new StringBuilder(2048); + while( true ) { + String line = lnr.readLine(); + if ( line == null ) { + break; + } + request_json_str.append( line ); + } + if ( logit ) { + log( "-> " + request_json_str ); + } + request_json = JSONUtils.decodeJSON( request_json_str.toString()); + } + for (Enumeration en = request.getParameterNames(); en.hasMoreElements(); ) { + // cgi params overwrite JSON POST data map values + String param = en.nextElement(); + String val = request.getParameter(param); + if (logit) + log( "-> param: " + param + " = " + val); + if (param.startsWith("{")) { + // json in body, but with x-www-form-urlencoded content-type (transmission-remote) + request_json = JSONUtils.decodeJSON(param); + } else if (param.equals("json")) { + // debugging? + request_json = JSONUtils.decodeJSON(val); + } else { + if (request_json == null) + request_json = new HashMap(4); + // cgi params or standard form + request_json.put(param, val); + } + } + Map response_json = processRequest( request, session_id_plus, request_json ); + String response_json_str = JSONUtils.encodeToJSON( response_json ); + if ( logit ) { + log( "<- " + response_json_str.length() ); + log( "<- " + response_json_str ); + } + response.setContentType( "application/json; charset=UTF-8" ); + PrintWriter pw = new PrintWriter( new OutputStreamWriter( response.getOutputStream(), "UTF-8" )); + pw.println( response_json_str ); + pw.flush(); + //response.setGZIP( true ); + return( true ); + /* example code to relay a stream + } else if ( url.startsWith( "/vuze/test.mkv" )) { + Map headers = request.getHeaders(); + OutputStream os = response.getRawOutputStream(); + Socket sock = new Socket( "127.0.0.1", 46409 ); + OutputStream sos = sock.getOutputStream(); + String req = "GET /Content/test.mkv HTTP/1.1\r\n"; + String range = (String)headers.get( "range" ); + if ( range != null ) { + req += "Range: " + range + "\r\n"; + } + req += "\r\n"; + sos.write( req.getBytes( "ISO-8859-1")); + sos.flush(); + InputStream is = sock.getInputStream(); + byte[] buffer = new byte[256*1024]; + while( true ) { + int len = is.read( buffer ); + if ( len <= 0 ) { + break; + } + os.write( buffer, 0, len ); + } + return( true ); + */ +/**** + } else if ( url.startsWith( "/vuze/resource?json=" )) { + Map request_json = JSONUtils.decodeJSON( UrlUtils.decode( url.substring( url.indexOf( '?' ) + 6 ))); + return( processResourceRequest( request, response, request_json )); +****/ + } else if (url.equals("/web/transmission/upload") || url.equals("/upload")) { + if ( logit ) { + log( "upload request" ); + } + checkUpdatePermissions(); + RequestWrapper rw = new RequestWrapper(request); + boolean add_stopped = Boolean.parseBoolean(rw.getParameter("paused")); + try { + int num_found = 0; + for ( Enumeration en = rw.getParameterNames(); en.hasMoreElements(); ) { + String field_name = en.nextElement(); + if ( field_name.equalsIgnoreCase( "torrent_file" ) || field_name.equalsIgnoreCase( "torrent_files[]" )) { + num_found++; + String torrent_file_name = rw.getParameter( "filename" ); + if ( torrent_file_name == null ) { + throw( new IOException( "upload filename missing" )); + } + InputStream tis = rw.getInputStream(field_name); + MetaInfo torrent; + try { + torrent = new MetaInfo( tis ); + } catch( Throwable e ) { + throw( new IOException( "Failed to deserialise torrent file", e)); + } + try { + Snark download = addTorrent( torrent, null, add_stopped, null ); + response.setContentType( "text/xml; charset=UTF-8" ); + response.getOutputStream().write( "

200: OK

".getBytes()); + return( true ); + } catch( Throwable e ) { + throw( new IOException("Failed to add torrent", e)); + } + } + } + if ( num_found == 0 ) { + log( "No torrents found in upload request" ); + } + return( true ); + } finally { + // nothing + } + } else if ( url.startsWith( "/web/transmission/web")) { + response.setStatus( 301 ); + response.setHeader( "Location", "/transmission/web/" ); + return( true ); + } else { + log( "404: " + url); + return( false ); + } + } catch( PermissionDeniedException e ) { + log( "401", e ); + response.setStatus( 401 ); + return( true ); + } catch( IOException e ) { + if ( logit ) { + log( "Processing failed", e ); + e.printStackTrace(); + } + throw( e ); + } catch( Throwable e ) { + if ( logit ) { + log( "Processing failed", e ); + e.printStackTrace(); + } + throw( new IOException( "Processing failed", e)); + } + } + + private String + getCookie( + String cookies, + String cookie_id) + { + if ( cookies == null ) { + return null; + } + String[] cookie_list = cookies.split( ";" ); + for ( String cookie: cookie_list ) { + String[] bits = cookie.split( "=" ); + if ( bits.length == 2 ) { + if ( bits[0].trim().equals( cookie_id )) { + return bits[1].trim(); + } + } + } + return null; + } + + private boolean + isSessionValid( + HttpServletRequest request) + { + //if (!request.getServletPath().startsWith("/transmission/")) { + // return true; + //} + // tunnel requests are already strongly authenticated and session based + //String tunnel = request.getHeader( "x-vuze-is-tunnel" ); + //if ( tunnel != null && tunnel.equalsIgnoreCase( "true" )) { + // return true; + //} + String session_id = getSessionID(request); + String header_session_id = request.getHeader( + "X-Transmission-Session-Id"); + if (header_session_id == null) { + header_session_id = request.getHeader( + "x-transmission-session-id"); + } + if (header_session_id == null) { + header_session_id = getCookie( + request.getHeader("cookie"), + "X-Transmission-Session-Id"); + } + //System.out.println("header_session_id=" + header_session_id); + if (header_session_id == null) { + return false; + } + return (header_session_id.equals(session_id)); + } + + private String + getSessionID( + HttpServletRequest request) + { + String clientAddress = request.getRemoteAddr(); + synchronized (ip_to_session_id) { + String session_id = ip_to_session_id.get(clientAddress); + if (session_id == null) { + session_id = Double.toHexString(_context.random().nextDouble()); + ip_to_session_id.put(clientAddress, session_id); + } + return session_id; + } + } + + private static final Object add_torrent_lock = new Object(); + + protected Snark + addTorrent( + final MetaInfo torrent, + File download_dir, + boolean add_stopped, + final DownloadWillBeAddedListener listener) + throws DownloadException + { + synchronized( add_torrent_lock ) { + final SnarkManager dm = _manager; + Snark download = dm.getTorrentByInfoHash( torrent.getInfoHash() ); + if ( download == null ) { + if (listener != null) { +/**** + dm.addDownloadWillBeAddedListener(new DownloadWillBeAddedListener() { + public void initialised(Snark dlAdding) { + boolean b = Arrays.equals(dlAdding.getInfoHash(), torrent.getInfoHash()); + if (b) { + dm.removeDownloadWillBeAddedListener(this); + listener.initialised(dlAdding); + } + } + }); +****/ + } + try { + boolean success = dm.addTorrent( torrent, null, null, download_dir, add_stopped ); + if (success) + download = dm.getTorrentByInfoHash(torrent.getInfoHash()); + } catch (IOException ioe) { + throw new DownloadException("Failed to add", ioe); + } + // particularly necessary for the android client as untidy closedown is common + //AzureusCoreFactory.getSingleton().saveState(); + } + return( download ); + } + } + + protected void + checkUpdatePermissions() + throws IOException + { + if ( view_mode ) { + log( "permission denied" ); + throw( new PermissionDeniedException()); + } + } + + protected Map + processRequest( + HttpServletRequest wp_request, + String session_id, + Map request ) + throws IOException + { + Map response = new HashMap(); + if (request == null) { + response.put( "result", "error: Bad or missing JSON string"); + return response; + } + String method = (String) request.get("method"); + if ( method == null ) { + response.put("result", "error: 'method' missing"); + } else { + Map args = (Map) request.get("arguments"); + if ( args == null ) { + args = Collections.emptyMap(); + } + try { + Map result = processRequest( wp_request, session_id, method, args ); + if ( result != null ) { + response.put( "arguments", result ); + } + response.put( "result", "success" ); + } catch( PermissionDeniedException e ) { + log("permission denied", e); + response.put( "result", "error: permission denied " + e ); + } catch( TextualException e ) { + response.put( "result", e.getMessage()); + } catch( Throwable e ) { + log("processRequest", e); + response.put( "result", "error: " + e ); + } + } + Object tag = request.get( "tag" ); + if ( tag != null ) { + response.put( "tag", tag ); + } + return( response ); + } + + private static Number getNumber( + Object val) + { + return getNumber(val, 0); + } + + private static Number getNumber( + Object val, + Number defaultNumber) + { + if (val instanceof Number) { + return (Number) val; + } + if (val instanceof String) { + NumberFormat format = NumberFormat.getInstance(); + try { + Number number = format.parse((String) val); + return number; + } catch (ParseException e) { + return defaultNumber; + } + } + return defaultNumber; + } + + protected Map + processRequest( + HttpServletRequest request, + String session_id, + String method, + Map args ) + throws Exception + { + boolean save_core_state = false; + try { + Map result = new HashMap(); + // https://trac.transmissionbt.com/browser/trunk/extras/rpc-spec.txt + // to get 271 working with this backend change remote.js RPC _Root to be + // _Root : './transmission/rpc', + if (false) { +/**** + if ( method.equals( "session-set" )) { + try { + method_Session_Set(args, result); + } finally { + // assume something important was changed and persist it now + COConfigurationManager.save(); + } +****/ + } else if ( method.equals( "session-get" ) ) { + method_Session_Get(args, result); + } else if ( method.equals( "session-stats" )) { + method_Session_Stats(args, result); +/**** + } else if ( method.equals( "torrent-add" )) { + String agent = MapUtils.getMapString(request.getHeaders(), "User-Agent", ""); + boolean xmlEscape = agent.startsWith("Mozilla/"); + method_Torrent_Add(args, result, xmlEscape); + // this is handled within the torrent-add method: save_core_state = true; +****/ + } else if ( method.equals( "torrent-start-all" )) { + checkUpdatePermissions(); + _manager.startAllTorrents(); + save_core_state = true; + } else if ( method.equals( "torrent-stop-all" )) { + checkUpdatePermissions(); + _manager.stopAllTorrents(false); + save_core_state = true; + } else if ( method.equals( "torrent-start" )) { + method_Torrent_Start(args, result); + save_core_state = true; + } else if ( method.equals( "torrent-start-now" )) { + // RPC v14 + method_Torrent_Start_Now(args, result); + save_core_state = true; + } else if ( method.equals( "torrent-stop" )) { + method_Torrent_Stop(args, result); + save_core_state = true; + } else if ( method.equals( "torrent-verify" )) { + method_Torrent_Verify(args, result); + } else if ( method.equals( "torrent-remove" )) { + // RPC v3 + method_Torrent_Remove(args, result); + save_core_state = true; +/**** + } else if ( method.equals( "torrent-set" )) { + method_Torrent_Set( session_id, args, result); +****/ + } else if ( method.equals( "torrent-get" )) { + method_Torrent_Get(request, session_id, args, result); +/**** + } else if ( method.equals( "torrent-reannounce" )) { + // RPC v5 + method_Torrent_Reannounce(args, result); + } else if ( method.equals( "torrent-set-location" )) { + // RPC v6 + method_Torrent_Set_Location(args, result); + } else if ( method.equals( "blocklist-update" )) { + // RPC v5 + method_Blocklist_Update(args, result); +****/ + } else if ( method.equals( "session-close" )) { + // RPC v12 + synchronized (ip_to_session_id) { + ip_to_session_id.remove(session_id); + } +/**** + } else if ( method.equals( "queue-move-top" )) { + // RPC v14 + method_Queue_Move_Top(args, result); + } else if ( method.equals( "queue-move-up" )) { + // RPC v14 + method_Queue_Move_Up(args, result); + } else if ( method.equals( "queue-move-down" )) { + // RPC v14 + method_Queue_Move_Down(args, result); + } else if ( method.equals( "queue-move-bottom" )) { + // RPC v14 + method_Queue_Move_Bottom(args, result); +*/ + } else if ( method.equals( "free-space" )) { + // RPC v15 + method_Free_Space(args, result); +/* + } else if ( method.equals( "torrent-rename-path" )) { + // RPC v15 + method_Torrent_Rename_Path(args, result); + } else if ( method.equals( "tags-get-list" )) { + // Vuze RPC v3 + method_Tags_Get_List(args, result); + } else if ( method.equals( "tags-lookup-start" )) { + method_Tags_Lookup_Start(args, result); + } else if ( method.equals( "tags-lookup-get-results" )) { + method_Tags_Lookup_Get_Results(args, result); + } else if ( method.equals( "subscription-get" )) { + method_Subscription_Get(args, result); + } else if ( method.equals( "subscription-add" )) { + method_Subscription_Add(args, result); + } else if ( method.equals( "subscription-set" )) { + method_Subscription_Set(args, result); + } else if ( method.equals( "subscription-remove" )) { + method_Subscription_Remove(args, result); + } else if ( method.equals( "vuze-search-start" )) { + method_Vuze_Search_Start(args, result); + } else if ( method.equals( "vuze-search-get-results" )) { + method_Vuze_Search_Get_Results(args, result); + } else if ( method.equals( "vuze-config-set" )) { + method_Vuze_Config_Set(args, result); + } else if ( method.equals( "vuze-config-get" )) { + method_Vuze_Config_Get(args, result); + } else if ( method.equals( "vuze-lifecycle" )) { + processVuzeLifecycle( args, result ); + } else if ( method.equals( "vuze-pairing" )) { + if ( IS_5101_PLUS ) { + processVuzePairing( args, result ); + } else { + throw( new IOException( "Client version too old!" )); + } + } else if ( method.equals( "vuze-torrent-get" )) { + processVuzeTorrentGet( request, args, result ); + } else if ( method.equals( "vuze-file-add" )) { + processVuzeFileAdd( args, result ); +****/ + } else { +/**** + if ( IS_5101_PLUS ) { + Utilities.JSONServer server = (Utilities.JSONServer)json_server_methods.get( method ); + if ( server != null ) { + return( server.call( method, args )); + } + } +****/ + if ( trace_param ) { + log( "unhandled method: " + method); + } + throw new TextualException("unsupported method: " + method); + } + return( result ); + } finally { + if ( save_core_state ) { + // particularly necessary for the android client as untidy closedown is common + //AzureusCoreFactory.getSingleton().saveState(); + } + } + } + +/**** + private void method_Vuze_Config_Get(Map args, Map result) { + List listKeys = MapUtils.getMapList(args, "keys", Collections.EMPTY_LIST); + for (Object key : listKeys) { + String keyString = key.toString(); + if (ignoreConfigKey(keyString)) { + continue; + } + Object val = COConfigurationManager.getParameter(keyString); + if (val instanceof byte[]) { + // Place parsed string in key's value, B64 of bytes in key + ".B64" + String valString; + byte[] bytes = (byte[]) val; + try { + valString = new String(bytes, "UTF-8"); + } catch (Throwable e) { + valString = new String(bytes); + } + result.put(key, valString); + try { + result.put(key + ".B64", new String(Base64.encode(bytes), "utf8")); + } catch (UnsupportedEncodingException e) { + } + } else { + result.put(key, val); + } + } + } + + private void method_Vuze_Config_Set(Map args, Map result) { + Map mapDirect = MapUtils.getMapMap(args, "direct", Collections.EMPTY_MAP); + for (Object key : mapDirect.keySet()) { + String keyString = key.toString(); + if (ignoreConfigKey(keyString)) { + result.put(keyString, "key ignored"); + continue; + } + Object val = mapDirect.get(key); + boolean changed; + if (val instanceof String) { + changed = COConfigurationManager.setParameter(keyString, (String) val); + } else if (val instanceof Boolean) { + changed = COConfigurationManager.setParameter(keyString, (Boolean) val); + } else if (val instanceof Float) { + changed = COConfigurationManager.setParameter(keyString, (Float) val); + } else if (val instanceof Double) { + changed = COConfigurationManager.setParameter(keyString, + ((Number) val).floatValue()); + } else if (val instanceof Number) { + changed = COConfigurationManager.setParameter(keyString, + ((Number) val).longValue()); + } else if (val instanceof Map) { + changed = COConfigurationManager.setParameter(keyString, (Map) val); + } else { + result.put(keyString, "error"); + continue; + } + result.put(keyString, changed); + } + Map mapByteArray = MapUtils.getMapMap(args, "byteArray.B64", + Collections.EMPTY_MAP); + for (Object key : mapByteArray.keySet()) { + String keyString = key.toString(); + if (ignoreConfigKey(keyString)) { + result.put(keyString, "key ignored"); + continue; + } + Object val = mapByteArray.get(key); + if (val instanceof String) { + byte[] decode = Base64.decode((String) val); + boolean changed = COConfigurationManager.setParameter(keyString, + decode); + result.put(keyString, changed); + } else { + result.put(keyString, "error"); + } + } + COConfigurationManager.save(); + } + + private boolean + ignoreConfigKey( + String key ) + { + String lc_key = key.toLowerCase(Locale.US); + if (key.startsWith(CryptoManager.CRYPTO_CONFIG_PREFIX) + || lc_key.equals("id") || lc_key.equals("azbuddy.dchat.optsmap") + || lc_key.endsWith(".privx") || lc_key.endsWith(".user") + || lc_key.contains("password") || lc_key.contains("username") + || lc_key.contains("session key")) { + return (true); + } + Object value = COConfigurationManager.getParameter(key); + if (value instanceof byte[]) { + try { + value = new String((byte[]) value, "UTF-8"); + } catch (Throwable e) { + } + } + if (value instanceof String) { + if (((String) value).toLowerCase(Locale.US).endsWith(".b32.i2p")) { + return (true); + } + } + return (false); + } + + private void method_Tags_Lookup_Start(Map args, Map result) { + Object ids = args.get("ids"); + TagSearchInstance tagSearchInstance = new TagSearchInstance(); + try { + List listDefaultNetworks = new ArrayList(); + for (int i = 0; i < AENetworkClassifier.AT_NETWORKS.length; i++) { + String nn = AENetworkClassifier.AT_NETWORKS[i]; + String config_name = "Network Selection Default." + nn; + boolean enabled = COConfigurationManager.getBooleanParameter( + config_name, false); + if (enabled) { + listDefaultNetworks.add(nn); + } + } + org.gudy.azureus2.plugins.download.SnarkManager dlm = _manager; + String[] networks; + if (ids instanceof List) { + List idList = (List) ids; + for (Object id : idList) { + if (id instanceof String) { + String hash = (String) id; + byte[] hashBytes = ByteFormatter.decodeString(hash); + Snark download = dlm.getDownload(hashBytes); + SnarkManager dm = PluginCoreUtils.unwrap(download); + if (dm != null) { + networks = dm.getDownloadState().getNetworks(); + } else { + networks = listDefaultNetworks.toArray(new String[0]); + } + tagSearchInstance.addSearch(hash, hashBytes, networks); + synchronized (active_tagsearches) { + active_tagsearches.put(tagSearchInstance.getID(), tagSearchInstance); + } + } + } + } + } catch (Throwable t) { + } + result.put("id", tagSearchInstance.getID()); + } + + private void method_Tags_Lookup_Get_Results(Map args, Map result) + throws IOException { + String id = (String) args.get("id"); + if (id == null) { + throw (new IOException("ID missing")); + } + synchronized (active_tagsearches) { + TagSearchInstance search_instance = active_tagsearches.get(id); + if (search_instance != null) { + if (search_instance.getResults(result)) { + active_tagsearches.remove(id); + } + } else { + throw (new IOException("ID not found - already complete?")); + } + } + } + + private void method_Vuze_Search_Get_Results(Map args, Map result) + throws IOException { + String sid = (String) args.get("sid"); + if (sid == null) { + throw (new IOException("SID missing")); + } + synchronized (active_searches) { + SearchInstance search_instance = active_searches.get(sid); + if (search_instance != null) { + if (search_instance.getResults(result)) { + active_searches.remove(sid); + } + } else { + throw (new IOException("SID not found - already complete?")); + } + } + } + + private void method_Vuze_Search_Start(Map args, Map result) + throws IOException { + String expression = (String) args.get("expression"); + if (expression == null) { + throw (new IOException("Search expression missing")); + } + MetaSearchManager ms_manager = MetaSearchManagerFactory.getSingleton(); + MetaSearch ms = ms_manager.getMetaSearch(); + List sps = new ArrayList(); + sps.add(new SearchParameter("s", expression)); + SearchParameter[] parameters = sps.toArray(new SearchParameter[sps.size()]); + Map context = new HashMap(); + context.put(Engine.SC_SOURCE, "xmwebui"); + context.put(Engine.SC_REMOVE_DUP_HASH, "true"); + Engine[] engines = ms_manager.getMetaSearch().getEngines(true, true); + if (engines.length == 0) { + throw (new IOException("No search templates available")); + } + SearchInstance search_instance = new SearchInstance(this, engines); + engines = ms.search(engines, search_instance, parameters, null, context, + 100); + if (engines.length == 0) { + throw (new IOException("No search templates available")); + } + synchronized (active_searches) { + active_searches.put(search_instance.getSID(), search_instance); + } + search_instance.setEngines(engines); + result.put("sid", search_instance.getSID()); + List l_engines = new ArrayList(); + result.put("engines", l_engines); + for (Engine engine : engines) { + JSONObject map = new JSONObject(); + l_engines.add(map); + map.put("name", engine.getName()); + map.put("id", engine.getUID()); + map.put("favicon", engine.getIcon()); + map.put("dl_link_css", engine.getDownloadLinkCSS()); + map.put("selected", Engine.SEL_STATE_STRINGS[engine.getSelectionState()]); + map.put("source", Engine.ENGINE_SOURCE_STRS[engine.getSource()]); + int type = engine.getType(); + map.put("type", type < Engine.ENGINE_TYPE_STRS.length ? Engine.ENGINE_TYPE_STRS[type] : type); + } + } + + private void method_Subscription_Add(Map args, Map result) throws MalformedURLException, SubscriptionException { + String url = MapUtils.getMapString(args, "rss-url", null); + String name = MapUtils.getMapString(args, "name", + "Subscription " + DateFormat.getInstance().toString()); + boolean anonymous = MapUtils.getMapBoolean(args, "anonymous", false); + if (url != null) { + Subscription subRSS = SubscriptionManagerFactory.getSingleton().createRSS( + name, new URL(url), SubscriptionHistory.DEFAULT_CHECK_INTERVAL_MINS, anonymous, + null); + result.put("subscription", subRSS.getJSON()); + } + } +****/ + + /* + * { + * : + * { + * : value, + * "results" : { + * : value, + * etc + * }, + * etc + * }, + * etc + * } + */ +/**** + private void method_Subscription_Set(Map args, Map result) + throws SubscriptionException, IOException { + Object oIDs = args.get("ids"); + if (oIDs == null) { + throw new IOException("ids missing"); + } + if (!(oIDs instanceof Map)) { + throw new IOException("ids not map"); + } + Map mapSubscriptionIDs = (Map) oIDs; + SubscriptionManager subMan = SubscriptionManagerFactory.getSingleton(); + for (Object oSubscriptionID : mapSubscriptionIDs.keySet()) { + Subscription subs = subMan.getSubscriptionByID((String) oSubscriptionID); + if (subs == null) { + result.put(oSubscriptionID, "Error: Not Found"); + continue; + } + Object oVal = mapSubscriptionIDs.get(oSubscriptionID); + if (!(oVal instanceof Map)) { + continue; + } + Map mapSubscriptionFields = (Map) oVal; + // could change name, subscribed state, etc + int numChanged = 0; + for (Object oSubscriptionFieldName : mapSubscriptionFields.keySet()) { + String subscriptionFieldName = (String) oSubscriptionFieldName; + Object oSubscriptionFieldValue = mapSubscriptionFields.get( + subscriptionFieldName); + if (subscriptionFieldName.equals(FIELD_SUBSCRIPTION_NAME)) { + subs.setName((String) oSubscriptionFieldValue); + numChanged++; + } else if (subscriptionFieldName.equals(FIELD_SUBSCRIPTION_AUTO_DOWNLOAD) + && (oSubscriptionFieldValue instanceof Boolean)) { + subs.getHistory().setAutoDownload((Boolean) oSubscriptionFieldValue); + numChanged++; + } else if (subscriptionFieldName.equals(FIELD_SUBSCRIPTION_SUBSCRIBED) + && (oSubscriptionFieldValue instanceof Boolean)) { + subs.setSubscribed((Boolean) oSubscriptionFieldValue); + numChanged++; + } else if (subscriptionFieldName.equals(FIELD_SUBSCRIPTION_RESULTS) + && (oSubscriptionFieldValue instanceof Map)) { + Map map = new HashMap(); + Map mapResults = (Map) oSubscriptionFieldValue; + SubscriptionResult[] results = subs.getResults(false); + for (Object oResultKey : mapResults.keySet()) { + String subs_id = (String) oResultKey; + Map mapResultEntries = (Map) mapResults.get(oResultKey); + for (SubscriptionResult entry : results) { + if (entry.getID().equals(subs_id)) { + Boolean isRead = (Boolean) mapResultEntries.get( + FIELD_SUBSCRIPTION_RESULT_ISREAD); + if (isRead != null) { + numChanged++; + entry.setRead(isRead); + } + break; + } + } + } + } + if (numChanged > 0) { + Map map = buildSubscriptionMap(subs, null, null, true); + result.put(oSubscriptionID, map); + } + } + } + } + + private void method_Subscription_Remove(Map args, Map result) throws IOException { + Object oID = args.get("ids"); + if (oID == null) { + throw new IOException("ID missing"); + } + String[] ids = new String[0]; + if (oID instanceof String) { + ids = new String[] { (String) oID }; + } else if (oID instanceof List) { + ids = (String[]) ((List) oID).toArray(new String[0]); + } else if (oID instanceof Object[]) { + Object[] oIDS = (Object[]) oID; + ids = new String[oIDS.length]; + for (int i = 0; i < oIDS.length; i++) { + ids[i] = oIDS[i].toString(); + } + } + SubscriptionManager subMan = SubscriptionManagerFactory.getSingleton(); + for (String id : ids) { + Subscription subs = subMan.getSubscriptionByID(id); + if (subs == null) { + result.put(id, "Error: Not Found"); + } else { + subs.remove(); + result.put(id, "Removed"); + } + } + } +****/ + + /* + * For non-torrent specific: + * + * Subscriptions : + * { + * SubscriptionID : + * { + * Field:Value, + * }, + * SubscriptionID : + * { + * Field:Value, + * }, + * } + * + * For torrent specific: + * Subscriptions : + * { + * SubscriptionID: { + * torrentId: #, + * Field:Value, + * }, + * etc + * } + */ +/**** + private void method_Subscription_Get(Map args, Map result) + throws IOException { + boolean subscribedOnly = MapUtils.getMapBoolean(args, "subscribed-only", + true); + Map> mapSubcriptions = new HashMap>(); + SubscriptionManager subMan = SubscriptionManagerFactory.getSingleton(); + List fields = (List) args.get("fields"); + boolean all = fields == null || fields.size() == 0; + if (!all) { + // sort so we can't use Collections.binarySearch + Collections.sort(fields); + } + Object oTorrentHashes = args.get("torrent-ids"); + if (oTorrentHashes != null) { + List downloads = getDownloads(oTorrentHashes, false); + for (Snark stub : downloads) { + Subscription[] subs = subMan.getKnownSubscriptions( + stub.getInfoHash()); + if (subs != null) { + for (Subscription sub : subs) { + Map map = buildSubscriptionMap(sub, args, fields, all); + map.put("torrentID", getID(stub, false)); + mapSubcriptions.put(sub.getID(), map); + } + } + } + } else { + Subscription[] subscriptions; + Object oID = args.get("ids"); + String[] ids = new String[0]; + if (oID instanceof String) { + ids = new String[] { + (String) oID + }; + } else if (oID instanceof List) { + ids = (String[]) ((List) oID).toArray(new String[0]); + } else if (oID instanceof Object[]) { + Object[] oIDS = (Object[]) oID; + ids = new String[oIDS.length]; + for (int i = 0; i < oIDS.length; i++) { + ids[i] = oIDS[i].toString(); + } + } + if (ids.length == 0) { + subscriptions = subMan.getSubscriptions(subscribedOnly); + } else { + List list = new ArrayList(); + for (String id : ids) { + Subscription subscriptionByID = subMan.getSubscriptionByID(id); + if (subscriptionByID == null) { + mapSubcriptions.put(id, Collections.EMPTY_MAP); + } else { + list.add(subscriptionByID); + } + } + subscriptions = list.toArray(new Subscription[0]); + } + for (Subscription sub : subscriptions) { + Map map = buildSubscriptionMap(sub, args, fields, all); + mapSubcriptions.put(sub.getID(), map); + } + } + result.put("subscriptions", mapSubcriptions); + } + + private Map buildSubscriptionMap(Subscription sub, Map args, + List fields, boolean all) { + Map map = new HashMap(); + if (all || Collections.binarySearch(fields, + "json") >= 0) { + try { + Map mapJSON = JSONUtils.decodeJSON(sub.getJSON()); + mapJSON.remove("engines"); + map.put("json", mapJSON); + } catch (SubscriptionException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + } + if (all + || Collections.binarySearch(fields, FIELD_SUBSCRIPTION_NAME) >= 0) { + map.put(FIELD_SUBSCRIPTION_NAME, sub.getName()); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_ADDEDON) >= 0) { + map.put(FIELD_SUBSCRIPTION_ADDEDON, sub.getAddTime()); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_ASSOCIATION_COUNT) >= 0) { + map.put(FIELD_SUBSCRIPTION_ASSOCIATION_COUNT, + sub.getAssociationCount()); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_POPULARITY) >= 0) { + map.put(FIELD_SUBSCRIPTION_POPULARITY, sub.getCachedPopularity()); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_CATEGORY) >= 0) { + addNotNullToMap(map, FIELD_SUBSCRIPTION_CATEGORY, sub.getCategory()); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_CREATOR) >= 0) { + addNotNullToMap(map, FIELD_SUBSCRIPTION_CREATOR, sub.getCreatorRef()); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_ENGINE) >= 0) { + try { + Engine engine = sub.getEngine(); + if (engine != null) { + Map mapEngine = new HashMap(); + map.put(FIELD_SUBSCRIPTION_ENGINE, mapEngine); + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_ENGINE_NAME) >= 0) { + mapEngine.put("name", engine.getName()); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_ENGINE_NAMEX) >= 0) { + mapEngine.put(FIELD_SUBSCRIPTION_ENGINE_NAMEX, + engine.getNameEx()); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_ENGINE_TYPE) >= 0) { + map.put(FIELD_SUBSCRIPTION_ENGINE_TYPE, engine.getType()); + int type = engine.getType(); + mapEngine.put("type", type < Engine.ENGINE_TYPE_STRS.length + ? Engine.ENGINE_TYPE_STRS[type] : type); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_ENGINE_NAMEX) >= 0) { + mapEngine.put(FIELD_SUBSCRIPTION_ENGINE_NAMEX, + engine.getNameEx()); + } + //engine.getAutoDownloadSupported() same as sub.getAutoDownloadSupported + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_ENGINE_SOURCE) >= 0) { + mapEngine.put("source", + Engine.ENGINE_SOURCE_STRS[engine.getSource()]); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_ENGINE_LASTUPDATED) >= 0) { + mapEngine.put(FIELD_SUBSCRIPTION_ENGINE_LASTUPDATED, + engine.getLastUpdated()); + } + mapEngine.put("id", engine.getUID()); + addNotNullToMap(mapEngine, "favicon", engine.getIcon()); + mapEngine.put("dl_link_css", engine.getDownloadLinkCSS()); + mapEngine.put("selected", + Engine.SEL_STATE_STRINGS[engine.getSelectionState()]); + mapEngine.put("class", engine.getClass().getSimpleName()); + if (engine instanceof WebEngine) { + WebEngine web_engine = (WebEngine) engine; + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_ENGINE_URL) >= 0) { + mapEngine.put(FIELD_SUBSCRIPTION_ENGINE_URL, + web_engine.getSearchUrl(true)); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_ENGINE_AUTHMETHOD) >= 0) { + mapEngine.put(FIELD_SUBSCRIPTION_ENGINE_AUTHMETHOD, + web_engine.getAuthMethod()); + } + } + } + } catch (SubscriptionException e) { + } + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_HIGHEST_VERSION) >= 0) { + map.put(FIELD_SUBSCRIPTION_HIGHEST_VERSION, sub.getHighestVersion()); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_NAME_EX) >= 0) { + map.put(FIELD_SUBSCRIPTION_NAME_EX, sub.getNameEx()); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_QUERY_KEY) >= 0) { + map.put(FIELD_SUBSCRIPTION_QUERY_KEY, sub.getQueryKey()); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_REFERER) >= 0) { + map.put(FIELD_SUBSCRIPTION_REFERER, sub.getReferer()); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_TAG_UID) >= 0) { + map.put(FIELD_SUBSCRIPTION_TAG_UID, sub.getTagID()); + } + if (all + || Collections.binarySearch(fields, FIELD_SUBSCRIPTION_URI) >= 0) { + map.put(FIELD_SUBSCRIPTION_URI, sub.getURI()); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_ANONYMOUS) >= 0) { + map.put(FIELD_SUBSCRIPTION_ANONYMOUS, sub.isAnonymous()); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_AUTO_DL_SUPPORTED) >= 0) { + map.put(FIELD_SUBSCRIPTION_AUTO_DL_SUPPORTED, + sub.isAutoDownloadSupported()); + } + if (all + || Collections.binarySearch(fields, FIELD_SUBSCRIPTION_MINE) >= 0) { + map.put(FIELD_SUBSCRIPTION_MINE, sub.isMine()); + } + if (all + || Collections.binarySearch(fields, FIELD_SUBSCRIPTION_PUBLIC) >= 0) { + map.put(FIELD_SUBSCRIPTION_PUBLIC, sub.isPublic()); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_IS_SEARCH_TEMPLATE) >= 0) { + map.put(FIELD_SUBSCRIPTION_IS_SEARCH_TEMPLATE, sub.isSearchTemplate()); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_SUBSCRIBED) >= 0) { + map.put(FIELD_SUBSCRIPTION_SUBSCRIBED, sub.isSubscribed()); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_UPDATEABLE) >= 0) { + map.put(FIELD_SUBSCRIPTION_UPDATEABLE, sub.isUpdateable()); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_SHAREABLE) >= 0) { + map.put(FIELD_SUBSCRIPTION_SHAREABLE, sub.isShareable()); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_RESULTS_COUNT) >= 0) { + map.put(FIELD_SUBSCRIPTION_RESULTS_COUNT, sub.getResults(false).length); + } + SubscriptionHistory history = sub.getHistory(); + if (history != null) { + if (all || Collections.binarySearch(fields, + "newResultsCount") >= 0) { + map.put("newResultsCount", history.getNumUnread()); + } + if (all || Collections.binarySearch(fields, + "nextScanTime") >= 0) { + map.put("nextScanTime", history.getNextScanTime()); + } + if (all || Collections.binarySearch(fields, + "checkFrequency") >= 0) { + map.put("checkFrequency", history.getCheckFrequencyMins()); + } + if (all || Collections.binarySearch(fields, + "consecutiveFails") >= 0) { + map.put("consecutiveFails", history.getConsecFails()); + } + if (all || Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_AUTO_DOWNLOAD) >= 0) { + map.put(FIELD_SUBSCRIPTION_AUTO_DOWNLOAD, history.isAutoDownload()); + } + if (all || Collections.binarySearch(fields, + "authFail") >= 0) { + map.put("authFail", history.isAuthFail()); + } + if (all || Collections.binarySearch(fields, + "error") >= 0) { + addNotNullToMap(map, "error", history.getLastError()); + } + if (fields != null && Collections.binarySearch(fields, + FIELD_SUBSCRIPTION_RESULTS) >= 0) { + List listResults = new ArrayList(); + SubscriptionResult[] results = sub.getHistory().getResults(false); + List fieldsResults = args == null ? null : (List) args.get("results-fields"); + boolean allResults = fieldsResults == null || fieldsResults.size() == 0; + for (int i = 0; i < results.length; i++) { + SubscriptionResult r = results[i]; + listResults.add(buildSubscriptionResultMap(r, fieldsResults, allResults)); + } + map.put(FIELD_SUBSCRIPTION_RESULTS, listResults); + } + } + return map; + } + + private Map buildSubscriptionResultMap(SubscriptionResult r, + List fieldsResults, boolean allResults) { + Map jsonMap = r.toJSONMap(); + if (!allResults) { + jsonMap.keySet().retainAll(fieldsResults); + } + return jsonMap; + } + + private void addNotNullToMap(Map map, + String id, Object o) { + if (o == null) { + return; + } + map.put(id, o); + } + + private void method_Tags_Get_List(Map args, Map result) { + List fields = (List) args.get("fields"); + boolean all = fields == null || fields.size() == 0; + if (!all) { + // sort so we can't use Collections.binarySearch + Collections.sort(fields); + } + List> listTags = + new ArrayList>(); + TagManager tm = TagManagerFactory.getTagManager(); + List tagTypes = tm.getTagTypes(); + for (TagType tagType : tagTypes) { + List tags = tagType.getTags(); + for (Tag tag : tags) { + SortedMap map = new TreeMap(); + if (all || Collections.binarySearch(fields, FIELD_TAG_NAME) >= 0) { + map.put(FIELD_TAG_NAME, tag.getTagName(true)); + } + //map.put("taggableTypes", tag.getTaggableTypes()); // com.aelitis.azureus.core.tag.Taggable + if (all || Collections.binarySearch(fields, FIELD_TAG_COUNT) >= 0) { + map.put(FIELD_TAG_COUNT, tag.getTaggedCount()); + } + if (all || Collections.binarySearch(fields, FIELD_TAG_TYPE) >= 0) { + map.put(FIELD_TAG_TYPE, tag.getTagType().getTagType()); + } + if (all || Collections.binarySearch(fields, FIELD_TAG_TYPENAME) >= 0) { + map.put(FIELD_TAG_TYPENAME, tag.getTagType().getTagTypeName(true)); + } + if (all + || Collections.binarySearch(fields, FIELD_TAG_CATEGORY_TYPE) >= 0) { + if (tag instanceof Category) { + map.put(FIELD_TAG_CATEGORY_TYPE, ((Category) tag).getType()); + } + } + if (all || Collections.binarySearch(fields, FIELD_TAG_UID) >= 0) { + map.put(FIELD_TAG_UID, tag.getTagUID()); + } + if (all || Collections.binarySearch(fields, FIELD_TAG_ID) >= 0) { + map.put(FIELD_TAG_ID, tag.getTagID()); + } + if (all || Collections.binarySearch(fields, FIELD_TAG_COLOR) >= 0) { + int[] color = tag.getColor(); + if (color != null) { + String hexColor = "#"; + for (int c : color) { + if (c < 0x10) { + hexColor += "0"; + } + hexColor += Integer.toHexString(c); + } + map.put(FIELD_TAG_COLOR, hexColor); + } + } + if (all + || Collections.binarySearch(fields, FIELD_TAG_CANBEPUBLIC) >= 0) { + map.put(FIELD_TAG_CANBEPUBLIC, tag.canBePublic()); + } + if (all || Collections.binarySearch(fields, FIELD_TAG_PUBLIC) >= 0) { + map.put(FIELD_TAG_PUBLIC, tag.isPublic()); + } + if (all || Collections.binarySearch(fields, FIELD_TAG_VISIBLE) >= 0) { + map.put(FIELD_TAG_VISIBLE, tag.isVisible()); + } + if (all || Collections.binarySearch(fields, FIELD_TAG_GROUP) >= 0) { + map.put(FIELD_TAG_GROUP, tag.getGroup()); + } + if (all || Collections.binarySearch(fields, FIELD_TAG_AUTO_ADD) >= 0 + || Collections.binarySearch(fields, FIELD_TAG_AUTO_REMOVE) >= 0) { + boolean[] auto = tag.isTagAuto(); + if (all + || Collections.binarySearch(fields, FIELD_TAG_AUTO_ADD) >= 0) { + map.put(FIELD_TAG_AUTO_ADD, auto[0]); + } + if (all + || Collections.binarySearch(fields, FIELD_TAG_AUTO_REMOVE) >= 0) { + map.put(FIELD_TAG_AUTO_REMOVE, auto[1]); + } + } + listTags.add(map); + } + } + String hc = Long.toHexString(longHashSimpleList(listTags)); + result.put("tags-hc", hc); + String oldHC = MapUtils.getMapString(args, "tags-hc", null); + if (!hc.equals(oldHC)) { + result.put("tags", listTags); + } + } +****/ + +/* + This method tests how much free space is available in a + client-specified folder. + Method name: "free-space" + Request arguments: + string | value type & description + ------------+---------------------------------------------------------- + "path" | string the directory to query + Response arguments: + string | value type & description + ------------+---------------------------------------------------------- + "path" | string same as the Request argument + "size-bytes"| number the size, in bytes, of the free space in that directory + */ + private void method_Free_Space(Map args, Map result) { + // RPC v15 + Object oPath = args.get("path"); + if (!(oPath instanceof String)) { + return; + } + File file = new File((String) oPath); + while (file != null && !file.exists()) { + file = file.getParentFile(); + } + if (file == null) { + result.put("path", oPath); + result.put("size-bytes", 0); + return; + } + long space = file.getFreeSpace(); + result.put("path", oPath); + result.put("size-bytes", space); + } + +/* + string | value type & description + ------------+---------------------------------------------------------- + "ids" | array torrent list, as described in 3.1. +*/ +/**** + private void method_Queue_Move_Bottom(Map args, Map result) { + // RPC v14 + Object ids = args.get( "ids" ); + AzureusCore core = AzureusCoreFactory.getSingleton(); + GlobalManager gm = core.getGlobalManager(); + List dms = getSnarkManagerListFromIDs( gm, ids ); + gm.moveEnd(dms.toArray(new SnarkManager[0])); + } +****/ + +/* + string | value type & description + ------------+---------------------------------------------------------- + "ids" | array torrent list, as described in 3.1. +*/ +/**** + private void method_Queue_Move_Down(Map args, Map result) { + // RPC v14 + Object ids = args.get("ids"); + AzureusCore core = AzureusCoreFactory.getSingleton(); + GlobalManager gm = core.getGlobalManager(); + List dms = getSnarkManagerListFromIDs( gm, ids ); + Collections.sort(dms, new Comparator() { + public int compare(SnarkManager a, SnarkManager b) { + return b.getPosition() - a.getPosition(); + } + }); + for (SnarkManager dm : dms) { + gm.moveDown(dm); + } + } + +****/ + +/* + string | value type & description + ------------+---------------------------------------------------------- + "ids" | array torrent list, as described in 3.1. +*/ +/**** + private void method_Queue_Move_Up(Map args, Map result) { + // RPC v14 + Object ids = args.get("ids"); + AzureusCore core = AzureusCoreFactory.getSingleton(); + GlobalManager gm = core.getGlobalManager(); + List dms = getSnarkManagerListFromIDs( gm, ids ); + Collections.sort(dms, new Comparator() { + public int compare(SnarkManager a, SnarkManager b) { + return a.getPosition() - b.getPosition(); + } + }); + for (SnarkManager dm : dms) { + gm.moveUp(dm); + } + } +****/ + +/* + string | value type & description + ------------+---------------------------------------------------------- + "ids" | array torrent list, as described in 3.1. +*/ +/**** + private void method_Queue_Move_Top(Map args, Map result) { + // RPC v14 + Object ids = args.get( "ids" ); + AzureusCore core = AzureusCoreFactory.getSingleton(); + GlobalManager gm = core.getGlobalManager(); + List dms = getSnarkManagerListFromIDs( gm, ids ); + gm.moveTop(dms.toArray(new SnarkManager[0])); + } +****/ + + private void method_Session_Get(Map args, Map result) { + String save_dir = _manager.getDataDir().getAbsolutePath(); + result.put(TR_PREFS_KEY_BLOCKLIST_ENABLED, Boolean.FALSE); + result.put(TR_PREFS_KEY_BLOCKLIST_URL, ""); + // RPC v5, but no constant! + result.put( "blocklist-size", 0); // number number of rules in the blocklist + result.put(TR_PREFS_KEY_MAX_CACHE_SIZE_MB, 0 ); // TODO + result.put(TR_PREFS_KEY_DHT_ENABLED, _util.shouldUseDHT() ); + result.put(TR_PREFS_KEY_UTP_ENABLED, Boolean.FALSE ); + result.put(TR_PREFS_KEY_LPD_ENABLED, Boolean.FALSE ); + result.put(TR_PREFS_KEY_DOWNLOAD_DIR, save_dir); + // RPC 12 to 14 + result.put("download-dir-free-space", _manager.getDataDir().getFreeSpace()); + result.put(TR_PREFS_KEY_DSPEED_KBps, 99999); + result.put(TR_PREFS_KEY_DSPEED_ENABLED, Boolean.TRUE ); + result.put(TR_PREFS_KEY_ENCRYPTION, "required" ); // string "required", "preferred", "tolerated" + result.put(TR_PREFS_KEY_IDLE_LIMIT, 30 ); //TODO + result.put(TR_PREFS_KEY_IDLE_LIMIT_ENABLED, false );//TODO + result.put(TR_PREFS_KEY_INCOMPLETE_DIR, save_dir ); + result.put(TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, false );//TODO + //result.put(TR_PREFS_KEY_MSGLEVEL, TR_MSG_INF ); // Not in Spec + result.put(TR_PREFS_KEY_DOWNLOAD_QUEUE_SIZE, 5 );//TODO + result.put(TR_PREFS_KEY_DOWNLOAD_QUEUE_ENABLED, true ); //TODO + result.put(TR_PREFS_KEY_PEER_LIMIT_GLOBAL, _util.getMaxConnections() ); + result.put(TR_PREFS_KEY_PEER_LIMIT_TORRENT, _util.getMaxConnections() ); + result.put(TR_PREFS_KEY_PEER_PORT, TrackerClient.PORT ); + result.put(TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, false ); //TODO + //result.put(TR_PREFS_KEY_PEER_PORT_RANDOM_LOW, 49152 ); // Not in Spec + //result.put(TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH, 65535 ); // Not in Spec + //result.put(TR_PREFS_KEY_PEER_SOCKET_TOS, TR_DEFAULT_PEER_SOCKET_TOS_STR ); //TODO + result.put(TR_PREFS_KEY_PEX_ENABLED, Boolean.TRUE ); + result.put(TR_PREFS_KEY_PORT_FORWARDING, Boolean.FALSE ); + //result.put(TR_PREFS_KEY_PREALLOCATION, TR_PREALLOCATE_SPARSE ); //TODO + //result.put(TR_PREFS_KEY_PREFETCH_ENABLED, DEFAULT_PREFETCH_ENABLED ); //TODO + result.put(TR_PREFS_KEY_QUEUE_STALLED_ENABLED, true ); //TODO + result.put(TR_PREFS_KEY_QUEUE_STALLED_MINUTES, 30 ); //TODO + result.put(TR_PREFS_KEY_RATIO, 2.0 ); //TODO (wrong key?) + result.put(TR_PREFS_KEY_RATIO_ENABLED, false ); //TODO (wrong key?) + result.put(TR_PREFS_KEY_RENAME_PARTIAL_FILES, Boolean.FALSE ); + //result.put(TR_PREFS_KEY_RPC_AUTH_REQUIRED, false ); // Not in Spec + //String bindIP = pc.getPluginStringParameter(WebPlugin.CONFIG_BIND_IP); + //result.put(TR_PREFS_KEY_RPC_BIND_ADDRESS, bindIP == null || bindIP.length() == 0 ? "0.0.0.0" : bindIP ); + //result.put(TR_PREFS_KEY_RPC_ENABLED, false ); // Not in Spec + //result.put(TR_PREFS_KEY_RPC_PASSWORD, "" ); // Not in Spec + //result.put(TR_PREFS_KEY_RPC_USERNAME, "" ); // Not in Spec + //result.put(TR_PREFS_KEY_RPC_WHITELIST, TR_DEFAULT_RPC_WHITELIST ); // Not in Spec + //result.put(TR_PREFS_KEY_RPC_WHITELIST_ENABLED, true ); // Not in Spec + //result.put(TR_PREFS_KEY_RPC_PORT, atoi( TR_DEFAULT_RPC_PORT_STR ) ); // Not in Spec + //result.put(TR_PREFS_KEY_RPC_URL, TR_DEFAULT_RPC_URL_STR ); // Not in Spec + //result.put(TR_PREFS_KEY_SCRAPE_PAUSED_TORRENTS, true ); // Not in Spec + result.put(TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, "" ); //TODO + result.put(TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, false ); //TODO + result.put(TR_PREFS_KEY_SEED_QUEUE_SIZE, 10 ); //TODO + result.put(TR_PREFS_KEY_SEED_QUEUE_ENABLED, false ); //TODO + result.put(TR_PREFS_KEY_ALT_SPEED_ENABLED, false ); //TODO + result.put(TR_PREFS_KEY_ALT_SPEED_UP_KBps, 50 ); //TODO + result.put(TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, 50 ); //TODO + result.put(TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, 540 ); // 9am //TODO + result.put(TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, false ); //TODO + result.put(TR_PREFS_KEY_ALT_SPEED_TIME_END, 1020 ); // 5pm //TODO + result.put(TR_PREFS_KEY_ALT_SPEED_TIME_DAY, TR_SCHED_ALL ); //TODO + result.put(TR_PREFS_KEY_USPEED_KBps, _util.getMaxUpBW()); + result.put(TR_PREFS_KEY_USPEED_ENABLED, Boolean.TRUE); + //result.put(TR_PREFS_KEY_UMASK, 022 ); // Not in Spec + result.put(TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, Math.min(_util.getMaxUploaders(), 8)); // PeerCoordinator.MAX_UPLOADERS + //result.put(TR_PREFS_KEY_BIND_ADDRESS_IPV4, TR_DEFAULT_BIND_ADDRESS_IPV4 ); //TODO + //result.put(TR_PREFS_KEY_BIND_ADDRESS_IPV6, TR_DEFAULT_BIND_ADDRESS_IPV6 ); //TODO + result.put("config-dir", _manager.getConfigDir().getAbsolutePath() ); + boolean startStopped = _manager.shouldAutoStart(); + result.put(TR_PREFS_KEY_START, !startStopped ); //TODO + result.put(TR_PREFS_KEY_RENAME_PARTIAL_FILES, Boolean.FALSE); + result.put(TR_PREFS_KEY_TRASH_ORIGINAL, false ); //TODO + result.put( "port", TrackerClient.PORT ); // number port number + result.put( "rpc-version", Long.valueOf(15)); // number the current RPC API version + result.put( "rpc-version-minimum", Long.valueOf(6)); // number the minimum RPC API version supported + result.put( "seedRatioLimit", Double.valueOf(100.0) ); // double the default seed ratio for torrents to use + result.put( "seedRatioLimited", Boolean.FALSE ); // boolean true if seedRatioLimit is honored by default + result.put( "version", CoreVersion.VERSION); // string + result.put( "az-rpc-version", VUZE_RPC_VERSION); + result.put( "az-version", az_version ); // string + result.put( "az-mode", az_mode ); // string + //result.put( "rpc-i2p-address", pc.getPluginStringParameter("webui.i2p_dest")); + //result.put( "rpc-tor-address", pc.getPluginStringParameter("webui.tor_dest")); + result.put( "az-content-port", getMediaServerActivePort()); +/**** + List listSupports = new ArrayList(); + Collections.addAll(listSupports, "rpc:receive-gzip", "field:files-hc", + "method:tags-get-list", "field:torrent-set-name", + "method:subscription-get", "method:subscription-add", + "method:subscription-remove", "method:subscription-set", + "method:vuze-plugin-get-list", "method:tags-lookup-start", + "method:tags-lookup-get-results", "method:vuze-search-start", + "method:vuze-search-get-results", "torrent-add:torrent-duplicate"); + synchronized( json_server_method_lock ) { + for (String key : json_server_methods.keySet()) { + listSupports.add("method:" + key); + } + } + result.put("rpc-supports", listSupports); + if (lastVerserverCheck == 0 || _context.clock().now() - lastVerserverCheck > 864000l) { + lastVerserverCheck = _context.clock().now(); + Map decoded = VersionCheckClient.getSingleton().getVersionCheckInfo("xmw"); + String userMessage = getUserMessage(decoded); + if (userMessage != null) { + result.put("az-message", userMessage); + } + } + //result.put("az-message", "This is a test message with a Link"); +****/ + } + + private int getMediaServerActivePort() { + return 7657; + } + +/**** + private String + getUserMessage( + Map reply ) + { + try { + byte[] message_bytes = MapUtils.getMapByteArray(reply, "xmwebui_message", + null); + if (message_bytes == null || message_bytes.length == 0) { + return null; + } + String message; + try { + message = new String(message_bytes, "UTF-8"); + } catch (Throwable e) { + message = new String(message_bytes); + } + byte[] signature = MapUtils.getMapByteArray(reply, "xmwebui_message_sig", + null); + if (signature == null) { + log("Signature missing from message"); + return null; + } + try { + AEVerifier.verifyData(message, signature); + } catch (Throwable e) { + log("Message signature check failed", e); + return null; + } + return message; + } catch (Throwable e) { + log("Failed get message", e); + Debug.printStackTrace(e); + } + return null; + } +****/ + +/* + "download-queue-size" | number | max number of torrents to download at once (see download-queue-enabled) + "download-queue-enabled" | boolean | if true, limit how many torrents can be downloaded at once + "dht-enabled" | boolean | true means allow dht in public torrents + "encryption" | string | "required", "preferred", "tolerated" + "idle-seeding-limit" | number | torrents we're seeding will be stopped if they're idle for this long + "idle-seeding-limit-enabled" | boolean | true if the seeding inactivity limit is honored by default + "incomplete-dir" | string | path for incomplete torrents, when enabled + "incomplete-dir-enabled" | boolean | true means keep torrents in incomplete-dir until done + "lpd-enabled" | boolean | true means allow Local Peer Discovery in public torrents + "peer-limit-global" | number | maximum global number of peers + "peer-limit-per-torrent" | number | maximum global number of peers + "pex-enabled" | boolean | true means allow pex in public torrents + "peer-port" | number | port number + "peer-port-random-on-start" | boolean | true means pick a random peer port on launch + "port-forwarding-enabled" | boolean | true means enabled + "queue-stalled-enabled" | boolean | whether or not to consider idle torrents as stalled + "queue-stalled-minutes" | number | torrents that are idle for N minuets aren't counted toward seed-queue-size or download-queue-size + "rename-partial-files" | boolean | true means append ".part" to incomplete files + "script-torrent-done-filename" | string | filename of the script to run + "script-torrent-done-enabled" | boolean | whether or not to call the "done" script + "seedRatioLimit" | double | the default seed ratio for torrents to use + "seedRatioLimited" | boolean | true if seedRatioLimit is honored by default + "seed-queue-size" | number | max number of torrents to uploaded at once (see seed-queue-enabled) + "seed-queue-enabled" | boolean | if true, limit how many torrents can be uploaded at once + "speed-limit-down" | number | max global download speed (KBps) + "speed-limit-down-enabled" | boolean | true means enabled + "speed-limit-up" | number | max global upload speed (KBps) + "speed-limit-up-enabled" | boolean | true means enabled + "start-added-torrents" | boolean | true means added torrents will be started right away + "trash-original-torrent-files" | boolean | true means the .torrent file of added torrents will be deleted + "utp-enabled" | boolean | true means allow utp + */ +/**** + private void method_Session_Set(Map args, Map result) + throws IOException { + checkUpdatePermissions(); + PluginConfig pc = plugin_interface.getPluginconfig(); + for (Map.Entry arg : ((Map) args).entrySet()) { + String key = arg.getKey(); + Object val = arg.getValue(); + try { + if (key.startsWith("alt-speed")) { + // TODO: + // "alt-speed-down" | number | max global download speed (KBps) + // "alt-speed-enabled" | boolean | true means use the alt speeds + // "alt-speed-time-begin" | number | when to turn on alt speeds (units: minutes after midnight) + // "alt-speed-time-enabled" | boolean | true means the scheduled on/off times are used + // "alt-speed-time-end" | number | when to turn off alt speeds (units: same) + // "alt-speed-time-day" | number | what day(s) to turn on alt speeds (look at tr_sched_day) + // "alt-speed-up" | number | max global upload speed (KBps) + } else if (key.equals("blocklist-url")) { + // "blocklist-url" | string | location of the blocklist to use for "blocklist-update" + IpFilter ipFilter = IpFilterManagerFactory.getSingleton().getIPFilter(); + COConfigurationManager.setParameter("Ip Filter Autoload File", + (String) val); + COConfigurationManager.setParameter( + IpFilterAutoLoaderImpl.CFG_AUTOLOAD_LAST, 0); + try { + ipFilter.reload(); + } catch (Exception e) { + e.printStackTrace(); + } + } else if (key.equals("blocklist-enabled")) { + // "blocklist-enabled" | boolean | true means enabled + plugin_interface.getIPFilter().setEnabled(getBoolean(val)); + } else if (key.equals("cache-size-mb")) { + // "cache-size-mb" | number | maximum size of the disk cache (MB) + // umm.. not needed + } else if (key.equals("download-dir")) { + // "download-dir" | string | default path to download torrents + String dir = (String) val; + String save_dir = pc.getCoreStringParameter(PluginConfig.CORE_PARAM_STRING_DEFAULT_SAVE_PATH); + if (!save_dir.equals(dir)) { + save_dir = dir; + pc.setCoreStringParameter( + PluginConfig.CORE_PARAM_STRING_DEFAULT_SAVE_PATH, dir); + } + } else if (key.equals(TR_PREFS_KEY_START)) { + COConfigurationManager.setParameter("Default Start Torrents Stopped", !getBoolean(val)); + } else if (key.equals(TR_PREFS_KEY_RENAME_PARTIAL_FILES)) { + COConfigurationManager.setParameter("Rename Incomplete Files", getBoolean(val)); + } else if (key.equals("speed-limit-down-enabled") + || key.equals("downloadLimited")) { + int down_limit = pc.getCoreIntParameter(PluginConfig.CORE_PARAM_INT_MAX_DOWNLOAD_SPEED_KBYTES_PER_SEC); + boolean enable = getBoolean(val); + if (!enable && down_limit != 0) { + down_limit = 0; + pc.setCoreIntParameter( + PluginConfig.CORE_PARAM_INT_MAX_DOWNLOAD_SPEED_KBYTES_PER_SEC, + down_limit); + } else if (enable && down_limit == 0) { + int lastRate = pc.getUnsafeIntParameter("config.ui.speed.partitions.manual.download.last"); + if (lastRate <= 0) { + lastRate = 10; + } + pc.setCoreIntParameter( + PluginConfig.CORE_PARAM_INT_MAX_DOWNLOAD_SPEED_KBYTES_PER_SEC, + lastRate); + } + } else if (key.equals("speed-limit-down") + || key.equals("downloadLimit")) { + int down_limit = pc.getCoreIntParameter(PluginConfig.CORE_PARAM_INT_MAX_DOWNLOAD_SPEED_KBYTES_PER_SEC); + int limit = getNumber(val).intValue(); + if (limit != down_limit) { + down_limit = limit; + pc.setCoreIntParameter( + PluginConfig.CORE_PARAM_INT_MAX_DOWNLOAD_SPEED_KBYTES_PER_SEC, + limit); + } + } else if (key.equals("speed-limit-up-enabled") + || key.equals("uploadLimited")) { + boolean enable = getBoolean(val); + // turn off auto speed for both normal and seeding-only mode + // this will reset upload speed to what it was before it was on + pc.setCoreBooleanParameter( + PluginConfig.CORE_PARAM_BOOLEAN_AUTO_SPEED_ON, false); + pc.setCoreBooleanParameter( + PluginConfig.CORE_PARAM_BOOLEAN_AUTO_SPEED_SEEDING_ON, false); + int up_limit = pc.getCoreIntParameter(PluginConfig.CORE_PARAM_INT_MAX_UPLOAD_SPEED_KBYTES_PER_SEC); + int up_limit_seeding = pc.getCoreIntParameter(PluginConfig.CORE_PARAM_INT_MAX_UPLOAD_SPEED_SEEDING_KBYTES_PER_SEC); + if (!enable) { + pc.setCoreIntParameter( + PluginConfig.CORE_PARAM_INT_MAX_UPLOAD_SPEED_KBYTES_PER_SEC, 0); + pc.setCoreIntParameter( + PluginConfig.CORE_PARAM_INT_MAX_UPLOAD_SPEED_SEEDING_KBYTES_PER_SEC, + 0); + } else if (enable && (up_limit == 0 || up_limit_seeding == 0)) { + int lastRate = pc.getUnsafeIntParameter("config.ui.speed.partitions.manual.upload.last"); + if (lastRate <= 0) { + lastRate = 10; + } + pc.setCoreIntParameter( + PluginConfig.CORE_PARAM_INT_MAX_UPLOAD_SPEED_KBYTES_PER_SEC, lastRate); + pc.setCoreIntParameter( + PluginConfig.CORE_PARAM_INT_MAX_UPLOAD_SPEED_SEEDING_KBYTES_PER_SEC, + lastRate); + } + } else if (key.equals("speed-limit-up") || key.equals("uploadLimit")) { + // turn off auto speed for both normal and seeding-only mode + // this will reset upload speed to what it was before it was on + pc.setCoreBooleanParameter( + PluginConfig.CORE_PARAM_BOOLEAN_AUTO_SPEED_ON, false); + pc.setCoreBooleanParameter( + PluginConfig.CORE_PARAM_BOOLEAN_AUTO_SPEED_SEEDING_ON, false); + int limit = getNumber(val).intValue(); + pc.setCoreIntParameter( + PluginConfig.CORE_PARAM_INT_MAX_UPLOAD_SPEED_KBYTES_PER_SEC, + limit); + pc.setCoreIntParameter( + PluginConfig.CORE_PARAM_INT_MAX_UPLOAD_SPEED_SEEDING_KBYTES_PER_SEC, + limit); + } else if (key.equals("peer-port") || key.equals("port")) { + int port = getNumber(val).intValue(); + pc.setCoreIntParameter(PluginConfig.CORE_PARAM_INT_INCOMING_TCP_PORT, + port); + } else if (key.equals("encryption")) { + String value = (String) val; + boolean required = value.equals("required"); + COConfigurationManager.setParameter( + "network.transport.encrypted.require", required); + } else if (key.equals("seedRatioLimit")) { + // RPC v5 + float ratio = getNumber(val).floatValue(); + COConfigurationManager.setParameter("Stop Ratio", ratio); + } else if (key.equals("seedRatioLimited")) { + // RPC v5 + boolean limit = getBoolean(val); + float ratio; + if (limit) { + // 2f is made up; sharing is caring + if (args.containsKey("seedRatioLimit")) { + ratio = getNumber(args.get("seedRatioLimit"), 2f).floatValue(); + } else { + ratio = 2f; + } + } else { + ratio = 0f; + } + COConfigurationManager.setParameter("Stop Ratio", ratio); + } else { + if ( trace_param ) { + log("Unhandled session-set field: " + key); + } + } + } catch (Throwable t) { + Debug.out(key + ":" + val, t); + } + } + } + + private void method_Blocklist_Update(Map args, Map result) { + // TODO + log("blocklist-update not supported"); + } +****/ + + /* + Request arguments: + string | value type & description + ---------------------------------+------------------------------------------------- + "ids" | array the torrent torrent list, as described in 3.1 + | (must only be 1 torrent) + "path" | string the path to the file or folder that will be renamed + "name" | string the file or folder's new name + Response arguments: "path", "name", and "id", holding the torrent ID integer + */ +/**** + private void + method_Torrent_Rename_Path( + Map args, + Map result) + { + if ( trace_param ) { + log( "unhandled method: torrent-rename-path - " + args ); + } + } +****/ + + /* + Request arguments: + string | value type & description + ---------------------------+------------------------------------------------- + "ids" | array torrent list, as described in 3.1 + "location" | string the new torrent location + "move" | boolean if true, move from previous location. + | otherwise, search "location" for files + | (default: false) + Response arguments: none + */ +/**** + private void + method_Torrent_Set_Location( + Map args, + Map result) + throws IOException, DownloadException + { + checkUpdatePermissions(); + Object ids = args.get( "ids" ); + boolean moveData = getBoolean( args.get( "move" )); + String sSavePath = (String) args.get("location"); + List downloads = getDownloads( ids, false ); + File fSavePath = new File(sSavePath); + for ( Snark download : downloads ) { + if (moveData) { + MetaInfo torrent = download.getTorrent(); + if (torrent == null || torrent.isSimpleTorrent() + || fSavePath.getParentFile() == null) { + download.moveDataFiles(fSavePath); + } else { + download.moveDataFiles(fSavePath.getParentFile(), fSavePath.getName()); + } + } else { + SnarkManager dm = PluginCoreUtils.unwrap(download); + // This is copied from TorrentUtils.changeDirSelectedTorrent + int state = dm.getState(); + if (state == SnarkManager.STATE_STOPPED) { + if (!dm.filesExist(true)) { + state = SnarkManager.STATE_ERROR; + } + } + if (state == SnarkManager.STATE_ERROR) { + dm.setTorrentSaveDir(sSavePath); + boolean found = dm.filesExist(true); + if (!found && dm.getTorrent() != null + && !dm.getTorrent().isSimpleTorrent()) { + String parentPath = fSavePath.getParent(); + if (parentPath != null) { + dm.setTorrentSaveDir(parentPath); + found = dm.filesExist(true); + if (!found) { + dm.setTorrentSaveDir(sSavePath); + } + } + } + if (found) { + dm.stopIt(SnarkManager.STATE_STOPPED, false, false); + dm.setStateQueued(); + } + } + } + } + } +****/ + + /* + string | value type + ---------------------------+------------------------------------------------- + "activeTorrentCount" | number + "downloadSpeed" | number + "pausedTorrentCount" | number + "torrentCount" | number + "uploadSpeed" | number + ---------------------------+-------------------------------+ + "cumulative-stats" | object, containing: | + +------------------+------------+ + | uploadedBytes | number | tr_session_stats + | downloadedBytes | number | tr_session_stats + | filesAdded | number | tr_session_stats + | sessionCount | number | tr_session_stats + | secondsActive | number | tr_session_stats + ---------------------------+-------------------------------+ + "current-stats" | object, containing: | + +------------------+------------+ + | uploadedBytes | number | tr_session_stats + | downloadedBytes | number | tr_session_stats + | filesAdded | number | tr_session_stats + | sessionCount | number | tr_session_stats + | secondsActive | number | tr_session_stats + */ + private void + method_Session_Stats( + Map args, + Map result) + { + List fields = (List) args.get("fields"); + boolean all = fields == null || fields.size() == 0; + if (!all) { + // sort so we can't use Collections.binarySearch + Collections.sort(fields); + } + Collection snarks = _manager.getTorrents(); + long ul = 0, dl = 0, ur = 0, dr = 0; + int act = 0, pau = 0, tot = snarks.size(); + if (_util.connected() && tot != 0) { + for (Snark snark : snarks) { + dl += snark.getDownloaded(); + ul += snark.getUploaded(); + boolean isRunning = !snark.isStopped(); + if (isRunning) { + long d = snark.getDownloadRate(); + long u = snark.getUploadRate(); + dr += d; + ur += u; + act++; + if (d > 0 || u > 0) + pau++; + } + } + } + float ratio; + if (dl == 0) { + ratio = (ul == 0 ? 1 : Float.MAX_VALUE); + } else { + ratio = ((float) ul) / dl; + } + long secondsActive = _util.getStartedTime(); + if (secondsActive != 0) + secondsActive = Math.max((_context.clock().now() - secondsActive) / 1000L, 0L); + // < RPC v4 + if (all + || Collections.binarySearch(fields, + TR_SESSION_STATS_ACTIVE_TORRENT_COUNT) >= 0) { + result.put(TR_SESSION_STATS_ACTIVE_TORRENT_COUNT, act); + } + if (all + || Collections.binarySearch(fields, TR_SESSION_STATS_DOWNLOAD_SPEED) >= 0) { + result.put(TR_SESSION_STATS_DOWNLOAD_SPEED, dr); + } + if (all + || Collections.binarySearch(fields, + TR_SESSION_STATS_PAUSED_TORRENT_COUNT) >= 0) { + result.put(TR_SESSION_STATS_PAUSED_TORRENT_COUNT, pau); + } + if (all + || Collections.binarySearch(fields, TR_SESSION_STATS_TORRENT_COUNT) >= 0) { + // XXX: This is size with low-noise torrents, which aren't normally shown + result.put(TR_SESSION_STATS_TORRENT_COUNT, tot); + } + if (all + || Collections.binarySearch(fields, TR_SESSION_STATS_UPLOAD_SPEED) >= 0) { + result.put(TR_SESSION_STATS_UPLOAD_SPEED, ur); + } + // RPC v4 + if (all || Collections.binarySearch(fields, TR_SESSION_STATS_CURRENT) >= 0) { + Map current_stats = new HashMap(); + result.put(TR_SESSION_STATS_CURRENT, current_stats); + current_stats.put("uploadedBytes", ul); + current_stats.put("downloadedBytes", dl); + current_stats.put("ratio", ratio); + current_stats.put("secondsActive", secondsActive); + current_stats.put("sessionCount", Integer.valueOf(1)); // not tracked + } + if (all + || Collections.binarySearch(fields, TR_SESSION_STATS_CUMULATIVE) >= 0) { + // RPC v4 + Map cumulative_stats = new HashMap(); + result.put("cumulative-stats", cumulative_stats); + // TODO: ALL! + cumulative_stats.put("uploadedBytes", ul); + cumulative_stats.put("downloadedBytes", dl); + cumulative_stats.put("ratio", ratio); + cumulative_stats.put("secondsActive", secondsActive); + cumulative_stats.put("sessionCount", Integer.valueOf(1)); // not tracked + } + } + +/**** + private void + method_Torrent_Set( + String session_id, + Map args, + Map result) + { + Object ids = args.get( "ids" ); + handleRecentlyRemoved( session_id, args, result ); + List downloads = getDownloads( ids, false ); + // RPC v5 + // Not used: Number bandwidthPriority = getNumber("bandwidthPriority", null); + Number speed_limit_down = getNumber( + args.get("downloadLimit"), + getNumber(args.get("speed-limit-down"), + getNumber(args.get("speedLimitDownload")))); + Boolean downloadLimited = getBoolean("downloadLimited", null); + List files_wanted = (List)args.get( "files-wanted" ); + List files_unwanted = (List)args.get( "files-unwanted" ); + // RPC v5 + // true if session upload limits are honored + // Not Used: Boolean honorsSessionLimits = getBoolean("honorsSessionLimits", null); + // "location" | string new location of the torrent's content + String location = (String) args.get("location"); + // Not Implemented: By default, Vuze automatically adjusts mac connections per torrent based on bandwidth and seeding state + // "peer-limit" | number maximum number of peers + List priority_high = (List)args.get( "priority-high" ); + List priority_low = (List)args.get( "priority-low" ); + List priority_normal = (List)args.get( "priority-normal" ); + List file_infos = (List)args.get( "files" ); + // RPC v14 + // "queuePosition" | number position of this torrent in its queue [0...n) + Number queuePosition = getNumber("queuePosition", null); + // RPC v10 + // "seedIdleLimit" | number torrent-level number of minutes of seeding inactivity + // RPC v10: Not used, always TR_IDLELIMIT_GLOBAL + // "seedIdleMode" | number which seeding inactivity to use. See tr_inactvelimit (OR tr_idlelimit and TR_IDLELIMIT_*) + // RPC v5: Not Supported + // "seedRatioLimit" | double torrent-level seeding ratio + // RPC v5: Not Supported + // "seedRatioMode" | number which ratio to use. See tr_ratiolimit + // RPC v10 + // "trackerAdd" | array strings of announce URLs to add + List trackerAddList = (List) args.get("trackerAdd"); + // RPC v10: TODO + // "trackerRemove" | array ids of trackers to remove + // List trackerRemoveList = (List) args.get("trackerRemove"); + // RPC v10: TODO + // "trackerReplace" | array pairs of + // "uploadLimit" | number maximum upload speed (KBps) + Number speed_limit_up = getNumber( + args.get("uploadLimit"), + getNumber(args.get("speed-limit-up"), + getNumber(args.get("speedLimitUpload")))); + // "uploadLimited" | boolean true if "uploadLimit" is honored + Boolean uploadLimited = getBoolean("uploadLimited", null); + // RPC Vuze + // "tagAdd" | array array of tags to add to torrent + List tagAddList = (List) args.get("tagAdd"); + List tagRemoveList = (List) args.get("tagRemove"); + Long l_uploaded_ever = (Long)args.get( "uploadedEver" ); + Long l_downloaded_ever = (Long)args.get( "downloadedEver" ); + long uploaded_ever = l_uploaded_ever==null?-1:l_uploaded_ever.longValue(); + long downloaded_ever = l_downloaded_ever==null?-1:l_downloaded_ever.longValue(); + String name = (String) args.get("name"); + for ( Snark download_stub: downloads ) { + try { + Snark download = destubbify( download_stub ); + MetaInfo t = download.getMetaInfo(); + if ( t == null ) { + continue; + } + if (location != null) { + File file = new File(location); + if (!file.isFile()) { + try { + download.moveDataFiles(file); + } catch (DownloadException e) { + Debug.out(e); + } + } + } + if (name != null) { + SnarkManager core_download = PluginCoreUtils.unwrap(download); + core_download.getDownloadState().setDisplayName(name); + } + if (queuePosition != null) { + download.moveTo(queuePosition.intValue()); + } + if (trackerAddList != null) { + for (Object oTracker : trackerAddList) { + if (oTracker instanceof String) { + String aTracker = (String) oTracker; + TorrentUtils.announceGroupsInsertFirst(PluginCoreUtils.unwrap(t), aTracker); + } + } + } + if ( speed_limit_down != null && Boolean.TRUE.equals(downloadLimited) ) { + download.setDownloadRateLimitBytesPerSecond( speed_limit_down.intValue()); + } else if (Boolean.FALSE.equals(downloadLimited)) { + download.setDownloadRateLimitBytesPerSecond(0); + } + if ( speed_limit_up != null && Boolean.TRUE.equals(uploadLimited) ) { + download.setUploadRateLimitBytesPerSecond( speed_limit_up.intValue()); + } else if (Boolean.FALSE.equals(uploadLimited)) { + download.setUploadRateLimitBytesPerSecond(0); + } + if (tagAddList != null) { + TagManager tm = TagManagerFactory.getTagManager(); + if (tm.isEnabled()) { + TagType tt = tm.getTagType(TagType.TT_DOWNLOAD_MANUAL); + for (Object oTagToAdd : tagAddList) { + if (oTagToAdd != null) { + addTagToDownload(download, oTagToAdd, tt); + } + } + } + } + if (tagRemoveList != null) { + TagManager tm = TagManagerFactory.getTagManager(); + if (tm.isEnabled()) { + TagType ttManual = tm.getTagType(TagType.TT_DOWNLOAD_MANUAL); + TagType ttCategory = tm.getTagType(TagType.TT_DOWNLOAD_CATEGORY); + for (Object oTagToAdd : tagRemoveList) { + if (oTagToAdd instanceof String) { + Tag tag = ttManual.getTag((String) oTagToAdd, true); + if (tag != null) { + tag.removeTaggable(PluginCoreUtils.unwrap(download)); + } + tag = ttCategory.getTag((String) oTagToAdd, true); + if (tag != null) { + tag.removeTaggable(PluginCoreUtils.unwrap(download)); + } + } else if (oTagToAdd instanceof Number) { + int uid = ((Number) oTagToAdd).intValue(); + Tag tag = ttManual.getTag(uid); + if (tag != null) { + tag.removeTaggable(PluginCoreUtils.unwrap(download)); + } + tag = ttCategory.getTag(uid); + if (tag != null) { + tag.removeTaggable(PluginCoreUtils.unwrap(download)); + } + } + } + } + } + DiskManagerFileInfo[] files = download.getDiskManagerFileInfo(); + if ( files_unwanted != null ) { + for ( int i=0;i= 0 && index <= files.length ) { + files[index].setSkipped( true ); + } + } + } + if ( files_wanted != null ) { + for ( int i=0;i= 0 && index <= files.length ) { + files[index].setSkipped( false ); + } + } + } + if ( priority_high != null ) { + for ( int i=0;i= 0 && index <= files.length ) { + files[index].setNumericPriority( DiskManagerFileInfo.PRIORITY_HIGH ); + } + } + } + if ( priority_normal != null ) { + for ( int i=0;i= 0 && index <= files.length ) { + files[index].setNumericPriority( DiskManagerFileInfo.PRIORITY_NORMAL ); + } + } + } + if ( priority_low != null ) { + for ( int i=0;i= 0 && index <= files.length ) { + files[index].setNumericPriority( DiskManagerFileInfo.PRIORITY_LOW ); + } + } + } + if ( uploaded_ever != -1 || downloaded_ever != -1 ) { + // new method in 4511 B31 + try { + download.getStats().resetUploadedDownloaded( uploaded_ever, downloaded_ever ); + } catch( Throwable e ) { + } + } + if ( file_infos != null ) { + boolean paused_it = false; + try { + for ( int i=0;i= files.length ) { + throw( new IOException( "File index '" + index + "' invalid for '" + download.getName()+ "'" )); + } + //String path = (String)file_info.get( "path" ); don't support changing this yet + String new_name = (String)file_info.get( "name" ); // terminal name of the file (NOT the whole relative path+name) + if ( new_name == null || new_name.trim().length() == 0 ) { + throw( new IOException( "'name' is mandatory")); + } + new_name = new_name.trim(); + DiskManagerFileInfo file = files[index]; + File existing = file.getFile( true ); + if ( existing.getName().equals( new_name )) { + continue; + } + if ( !download.isPaused()) { + download.pause(); + paused_it = true; + } + File new_file = new File( existing.getParentFile(), new_name ); + if ( new_file.exists()) { + throw( new IOException( "new file '" + new_file + "' already exists" )); + } + file.setLink( new_file ); + } + } finally { + if ( paused_it ) { + download.resume(); + } + } + } + } catch( Throwable e ) { + Debug.out( e ); + } + } + } + + private void addTagToDownload(Snark download, Object tagToAdd, TagType tt) { + Tag tag = null; + if (tagToAdd instanceof String) { + String tagNameToAdd = (String) tagToAdd; + tagToAdd = tagNameToAdd.trim(); + if (tagNameToAdd.length() == 0) { + return; + } + tag = tt.getTag(tagNameToAdd, true); + if (tag == null) { + try { + tag = tt.createTag(tagNameToAdd, true); + } catch (Throwable e) { + Debug.out(e); + } + } + } else if (tagToAdd instanceof Number) { + tag = tt.getTag(((Number) tagToAdd).intValue()); + } + if (tag != null) { + tag.addTaggable(PluginCoreUtils.unwrap(download)); + } + } + + private void + method_Torrent_Reannounce( + Map args, + Map result) + throws IOException + { + checkUpdatePermissions(); + Object ids = args.get( "ids" ); + List downloads = getDownloads( ids, false ); + for ( Snark download_stub: downloads ) { + try { + destubbify( download_stub ).requestTrackerAnnounce(); + } catch( Throwable e ) { + Debug.out( "Failed to reannounce '" + download_stub.getName() + "'", e ); + } + } + } +****/ + + /* + Request arguments: + string | value type & description + ---------------------------+------------------------------------------------- + "ids" | array torrent list, as described in 3.1 + "delete-local-data" | boolean delete local data. (default: false) + Response arguments: none + */ + private void + method_Torrent_Remove( + Map args, + Map result) + throws IOException + { + checkUpdatePermissions(); + Object ids = args.get( "ids" ); + boolean delete_data = getBoolean(args.get( "delete-local-data" )); + List downloads = getDownloads( ids, true ); + for ( Snark download: downloads ) { + try { + MetaInfo t = download.getMetaInfo(); + if (t == null) { + // magnet - remove and delete are the same thing + _manager.deleteMagnet(download); + addRecentlyRemoved( download ); + continue; + } + _manager.stopTorrent(download, true); + // torrent file + File f = new File(download.getName()); + f.delete(); + if (delete_data) { + Storage storage = download.getStorage(); + if (storage == null) + continue; + // we have no error indication for any of this + List> files = t.getFiles(); + if (files == null) { // single file torrent + for (File df : storage.getFiles()) { + // should be only one + df.delete(); + } + continue; + } + // step 1 delete files + for (File df : storage.getFiles()) { + df.delete(); + } + // step 2 delete dirs bottom-up + Set dirs = storage.getDirectories(); + if (dirs == null) + continue; // directory deleted out from under us + for (File df : dirs) { + df.delete(); + } + } + } catch( Throwable e ) { + log( "Failed to remove download '" + download.getName() + "'", e ); + } + } + } + + private void + method_Torrent_Verify( + Map args, + Map result) + throws IOException + { + checkUpdatePermissions(); + Object ids = args.get( "ids" ); + List downloads = getDownloads( ids, false ); + for ( Snark download: downloads ) { + try { + if ( !download.isStopped() ) { + _manager.stopTorrent(download, false); + } + _manager.recheckTorrent(download); + } catch( Throwable e ) { + } + } + } + + private void + method_Torrent_Stop( + Map args, + Map result) + throws IOException + { + checkUpdatePermissions(); + Object ids = args.get( "ids" ); + List downloads = getDownloads( ids, false ); + for ( Snark download: downloads ) { + try { + if ( !download.isStopped() ) { + _manager.stopTorrent(download, false); + } + } catch( Throwable e ) { + } + } + } + + private void + method_Torrent_Start( + Map args, + Map result) + throws IOException + { + checkUpdatePermissions(); + Object ids = args.get( "ids" ); + List downloads = getDownloads( ids, false ); + for ( Snark download: downloads ) { + try { + if ( download.isStopped() ) { + _manager.startTorrent(download); + } + } catch( Throwable e ) { + } + } + } + + private void + method_Torrent_Start_Now( + Map args, + Map result) + throws IOException + { + method_Torrent_Start(args, result); + } + +/* + private void + processVuzeFileAdd( + final Map args, + Map result ) + throws IOException, TextualException + { + checkUpdatePermissions(); + VuzeFileHandler vfh = VuzeFileHandler.getSingleton(); + VuzeFile vf = null; + String url = (String) args.get( "filename" ); + Throwable last_error = null; + if ( url != null ) { + try { + File f = new File( new URI( url )); + if ( f.exists()) { + vf = vfh.loadVuzeFile( f ); + if ( vf == null ) { + throw( new TextualException( "Decode failed - invalid Vuze file" )); + } + } + } catch( Throwable e ) { + last_error = e; + } + if ( vf == null && last_error == null ) { + try { + vf = vfh.loadVuzeFile( new ResourceDownloaderFactoryImpl().create( new URL( url )).download()); + } catch( Throwable e ) { + last_error = e; + } + } + } + if ( vf == null && last_error == null ) { + try { + String metainfoString = (String) args.get("metainfo"); + byte[] metainfoBytes = null; + if ( metainfoString != null ) { + metainfoBytes = Base64.decode( metainfoString.replaceAll("[\r\n]+", "") ); + vf = vfh.loadVuzeFile( metainfoBytes ); + if ( vf == null ) { + throw( new TextualException( "Decode failed - invalid Vuze file" )); + } + } else { + throw( new TextualException( "Missing parameter" )); + } + } catch( Throwable e ) { + last_error = e; + } + } + if ( vf != null ) { + VuzeFileComponent[] comps = vf.getComponents(); + for ( VuzeFileComponent comp: comps ) { + if ( comp.getType() != VuzeFileComponent.COMP_TYPE_METASEARCH_TEMPLATE ) { + throw( new TextualException( "Unsupported Vuze File component type: " + comp.getTypeName())); + } + } + vfh.handleFiles( new VuzeFile[]{ vf }, VuzeFileComponent.COMP_TYPE_METASEARCH_TEMPLATE ); + String added_templates = ""; + for ( VuzeFileComponent comp: comps ) { + if ( comp.isProcessed()) { + Engine e = (Engine)comp.getData( Engine.VUZE_FILE_COMPONENT_ENGINE_KEY ); + if ( e != null ) { + added_templates += (added_templates==""?"":", ") + e.getName(); + } + } + } + result.put( "msg", "Search templates added: " + added_templates ); + } else { + if ( last_error == null ) { + throw( new TextualException( "Unspecified error occurred" )); + } else { + if ( last_error instanceof TextualException ) { + throw((TextualException)last_error); + } else { + throw( new TextualException( "Vuze file addition failed: " + Debug.getNestedExceptionMessage( last_error ))); + } + } + } + } +****/ + + /* + Request arguments: + key | value type & description + ---------------------+------------------------------------------------- + "cookies" | string pointer to a string of one or more cookies. + "download-dir" | string path to download the torrent to + "filename" | string filename or URL of the .torrent file + "metainfo" | string base64-encoded .torrent content + "paused" | boolean if true, don't start the torrent + "peer-limit" | number maximum number of peers + "bandwidthPriority" | number torrent's bandwidth tr_priority_t + "files-wanted" | array indices of file(s) to download + "files-unwanted" | array indices of file(s) to not download + "priority-high" | array indices of high-priority file(s) + "priority-low" | array indices of low-priority file(s) + "priority-normal" | array indices of normal-priority file(s) + Either "filename" OR "metainfo" MUST be included. + All other arguments are optional. + additional vuze specific parameters + "vuze_category" | string (optional category name) + "vuze_tags" | array (optional list of tags) + "name" | string (optional friendly name to use instead of url/hash) + The format of the "cookies" should be NAME=CONTENTS, where NAME is the + cookie name and CONTENTS is what the cookie should contain. + Set multiple cookies like this: "name1=content1; name2=content2;" etc. + + Response arguments: on success, a "torrent-added" object in the + form of one of 3.3's tr_info objects with the + fields for id, name, and hashString. + */ +/**** + private void + method_Torrent_Add( + final Map args, + Map result, + boolean xmlEscape) + throws IOException, DownloadException, TextualException + { + checkUpdatePermissions(); + String metainfoString = (String) args.get("metainfo"); + byte[] metainfoBytes = null; + if ( metainfoString != null ) { + metainfoBytes = Base64.decode( metainfoString.replaceAll("[\r\n]+", "") ); + //metainfoBytes = Base64.decode( metainfoString ); + VuzeFileHandler vfh = VuzeFileHandler.getSingleton(); + if ( vfh != null ) { + VuzeFile vf = vfh.loadVuzeFile( metainfoBytes ); + if ( vf != null ) { + VuzeFileComponent[] comps = vf.getComponents(); + for ( VuzeFileComponent comp: comps ) { + if ( comp.getType() != VuzeFileComponent.COMP_TYPE_METASEARCH_TEMPLATE ) { + throw( new TextualException( "Unsupported Vuze File component type: " + comp.getTypeName())); + } + } + vfh.handleFiles( new VuzeFile[]{ vf }, VuzeFileComponent.COMP_TYPE_METASEARCH_TEMPLATE ); + String added_templates = ""; + for ( VuzeFileComponent comp: comps ) { + if ( comp.isProcessed()) { + Engine e = (Engine)comp.getData( Engine.VUZE_FILE_COMPONENT_ENGINE_KEY ); + if ( e != null ) { + added_templates += (added_templates==""?"":", ") + e.getName(); + } + } + } + if ( added_templates.length() == 0 ) { + throw( new TextualException( "No search template(s) added" )); + } else { + throw( new TextualException( "Installed search template(s): " + added_templates )); + } + } + } + } + Snark torrent = null; + Snark download = null; + String url = (String) args.get("filename"); + final boolean add_stopped = getBoolean(args.get("paused")); + String download_dir = (String) args.get("download-dir"); + final File file_Download_dir = download_dir == null ? null : new File(download_dir); + // peer-limit not used + //getNumber(args.get("peer-limit"), 0); + // bandwidthPriority not used + //getNumber(args.get("bandwidthPriority"), TR_PRI_NORMAL); + final DownloadWillBeAddedListener add_listener = + new DownloadWillBeAddedListener() { + public void initialised(Snark download) { + int numFiles = download.getDiskManagerFileCount(); + List files_wanted = getList(args.get("files-wanted")); + List files_unwanted = getList(args.get("files-unwanted")); + boolean[] toDelete = new boolean[numFiles]; // all false + int numWanted = files_wanted.size(); + if (numWanted != 0 && numWanted != numFiles) { + // some wanted -- so, set all toDelete and reset ones in list + Arrays.fill(toDelete, true); + for (Object oWanted : files_wanted) { + int idx = getNumber(oWanted, -1).intValue(); + if (idx >= 0 && idx < numFiles) { + toDelete[idx] = false; + } + } + } + for (Object oUnwanted : files_unwanted) { + int idx = getNumber(oUnwanted, -1).intValue(); + if (idx >= 0 && idx < numFiles) { + toDelete[idx] = true; + } + } + for (int i = 0; i < toDelete.length; i++) { + if (toDelete[i]) { + download.getDiskManagerFileInfo(i).setDeleted(true); + } + } + List priority_high = getList(args.get("priority-high")); + for (Object oHighPriority : priority_high) { + int idx = getNumber(oHighPriority, -1).intValue(); + if (idx >= 0 && idx < numFiles) { + download.getDiskManagerFileInfo(idx).setNumericPriority( + DiskManagerFileInfo.PRIORITY_HIGH); + } + } + List priority_low = getList(args.get("priority-low")); + for (Object oLowPriority : priority_low) { + int idx = getNumber(oLowPriority, -1).intValue(); + if (idx >= 0 && idx < numFiles) { + download.getDiskManagerFileInfo(idx).setNumericPriority( + DiskManagerFileInfo.PRIORITY_LOW); + } + } + // don't need priority-normal if they are normal by default. + // handle initial categories/tags + try { + String vuze_category = (String)args.get( "vuze_category" ); + if ( vuze_category != null ) { + vuze_category = vuze_category.trim(); + if ( vuze_category.length() > 0 ) { + TorrentAttribute ta_category = plugin_interface.getTorrentManager().getAttribute(TorrentAttribute.TA_CATEGORY); + download.setAttribute( ta_category, vuze_category ); + } + } + List vuze_tags = (List)args.get( "vuze_tags" ); + if ( vuze_tags != null ) { + TagManager tm = TagManagerFactory.getTagManager(); + if ( tm.isEnabled()) { + TagType tt = tm.getTagType( TagType.TT_DOWNLOAD_MANUAL ); + for ( String tag_name: vuze_tags ) { + addTagToDownload(download, tag_name, tt); + } + } + } + } catch( Throwable e ) { + e.printStackTrace(); + } + } + }; + TorrentManager torrentManager = plugin_interface.getTorrentManager(); + boolean duplicate = false; + if ( metainfoBytes != null ) { + try { + torrent = torrentManager.createFromBEncodedData( metainfoBytes); + SnarkManager dm = _manager; + download = dm.getDownload( torrent ); + duplicate = download != null; + } catch (Throwable e) { + e.printStackTrace(); + //System.err.println("decode of " + new String(Base64.encode(metainfoBytes), "UTF8")); + throw (new IOException("torrent download failed: " + + Debug.getNestedExceptionMessage(e))); + } + } else if (url == null) { + throw (new IOException("url missing")); + } else { + url = url.trim().replaceAll(" ", "%20"); + // hack due to core bug - have to add a bogus arg onto magnet uris else they fail to parse + String lc_url = url.toLowerCase( Locale.US ); + if ( lc_url.startsWith("magnet:")) { + url += "&dummy_param=1"; + } else if (!lc_url.startsWith("http")) { + url = UrlUtils.parseTextForURL(url, true, true); + } + byte[] hashFromMagnetURI = getHashFromMagnetURI(url); + if (hashFromMagnetURI != null) { + org.gudy.azureus2.plugins.download.SnarkManager dm = _manager; + download = dm.getDownload(hashFromMagnetURI); + duplicate = download != null; + } + if (download == null) { + URL torrent_url; + try { + torrent_url = new URL(url); + } catch (MalformedURLException mue) { + throw new TextualException("The torrent URI was not valid"); + } + try { + final TorrentDownloader dl = torrentManager.getURLDownloader(torrent_url, null, null); + Object cookies = args.get("cookies"); + if ( cookies != null ) { + dl.setRequestProperty("URL_Cookie", cookies); + } + boolean is_magnet = torrent_url.getProtocol().equalsIgnoreCase( "magnet" ); + if ( is_magnet ) { + TimerEvent magnet_event = null; + final Object[] f_result = { null }; + try { + final AESemaphore sem = new AESemaphore( "magnetsem" ); + final URL f_torrent_url = torrent_url; + final String f_name = (String) args.get("name"); + magnet_event = SimpleTimer.addEvent( + "magnetcheck", + _context.clock().getOffsetTime( 10*1000 ), + new TimerEventPerformer() + { + public void + perform( + TimerEvent event ) + { + synchronized( f_result ) { + if ( f_result[0] != null ) { + return; + } + MagnetSnark magnet_download = new MagnetDownload( f_torrent_url, f_name ); + byte[] hash = magnet_download.getInfoHash(); + synchronized( magnet_downloads ) { + boolean duplicate = false; + Iterator it = magnet_downloads.iterator(); + while( it.hasNext()) { + MagnetSnark md = it.next(); + if ( hash.length > 0 && Arrays.equals( hash, md.getInfoHash())) { + if ( md.getError() == null ) { + duplicate = true; + magnet_download = md; + break; + } else { + it.remove(); + addRecentlyRemoved( md ); + } + } + } + if ( !duplicate ) { + magnet_downloads.add( magnet_download ); + } + } + f_result[0] = magnet_download; + } + sem.release(); + } + }); + new AEThread2( "magnetasync" ) + { + public void + run() + { + try { + MetaInfo torrent = dl.download("UTF-8"); + synchronized( f_result ) { + if ( f_result[0] == null ) { + f_result[0] = torrent; + } else { + MagnetSnark md = (MagnetDownload)f_result[0]; + boolean already_removed; + synchronized( magnet_downloads ) { + already_removed = !magnet_downloads.remove( md ); + } + if ( !already_removed ) { + addRecentlyRemoved( md ); + addTorrent( torrent, file_Download_dir, add_stopped, add_listener ); + } + } + } + } catch( Throwable e ) { + synchronized( f_result ) { + if ( f_result[0] == null ) { + f_result[0] = e; + } else { + MagnetSnark md = (MagnetDownload)f_result[0]; + md.setError( e ); + } + } + } finally { + sem.release(); + } + } + }.start(); + sem.reserve(); + Object res; + synchronized( f_result ) { + res = f_result[0]; + } + if ( res instanceof Snark ) { + torrent = (Torrent)res; + } else if ( res instanceof Throwable ) { + throw((Throwable)res); + } else { + download = (MagnetDownload)res; + torrent = null; + } + } finally { + if ( magnet_event != null ) { + magnet_event.cancel(); + } + } + } else { + torrent = dl.download("UTF-8"); + } + } catch( Throwable e ) { + e.printStackTrace(); + throw( new IOException( Debug.getNestedExceptionMessage( e ))); + } + } + } + if ( download == null ) { + download = addTorrent( torrent, file_Download_dir, add_stopped, add_listener ); + } + Map torrent_details = new HashMap(); + torrent_details.put("id", new Long(getID(download, true))); + torrent_details.put("name", xmlEscape ? escapeXML(download.getName()) : download.getName()); + torrent_details.put(FIELD_TORRENT_HASH, + I2PSnarkUtil.toHex(download.getInfoHash())); + result.put(duplicate ? "torrent-duplicate" : "torrent-added", torrent_details); + } + + private static byte[] getHashFromMagnetURI(String magnetURI) { + Pattern patXT = Pattern.compile("xt=urn:(?:btih|sha1):([^&]+)"); + Matcher matcher = patXT.matcher(magnetURI); + if (matcher.find()) { + return UrlUtils.decodeSHA1Hash(matcher.group(1)); + } + return null; + } +****/ + + private Map + method_Torrent_Get( + HttpServletRequest request, + String session_id, + Map args, + Map result) + { + // When "file_indexes" key is present, returns: + // NOTE: Array position does not equal file index! Use "index" key! + // { + // torrents : [ + // { + // : , + // files : + // [ + // { + // "index": , + // : + // }, + // + // ] + // }, + // : + // ] + // } + List fields = (List)args.get( "fields" ); + if ( fields == null ) { + fields = Collections.emptyList(); + } + Object ids = args.get( "ids" ); + boolean is_recently_active = handleRecentlyRemoved( session_id, args, result ); + List downloads = getDownloads( ids, true ); + List file_fields = (List) args.get("file-fields"); + if (file_fields != null) { + Collections.sort(file_fields); + } + Map torrent_info = new LinkedHashMap(); + String agent = request.getHeader("User-Agent"); + boolean xmlEscape = agent != null && agent.startsWith("Mozilla/"); + log ("torrent-get for torrents: " + downloads.size() + " and fields: " + fields.size()); + for ( Snark download_stub: downloads ) { + method_Torrent_Get_NonStub(request, args, fields, torrent_info, + download_stub, file_fields, xmlEscape); + } // for downloads + if ( is_recently_active ) { + // just return the latest diff for this session + // we could possibly, in theory, update the cache for all calls to this method, not just the 'recently active' calls + // but I don't trust the client enough atm to behave correctly + synchronized( session_torrent_info_cache ) { + if ( session_torrent_info_cache.size() > 8 ) { + session_torrent_info_cache.clear(); + } + Map torrent_info_cache = session_torrent_info_cache.get( session_id ); + if ( torrent_info_cache == null ) { + torrent_info_cache = new HashMap(); + session_torrent_info_cache.put( session_id, torrent_info_cache ); + } + List same = new ArrayList(); + for ( Map.Entry entry: torrent_info.entrySet()) { + long id = entry.getKey(); + Map torrent = entry.getValue(); + String current = JSONUtils.encodeToJSON( torrent ); + String prev = torrent_info_cache.get( id ); + if ( prev != null && prev.equals( current )) { + same.add( id ); + } else { + torrent_info_cache.put( id, current ); + } + } + if ( same.size() > 0 ) { + // System.out.println( "same info: " + same.size() + " of " + torrent_info.size()); + for ( long id: same ) { + torrent_info.remove( id ); + } + } + } + } + List torrents = new ArrayList(); + result.put( "torrents", torrents ); + torrents.addAll(torrent_info.values()); + return result; + } + + private void method_Torrent_Get_NonStub( + HttpServletRequest request, + Map args, + List fields, + Map torrent_info, + Snark download, + List file_fields, + boolean xmlEscape) + { + // may be null + MetaInfo t = download.getMetaInfo(); + // may be null + Storage storage = download.getStorage(); + long download_id = getID(download, true); + SnarkManager core_download = _manager; + Map torrent = new HashMap(fields.size() + 8); + torrent_info.put(download_id, torrent); + for (String field : fields) { + Object value = null; + if (field.equals("activityDate")) { + // RPC v0 + // activityDate | number | tr_stat + // TODO + if (download.isStopped()) + value = 0; + else + value = _context.clock().now() / 1000L; + } else if (field.equals("activityDateRelative")) { + // RPC v0 + // activityDate | number | tr_stat + //value = torrentGet_activityDate(core_download, true); + value = 0; + } else if (field.equals("addedDate")) { + // RPC v0 + // addedDate | number | tr_stat + // When the torrent was first added. + long[] vals = _manager.getSavedAddedAndCompleted(download); + value = vals[0] / 1000L; + } else if (field.equals("announceURL")) { + // Removed in RPC v7 + if (t != null) + value = t.getAnnounce(); + else + value = ""; + } else if (field.equals("bandwidthPriority")) { + // RPC v5: Not Supported + // bandwidthPriority | number | tr_priority_t + // torrent's bandwidth priority. + value = TR_PRI_NORMAL; + } else if (field.equals("comment")) { + // RPC v0 + // comment | string | tr_info + if (t != null) + value = t.getComment(); + else + value = ""; + } else if (field.equals("corruptEver")) { + // RPC v0 TODO: Do we want just hash fails? + // corruptEver | number | tr_stat + // Byte count of all the corrupt data you've ever downloaded for + // this torrent. If you're on a poisoned torrent, this number can + // grow very large. + //value = stats.getDiscarded() + stats.getHashFails(); + value = 0; + } else if (field.equals("creator")) { + // RPC v0 + // creator | string | tr_info + if (t != null) + value = t.getCreatedBy(); + else + value = ""; + } else if (field.equals("dateCreated")) { + // RPC v0 + // dateCreated | number | tr_info + if (t != null) + value = t.getCreationDate() / 1000L; + else + value = 0; + } else if (field.equals("desiredAvailable")) { + // RPC v0 TODO: stats.getRemainingAvailable() ? + // desiredAvailable | number | tr_stat + // Byte count of all the piece data we want and don't have yet, + // but that a connected peer does have. [0...leftUntilDone] + if (download.isStopped() || download.isChecking() || download.isAllocating()) { + value = 0; + } else if (storage != null && storage.complete()) { + value = 0; + } else if (torrentGet_isStalled(download)) { + value = 0; + } else { + long needed = download.getNeededLength(); + if (needed < 0) + needed = download.getRemainingLength(); + if (needed >= 0) + value = needed; // not really + else + value = 0; // TODO + } + } else if (field.equals("doneDate")) { + // RPC v0 + // doneDate | number | tr_stat + // When the torrent finished downloading. + if (storage != null && storage.complete()) { + long[] vals = _manager.getSavedAddedAndCompleted(download); + value = vals[1] / 1000L; + } else { + // TODO: Verify what value to send when not complete + value = 0; + } + } else if (field.equals("downloadDir")) { + // RPC v4 + // downloadDir | string | tr_torrent + if (storage != null) { + value = storage.getBase().getAbsolutePath(); + } + } else if (field.equals("downloadedEver")) { + // RPC v0 + // downloadedEver | number | tr_stat + /** + * Byte count of all the non-corrupt data you've ever downloaded + * for this torrent. If you deleted the files and downloaded a second + * time, this will be 2*totalSize.. + */ + value = download.getDownloaded(); + } else if (field.equals("downloadLimit") + || field.equals("speed-limit-down")) { + // RPC v5 (alternate is from 'set' prior to v5 -- added for rogue clients) + // downloadLimit | number | tr_torrent + // maximum download speed (KBps) + value = 99999; + } else if (field.equals("downloadLimited") + || field.equals("speed-limit-down-enabled")) { + // RPC v5 (alternate is from 'set' prior to v5 -- added for rogue clients) + // downloadLimited | boolean | tr_torrent + // true if "downloadLimit" is honored + value = false; + } else if (field.equals("error")) { + // RPC v0 + // error | number | tr_stat + // Defines what kind of text is in errorString. TR_STAT_* + String error = download.getTrackerProblems(); + value = (error != null) ? TR_STAT_TRACKER_ERROR : TR_STAT_OK; + } else if (field.equals("errorString")) { + // RPC v0 + // errorString | string | tr_stat + String error = download.getTrackerProblems(); + value = (error != null) ? error : ""; + } else if (field.equals("eta")) { + // RPC v0 + // eta | number | tr_stat + long total = download.getTotalLength(); + long remaining = download.getRemainingLength(); + if (remaining > total) + remaining = total; + // does not include skipped files, -1 for magnet mode or when not running. + long needed = download.getNeededLength(); + if (needed < 0) + needed = download.getRemainingLength(); + if (needed > total) + needed = total; + long remainingSeconds; + long downBps = download.getDownloadRate(); + if (needed == 0) + remainingSeconds = 0; + else if (downBps > 0 && needed > 0) + remainingSeconds = needed / downBps; + else + remainingSeconds = TR_ETA_UNKNOWN; + value = remainingSeconds; + } else if (field.equals("etaIdle")) { + // RPC v15 + // If seeding, number of seconds left until the idle time limit is reached. + // TODO: No idea what etaIdle description means! What happens at idle time? + value = TR_ETA_UNKNOWN; + } else if (field.equals("files")) { + // RPC v0 + String host = request.getHeader( "host" ); + value = torrentGet_files(host, download, download_id, file_fields, args); + // One hash for all files. This won't work when our file list is a partial + //if (value instanceof Collection) { + // torrent.put("files-hc", longHashSimpleList((Collection) value)); + //} + } else if (field.equals("fileStats")) { + // RPC v5 + value = torrentGet_fileStats(download, file_fields, args); + } else if (field.equals(FIELD_TORRENT_HASH)) { + // RPC v0 + // hashString | string | tr_info + value = I2PSnarkUtil.toHex(download.getInfoHash()); + } else if (field.equals("haveUnchecked")) { + // haveUnchecked | number | tr_stat + /** Byte count of all the partial piece data we have for this torrent. + As pieces become complete, this value may decrease as portions of it + are moved to `corrupt' or `haveValid'. */ + // TODO: set when ST_CHECKING? + value = 0; + } else if (field.equals("haveValid")) { + // haveValid | number | tr_stat + // Byte count of all the checksum-verified data we have for this torrent. + long total = download.getTotalLength(); + long remaining = download.getRemainingLength(); + if (remaining > total) + remaining = total; + value = total - remaining; + } else if (field.equals("honorsSessionLimits")) { + // TODO RPC v5 + // honorsSessionLimits | boolean | tr_torrent + // true if session upload limits are honored + value = Boolean.TRUE; + } else if (field.equals("id")) { + // id | number | tr_torrent + value = download_id; + } else if (field.equals("isFinished")) { + // RPC v9: TODO + // isFinished | boolean | tr_stat + // A torrent is considered finished if it has met its seed ratio. + // As a result, only paused torrents can be finished. + value = false; + } else if (field.equals("isPrivate")) { + // RPC v0 + // isPrivate | boolean | tr_torrent + value = t != null && t.isPrivate(); + } else if (field.equals("isStalled")) { + // RPC v14 + // isStalled | boolean | tr_stat + value = torrentGet_isStalled(download); + } else if (field.equals("leechers")) { + // Removed in RPC v7 + // all leechers // todo + value = (storage == null) ? 0 : storage.complete() ? download.getPeerCount() : download.getPeerCount() / 2; + } else if (field.equals("leftUntilDone")) { + // RPC v0 + // leftUntilDone | number | tr_stat + // Byte count of how much data is left to be downloaded until we've got + // all the pieces that we want. [0...tr_info.sizeWhenDone] + long needed = download.getNeededLength(); + if (needed < 0) + needed = download.getRemainingLength(); + if (needed >= 0) + value = needed; + else + value = 16384; // TODO + } else if (field.equals("magnetLink")) { + // TODO RPC v7 + // magnetLink | number | n/a + // NOTE: I assume spec is wrong and it's a string.. + // TODO plus announce url? + value = "magnet:?xt=urn:btih:" + I2PSnarkUtil.toHex(download.getInfoHash()); +/* + } else if (field.equals("manualAnnounceTime")) { + // manualAnnounceTime | number | tr_stat + // spec is time_t, although it should be relative time. :( + value = torrentGet_manualAnnounceTime(core_download); +*/ + } else if (field.equals("maxConnectedPeers")) { + // maxConnectedPeers | number | tr_torrent + // TODO: Some sort of Peer Limit (tr_torrentSetPeerLimit ) + // not really, but PeerCoordinator.getMaxConnections() is private + value = _util.getMaxConnections(); + } else if (field.equals("metadataPercentComplete")) { + // RPC v7: TODO + // metadataPercentComplete | double | tr_stat + /** + * How much of the metadata the torrent has. + * For torrents added from a .torrent this will always be 1. + * For magnet links, this number will from from 0 to 1 as the metadata is downloaded. + * Range is [0..1] + */ + // RPC v7 + value = 1.0f; + } else if (field.equals("name")) { + value = download.getBaseName(); + } else if (field.equals("peer-limit")) { + // peer-limit | number | tr_torrent + // TODO + // how many peers this torrent can connect to + value = 20; + } else if (field.equals("peers")) { + // RPC v2 + value = torrentGet_peers(download); + } else if (field.equals("peersConnected")) { + // peersConnected | number | tr_stat + // Number of peers that we're connected to + value = download.getPeerCount(); +/* + } else if (field.equals("peersFrom")) { + value = torrentGet_peersFrom(pm); +*/ + } else if (field.equals("peersGettingFromUs")) { + // peersGettingFromUs | number | tr_stat + if (download.isStopped()) { + value = 0; + } else { + List peers = download.getPeerList(); + int count = 0; + for (Peer peer : peers) { + if (!peer.isConnected()) + continue; + if (peer.isInterested() && peer.getUploadRate() > 0) + count++; + } + value = count; + } + } else if (field.equals("peersSendingToUs")) { + // peersSendingToUs | number | tr_stat + if (download.isStopped()) { + value = 0; + } else { + List peers = download.getPeerList(); + int count = 0; + for (Peer peer : peers) { + if (!peer.isConnected()) + continue; + if (peer.isInteresting() && peer.getDownloadRate() > 0) + count++; + } + value = count; + } +/* + } else if (field.equals("percentDone")) { + // RPC v5 + // percentDone | double | tr_stat + // How much has been downloaded of the files the user wants. This differs + // from percentComplete if the user wants only some of the torrent's files. + // Range is [0..1] + value = core_download.getStats().getPercentDoneExcludingDND() / 1000.0f; +*/ + } else if (field.equals("pieces")) { + // RPC v5 + value = torrentGet_pieces(download); + } else if (field.equals("pieceCount")) { + // pieceCount | number | tr_info + if (t != null) + value = t.getPieces(); + else + value = 1; + } else if (field.equals("pieceSize")) { + // pieceSize | number | tr_info + if (t != null) + value = t.getPieceLength(0); + else + value = 16384; + } else if (field.equals("priorities")) { + value = torrentGet_priorities(download); +/* + } else if (field.equals("queuePosition")) { + // RPC v14 + // "queuePosition" | number position of this torrent in its queue [0...n) + value = core_download.getPosition(); +*/ + } else if (field.equals("rateDownload")) { + // rateSnark (B/s) | number | tr_stat + value = download.getDownloadRate(); + } else if (field.equals("rateUpload")) { + // rateUpload (B/s) | number | tr_stat + value = download.getUploadRate(); + } else if (field.equals("recheckProgress")) { + // recheckProgress | double | tr_stat + //value = torrentGet_recheckProgress(core_download, stats); + value = 1.0d; + } else if (field.equals("secondsDownloading")) { + // secondsDownloading | number | tr_stat + // Cumulative seconds the torrent's ever spent downloading + //value = stats.getSecondsDownloading(); + value = 0; + } else if (field.equals("secondsSeeding")) { + // secondsSeeding | number | tr_stat + // Cumulative seconds the torrent's ever spent seeding + // TODO: Want "only seeding" time, or seeding time (including downloading time)? + //value = stats.getSecondsOnlySeeding(); + value = 0; + } else if (field.equals("seedIdleLimit")) { + // RPC v10 + // "seedIdleLimit" | number torrent-level number of minutes of seeding inactivity + //value = (int) stats.getSecondsSinceLastUpload() / 60; + value = 0; + } else if (field.equals("seedIdleMode")) { + // RPC v10: Not used, always TR_IDLELIMIT_GLOBAL + // "seedIdleMode" | number which seeding inactivity to use. See tr_inactvelimit + value = TR_IDLELIMIT_GLOBAL; + } else if (field.equals("seedRatioLimit")) { + // RPC v5 + // "seedRatioLimit" | double torrent-level seeding ratio + //value = COConfigurationManager.getFloatParameter("Stop Ratio"); + value = 100.0d; + } else if (field.equals("seedRatioMode")) { + // RPC v5: Not used, always Global + // seedRatioMode | number | tr_ratiolimit + value = TR_RATIOLIMIT_GLOBAL; + } else if (field.equals("sizeWhenDone")) { + // sizeWhenDone | number | tr_stat + /** + * Byte count of all the piece data we'll have downloaded when we're done, + * whether or not we have it yet. This may be less than tr_info.totalSize + * if only some of the torrent's files are wanted. + * [0...tr_info.totalSize] + **/ + value = download.getTotalLength() - download.getSkippedLength(); + } else if (field.equals("startDate")) { + // When the torrent was last started. + //value = stats.getTimeStarted() / 1000; + value = 0; + } else if (field.equals("status")) { + if (download.isStarting()) + value = TR_STATUS_DOWNLOAD_WAIT; + else if (download.isStopped()) + value = TR_STATUS_STOPPED; + else if (download.isChecking()) + value = TR_STATUS_CHECK; + else if (download.isAllocating()) + value = TR_STATUS_DOWNLOAD; + else if (download.getRemainingLength() <= 0) + value = TR_STATUS_SEED; + else + value = TR_STATUS_DOWNLOAD; + } else if (field.equals("trackers")) { + String agent = request.getHeader("User-Agent"); + boolean hack = agent != null && agent.contains("httpok"); // Torrnado + value = torrentGet_trackers(download, hack); + } else if (field.equals("trackerStats")) { + // RPC v7 + //value = torrentGet_trackerStats(core_download); + // just do the same as 'trackers' for now + String agent = request.getHeader("User-Agent"); + boolean hack = agent != null && agent.contains("httpok"); // Torrnado + value = torrentGet_trackers(download, hack); + } else if (field.equals("totalSize")) { + value = download.getTotalLength(); + } else if (field.equals("torrentFile")) { + // torrentFile | string | tr_info + // Path to torrent + value = download.getName(); + } else if (field.equals("uploadedEver")) { + // uploadedEver | number | tr_stat + value = download.getUploaded(); + } else if (field.equals("uploadLimit") || field.equals("speed-limit-up")) { + // RPC v5 (alternate is from 'set' prior to v5 -- added for rogue clients) + // maximum upload speed (KBps) + value = _util.getMaxUpBW(); + } else if (field.equals("uploadLimited") + || field.equals("speed-limit-up-enabled")) { + // RPC v5 (alternate is from 'set' prior to v5 -- added for rogue clients) + // true if "uploadLimit" is honored + //value = download.getUploadRateLimitBytesPerSecond() > 0; + value = true; + } else if (field.equals("uploadRatio")) { + // uploadRatio | double | tr_stat + long total = download.getTotalLength(); + if (total > 0) + value = download.getUploaded() / (double) total; + else + value = 0.0; + } else if (field.equals("wanted")) { + value = torrentGet_wanted(download); + } else if (field.equals("webseeds")) { + //value = torrentGet_webSeeds(t); + value = Collections.emptyList(); + } else if (field.equals("webseedsSendingToUs")) { + //value = torrentGet_webseedsSendingToUs(core_download); + value = 0; +/* + } else if (field.equals("trackerSeeds")) { + // Vuze Specific? + SnarkScrapeResult scrape = download.getLastScrapeResult(); + value = new Long(scrape == null ? 0 : scrape.getSeedCount()); + } else if (field.equals("trackerLeechers")) { + // Vuze Specific? + SnarkScrapeResult scrape = download.getLastScrapeResult(); + value = new Long(scrape == null ? 0 : scrape.getNonSeedCount()); + } else if (field.equals("speedLimitDownload")) { + // Vuze Specific? + value = new Long(download.getDownloadRateLimitBytesPerSecond()); + } else if (field.equals("speedLimitUpload")) { + // Vuze Specific? + value = new Long(download.getUploadRateLimitBytesPerSecond()); + } else if (field.equals("seeders")) { + // Removed in RPC v7 + value = pm == null ? -1 : pm.getNbSeeds(); + } else if (field.equals("swarmSpeed")) { + // Removed in RPC v7 + value = new Long(core_download.getStats().getTotalAveragePerPeer()); +*/ + } else if (field.equals("announceResponse")) { + // Removed in RPC v7 + String error = download.getTrackerProblems(); + if (error != null) { + value = error; + } else { + value = ""; + } + } else if (field.equals("lastScrapeTime")) { + // Unsure of wanted format + // Removed in v7 + //value = core_download.getTrackerTime(); + value = 0; +/* + } else if (field.equals("scrapeURL")) { + // Removed in v7 + value = ""; + TRTrackerScraperResponse trackerScrapeResponse = core_download.getTrackerScrapeResponse(); + if (trackerScrapeResponse != null) { + URL url = trackerScrapeResponse.getURL(); + if (url != null) { + value = url.toString(); + } + } + } else if (field.equals("nextScrapeTime")) { + // Removed in v7 + // Unsure of wanted format + TRTrackerAnnouncer trackerClient = core_download.getTrackerClient(); + if (trackerClient != null) { + value = trackerClient.getTimeUntilNextUpdate(); + } else { + value = 0; + } + } else if (field.equals("nextAnnounceTime")) { + // Removed in v7 + // Unsure of wanted format + TRTrackerAnnouncer trackerClient = core_download.getTrackerClient(); + if (trackerClient != null) { + value = trackerClient.getTimeUntilNextUpdate(); + } else { + value = 0; + } +*/ + } else if (field.equals("downloadLimitMode") + || field.equals("uploadLimitMode")) { + // RPC < v5 -- Not supported -- ignore + } else if (field.equals("downloaders") + || field.equals("lastAnnounceTime") || field.equals("lastScrapeTime") + || field.equals("scrapeResponse") || field.equals("timesCompleted")) { + // RPC < v7 -- Not Supported -- ignore + } else if (field.equals("peersKnown")) { + // RPC < v13 -- Not Supported -- ignore + } else if (field.equals("fileCount")) { + // azRPC + if (t != null) { + List lengths = t.getLengths(); + value = (lengths != null) ? lengths.size() : 1; + } else { + value = 1; + } +/* + } else if (field.equals("speedHistory")) { + // azRPC + SnarkManagerStats core_stats = core_download.getStats(); + core_stats.setRecentHistoryRetention(true); + // TODO + // [0] send [1] receive [2] swarm + int[][] recentHistory = core_stats.getRecentHistory(); + long now = _context.clock().now(); + long sinceSecs = getNumber(args.get("speedHistorySinceSecs"), 0).longValue(); + long since = now - (sinceSecs * 1000); + long curEntryTime = now - (recentHistory.length *1000); + List listHistory = new ArrayList(); + for (int i = 0; i < recentHistory.length; i++) { + if (curEntryTime > since) { + int[] entry = recentHistory[i]; + Map mapHistory = new HashMap(3); + mapHistory.put("upload", entry[0]); + mapHistory.put("download", entry[1]); + mapHistory.put("swarm", entry[2]); + listHistory.add(entry); + } + curEntryTime += 1000; + } + value = listHistory; +*/ + /* + * [ + * { + * upload: + * download:
+ * swarm: + * } + * } + */ +/* + } else if (field.equals("tag-uids")) { + // azRPC + List listTags = new ArrayList(); + TagManager tm = TagManagerFactory.getTagManager(); + List tags = tm.getTagsForTaggable(core_download); + if (tags == null || tags.isEmpty()) { + Category catAll = CategoryManager.getCategory(Category.TYPE_ALL); + if (catAll != null) { + listTags.add(catAll.getTagUID()); + } + Category catUncat = CategoryManager.getCategory(Category.TYPE_UNCATEGORIZED); + if (catUncat != null) { + listTags.add(catUncat.getTagUID()); + } + } else { + for (Tag tag : tags) { + listTags.add(tag.getTagUID()); + } + } + value = listTags; +*/ + } else { + if ( trace_param ) { + log("Unhandled get-torrent field: " + field); + } + } + if (value != null) { + if (xmlEscape && (value instanceof String)) { + value = escapeXML((String) value); + } + torrent.put(field, value); + } else { + log("No value for field: " + field); + } + } // for fields + } + + /* + tag_initialising = new MyTag( 0, "tag.type.ds.init", + tag_downloading = new MyTag( 1, "tag.type.ds.down", + tag_seeding = new MyTag( 2, "tag.type.ds.seed", + tag_queued_downloading = new MyTag( 3, "tag.type.ds.qford" + tag_queued_seeding = new MyTag( 4, "tag.type.ds.qfors", + tag_stopped = new MyTag( 5, "tag.type.ds.stop", + tag_error = new MyTag( 6, "tag.type.ds.err", + */ +/**** + private Tag getTagFromState(int state, boolean complete) { + int id = 0; + switch (state) { + case Snark.ST_DOWNLOADING: + id = 1; + break; + case Snark.ST_SEEDING: + id = 2; + break; + case Snark.ST_QUEUED: + id = complete ? 4 : 3; + break; + case Snark.ST_STOPPED: + case Snark.ST_STOPPING: + id = 5; + break; + case Snark.ST_ERROR: + id = 6; + break; + } + TagManager tm = TagManagerFactory.getTagManager(); + return tm.getTagType(TagType.TT_DOWNLOAD_STATE).getTag(id); + } +****/ + + /** Number of webseeds that are sending data to us. */ +/**** + private Object torrentGet_webseedsSendingToUs(SnarkManager core_download) { + PEPeerManager peerManager = core_download.getPeerManager(); + if (peerManager == null) { + return 0; + } + int numWebSeedsConnected = 0; + List peers = peerManager.getPeers(); + for (PEPeer peer : peers) { + if (peer.getProtocol().toLowerCase().startsWith( "http" )) { + numWebSeedsConnected++; + } + } + return numWebSeedsConnected; + } + + private Object torrentGet_webSeeds(Torrent t) { + // webseeds + // | an array of strings: | + // +-------------------------+------------+ + // | webseed | string | tr_info + List getright = BDecoder.decodeStrings(getURLList(t, "url-list")); + List webseeds = BDecoder.decodeStrings(getURLList(t, "httpseeds")); + List list = new ArrayList(); + for (List l : new List[] { + getright, + webseeds + }) { + for (Object o : l) { + if (o instanceof String) { + list.add(o); + } + } + } + return list; + } +****/ + + /** + * When tr_stat.activity is TR_STATUS_CHECK or TR_STATUS_CHECK_WAIT, + * this is the percentage of how much of the files has been + * verified. When it gets to 1, the verify process is done. + * Range is [0..1] + **/ +/**** + private Object torrentGet_recheckProgress(SnarkManager core_download, + SnarkStats stats) { + double x = 1; + if ( core_download.getState() == SnarkManager.STATE_CHECKING ) { + DiskManager dm = core_download.getDiskManager(); + if ( dm != null ) { + x = ((double)stats.getCompleted())/1000; + } + } + return x; + } +****/ + + private Object torrentGet_priorities(Snark download) { + // | an array of tr_info.filecount | tr_info + // | numbers. each is the tr_priority_t | + // | mode for the corresponding file. | + MetaInfo t = download.getMetaInfo(); + if (t == null) + return Collections.EMPTY_LIST; + int count = (t.getLengths() != null) ? t.getLengths().size() : 1; + Storage storage = download.getStorage(); + List list = new ArrayList(count); + int[] priorities = (storage != null) ? storage.getFilePriorities() : null; + for (int i = 0; i < count; i++) { + int priority = (priorities != null) ? priorities[i] : 0; + long newPriority = convertVuzePriority(priority); + list.add(newPriority); + } + return list; + } + + private Object torrentGet_pieces(Snark download) { + Object value = null; + // TODO: No idea if this works + // pieces | string + // | A bitfield holding pieceCount flags | tr_torrent + // | which are set to 'true' if we have | + // | the piece matching that position. | + // | JSON doesn't allow raw binary data, | + // | so this is a base64-encoded string. | + Storage storage = download.getStorage(); + if ( storage != null ) { + BitField pieces = storage.getBitField(); + // high bit to low bit + byte[] bits = pieces.getFieldBytes(); +/**** + // low bit to high bit + // this is what Vuze does, but it looks backwards according to transmission-remote + // We can't use pieces.getFieldBytes() because the bits in a byte are backwards + // are backwards from what we need here + byte[] bits = new byte[ (int) Math.ceil(size / 8.0f)]; + int size = pieces.size(); + int pieceNo = 0; + int bitPos = 0; + while (pieceNo < size) { + bits[bitPos] = 0; + for (int i = 0; pieceNo < size && i < 8; i++) { + boolean done = pieces.get(pieceNo); + if (done) { + bits[bitPos] |= (byte)(1 << i); + } + pieceNo++; + } + bitPos++; + } +****/ + value = Base64.encode(bits, true); + } + return value; + } + +/**** + private Object torrentGet_peersFrom(PEPeerManager pm) { + // peersFrom | an object containing: | + // +-------------------------+------------+ + // | fromCache | number | tr_stat + // | fromDht | number | tr_stat + // | fromIncoming | number | tr_stat + // | fromLpd | number | tr_stat + // | fromLtep | number | tr_stat + // | fromPex | number | tr_stat + // | fromTracker | number | tr_stat + Map mapPeersFrom = new HashMap(); + if (pm == null) { + return mapPeersFrom; + } + List peers = pm.getPeers(); + for ( PEPeer peer: peers ) { + String peerSource = peer.getPeerSource(); + if (peerSource != null) { + if (peerSource.equals(PEPeerSource.PS_BT_TRACKER)) { + peerSource = "fromTracker"; + } else if (peerSource.equals(PEPeerSource.PS_DHT)) { + peerSource = "fromDht"; + } else if (peerSource.equals(PEPeerSource.PS_INCOMING)) { + peerSource = "fromIncoming"; + } else if (peerSource.equals(PEPeerSource.PS_OTHER_PEER)) { + peerSource = "fromPex"; + } else if (peerSource.equals(PEPeerSource.PS_PLUGIN)) { + // TODO: better cat? + peerSource = "fromCache"; + } else { + peerSource = "fromCache"; + } // missing: from Ltep + if (!mapPeersFrom.containsKey(peerSource)) { + mapPeersFrom.put(peerSource, 1l); + } else { + mapPeersFrom.put(peerSource, mapPeersFrom.get(peerSource) + 1); + } + } + } + return mapPeersFrom; + } +****/ + + /** + * time when one or more of the torrent's trackers will + * allow you to manually ask for more peers, + * or 0 if you can't + */ +/**** + private Object torrentGet_manualAnnounceTime(SnarkManager manager) { + // See ScrapeInfoView's updateButton logic + Object value; + TRTrackerAnnouncer trackerClient = manager.getTrackerClient(); + if (trackerClient != null) { + value = Math.max(_context.clock().now() / 1000, + trackerClient.getLastUpdateTime() + TRTrackerAnnouncer.REFRESH_MINIMUM_SECS); + } else { + // Technically the spec says "ask for more peers" which suggests + // we don't need to handle scrape -- but let's do it anyway + TRTrackerScraperResponse sr = manager.getTrackerScrapeResponse(); + if ( sr == null ) { + value = 0; + } else { + value = Math.max(_context.clock().now() / 1000, + sr.getScrapeStartTime() / 1000 + TRTrackerScraper.REFRESH_MINIMUM_SECS); + } + } + return value; + } +****/ + + /** + * If downloading, estimated number of seconds left until the torrent is done. + * If seeding, estimated number of seconds left until seed ratio is reached. + */ +/**** + private Object torrentGet_eta(SnarkManager core_download, Snark download, SnarkStats stats ) { + Object value; + int state = download.getState(); + if (state == Snark.ST_DOWNLOADING) { + long eta_secs = core_download.getStats().getSmoothedETA(); + //long eta_secs = stats.getETASecs(); + if (eta_secs == -1) { + value = TR_ETA_NOT_AVAIL; + } else if (eta_secs >= 315360000000L) { + value = TR_ETA_UNKNOWN; + } else { + value = eta_secs; + } + } else if (state == Snark.ST_SEEDING) { + // TODO: secs left until SR met + value = TR_ETA_NOT_AVAIL; + } else { + value = TR_ETA_NOT_AVAIL; + } + return value; + } +****/ + + private Object torrentGet_trackers(Snark download, boolean hack) { + MetaInfo t = download.getMetaInfo(); + if (t == null) + return Collections.EMPTY_LIST; + List trackers = new ArrayList(); + //trackers | array of objects, each containing: | + //+-------------------------+------------+ + //| announce | string | tr_tracker_info + //| id | number | tr_tracker_info + //| scrape | string | tr_tracker_info + //| tier | number | tr_tracker_info + int tier = 0; + String name = t.getAnnounce(); + if (name == null) + name = download.getTrackerURL(); // from magnet URL + if (name != null) { + if (hack && !name.contains("://")) { + name = "://" + name; + } + Map map = new HashMap(); + map.put("id", name.hashCode()); + // the full announce URL + map.put("announce", name); + // the full scrape URL + map.put("scrape", ""); + // which tier this tracker is in + map.put("tier", tier++); + trackers.add(map); + } + List> alist = t.getAnnounceList(); + if (alist != null && !alist.isEmpty()) { + for (List alist2 : alist) { + for (String name2 : alist2) { + Map map = new HashMap(); + if (hack && !name2.contains("://")) { + name2 = "://" + name2; + } + map.put("id", name.hashCode()); + map.put("announce", name2); + map.put("scrape", ""); + map.put("tier", tier); + trackers.add(map); + } + tier++; + } + } + return trackers; + } + +/**** + private Object torrentGet_trackerStats(Snark download) { + List tracker_stats = new ArrayList(); + List trackerPeerSources = core_download.getTrackerPeerSources(); + if (trackerPeerSources == null) { + return tracker_stats; + } + for (TrackerPeerSource tps : trackerPeerSources) { + String statusString = tps.getStatusString(); + if (statusString == null) { + statusString = ""; + } + Map map = new HashMap( 64 ); + // how many downloads this tracker knows of (-1 means it does not know) + map.put("downloadCount", -1); // TODO + // whether or not we've ever sent this tracker an announcement + map.put("hasAnnounced", tps.getPeers() >= 0); // TODO + // whether or not we've ever scraped to this tracker + map.put("hasScraped", false); // todo: bool); + String name = ""; + try { + name = tps.getName(); + } catch (Exception e) { + // NPE at com.aelitis.azureus.plugins.extseed.ExternalSeedPlugin$5.getName(ExternalSeedPlugin.java:561 + } + // human-readable string identifying the tracker + map.put("host", name); // TODO + // the full announce URL + map.put("announce", name); // TODO + // the full scrape URL + map.put("scrape", name); // TODO + // Transmission uses one tracker per tier, + // and the others are kept as backups + map.put("isBackup", false); // TODO + // is the tracker announcing, waiting, queued, etc + int status = tps.getStatus(); + int state; + if (status == tps.ST_AVAILABLE || status == tps.ST_ONLINE) { + state = TR_TRACKER_WAITING; + } else if (status == tps.ST_UPDATING) { + state = TR_TRACKER_ACTIVE; + } else if (status == tps.ST_QUEUED) { + state = TR_TRACKER_QUEUED; + } else { + state = TR_TRACKER_INACTIVE; + } + map.put("announceState", state); + // is the tracker scraping, waiting, queued, etc + map.put("scrapeState", state); + // number of peers the tracker told us about last time. + // if "lastAnnounceSucceeded" is false, this field is undefined + map.put("lastAnnouncePeerCount", tps.getPeers()); + // human-readable string with the result of the last announce. + // if "hasAnnounced" is false, this field is undefined + if (statusString != null) { + map.put("lastAnnounceResult", statusString); + } + // when the last announce was sent to the tracker. + // if "hasAnnounced" is false, this field is undefined + map.put("lastAnnounceStartTime", 0); // TODO: time_t); + // whether or not the last announce was a success. + // if "hasAnnounced" is false, this field is undefined + map.put("lastAnnounceSucceeded", tps.getPeers() >= 0); + // whether or not the last announce timed out. + map.put("lastAnnounceTimedOut", false); // TODO + // when the last announce was completed. + .. if "hasAnnounced" is false, this field is undefined + map.put("lastAnnounceTime", 0); // TODO: time_t); + // human-readable string with the result of the last scrape. + // if "hasScraped" is false, this field is undefined + if (statusString != null) { + map.put("lastScrapeResult", statusString); + } + // when the last scrape was sent to the tracker. + // if "hasScraped" is false, this field is undefined/ + map.put("lastScrapeStartTime", 0); // TODO: time_t); + // whether or not the last scrape was a success. + // if "hasAnnounced" is false, this field is undefined + map.put("lastScrapeSucceeded", tps.getPeers() >= 0); + // whether or not the last scrape timed out. + map.put("lastScrapeTimedOut", false); // TODO: bool); + // when the last scrape was completed. + // if "hasScraped" is false, this field is undefined + map.put("lastScrapeTime", 0); // TODO: time_t); + // number of leechers this tracker knows of (-1 means it does not know) + map.put("leecherCount", tps.getLeecherCount()); + // when the next periodic announce message will be sent out. + // if announceState isn't TR_TRACKER_WAITING, this field is undefined + map.put("nextAnnounceTime", 0); // TODO: time_t); + // when the next periodic scrape message will be sent out. + // if scrapeState isn't TR_TRACKER_WAITING, this field is undefined + map.put("nextScrapeTime", 0); // TODO: time_t); + // number of seeders this tracker knows of (-1 means it does not know) + map.put("seederCount", tps.getSeedCount()); + // which tier this tracker is in + map.put("tier", 0); // TODO: int); + // used to match to a tr_tracker_info + map.put("id", tps.hashCode()); + tracker_stats.add(map); + } + return tracker_stats; + } +****/ + + private Object torrentGet_wanted(Snark download) { + // wanted + // | an array of tr_info.fileCount | tr_info + // | 'booleans' true if the corresponding | + // | file is to be downloaded. | + MetaInfo t = download.getMetaInfo(); + if (t == null) + return Collections.EMPTY_LIST; + int count = (t.getLengths() != null) ? t.getLengths().size() : 1; + Storage storage = download.getStorage(); + List list = new ArrayList(count); + int[] priorities = (storage != null) ? storage.getFilePriorities() : null; + for (int i = 0; i < count; i++) { + boolean skipped = priorities != null && priorities[i] < 0; + list.add(!skipped); + } + return list; + } + + private Object torrentGet_fileStats( + Snark download, + List file_fields, + Map args) { + // | a file's non-constant properties. | + // | array of tr_info.filecount objects, | + // | each containing: | + // +-------------------------+------------+ + // | bytesCompleted | number | tr_torrent + // | wanted | boolean | tr_info + // | priority | number | tr_info + MetaInfo t = download.getMetaInfo(); + Storage storage = download.getStorage(); + // TODO we could still do this w/o storage + if (t == null || storage == null) + return Collections.EMPTY_LIST; + List file_list = new ArrayList(); + // Skip files that match these hashcodes + List lengths = t.getLengths(); + if (lengths == null) + lengths = Collections.singletonList(Long.valueOf(t.getTotalLength())); + long[] remainings = storage.remaining(); + // could still be null if complete or single-file + int[] priorities = storage.getFilePriorities(); + List stats_list = new ArrayList(); + for (int i = 0; i < lengths.size(); i++) { + TreeMap map = new TreeMap(); + stats_list.add(map); + Long length = lengths.get(i); + long downloaded = length.longValue() - remainings[i]; + boolean skipped = priorities != null && priorities[i] < 0; + int priority = (priorities != null) ? priorities[i] : 0; + torrentGet_fileStats(map, file_fields, downloaded, skipped, priority); + } + return stats_list; + } + + private void torrentGet_fileStats(Map map, List sortedFields, + long downloaded, boolean skipped, int priority) { + boolean all = sortedFields == null || sortedFields.size() == 0; + if (all + || Collections.binarySearch(sortedFields, + FIELD_FILESTATS_BYTES_COMPLETED) >= 0) { + map.put(FIELD_FILESTATS_BYTES_COMPLETED, downloaded); + } + if (all + || Collections.binarySearch(sortedFields, FIELD_FILESTATS_WANTED) >= 0) { + map.put(FIELD_FILESTATS_WANTED, skipped); + } + if (all + || Collections.binarySearch(sortedFields, FIELD_FILESTATS_PRIORITY) >= 0) { + map.put(FIELD_FILESTATS_PRIORITY, + convertVuzePriority(priority)); + } + } + + private Object torrentGet_files( + String host, + Snark download, + long download_id, + List file_fields, + Map args) + { + // | array of objects, each containing: | + // +-------------------------+------------+ + // | bytesCompleted | number | tr_torrent + // | length | number | tr_info + // | name | string | tr_info + // Vuze, when file_indexes present: + // | index | number + // | hc | number | hashcode to be later used to supress return of file map + MetaInfo t = download.getMetaInfo(); + Storage storage = download.getStorage(); + if (t == null || storage == null) + return Collections.EMPTY_LIST; + List file_list = new ArrayList(); + // Skip files that match these hashcodes + List listHCs = MapUtils.getMapList(args, "files-hc-" + download_id, null); + List files = storage.getFiles(); + List lengths = t.getLengths(); + if (lengths == null) + lengths = Collections.singletonList(Long.valueOf(t.getTotalLength())); + long[] remainings = storage.remaining(); + int[] priorities; + if (file_fields != null && file_fields.size() > 0) { + // could still be null if complete or single-file + priorities = storage.getFilePriorities(); + } else { + priorities = null; + } + String baseURL = MapUtils.getMapString(args, "base-url", null); + //if (file_indexes == null || file_indexes.length == 0) { + for (int i = 0; i < files.size(); i++) { + File file = files.get(i); + Long length = lengths.get(i); + Long remaining = Long.valueOf(remainings[i]); + TreeMap map = new TreeMap(); + map.put("index", i); + torrentGet_files(map, file_fields, host, baseURL, download, file, length, remaining); + if (file_fields != null && file_fields.size() > 0) { + long downloaded = length.longValue() - remaining.longValue(); + boolean skipped = priorities != null && priorities[i] < 0; + int priority = (priorities != null) ? priorities[i] : 0; + torrentGet_fileStats(map, file_fields, downloaded, skipped, priority); + } + hashAndAdd(map, file_list, listHCs, i); + } +/**** + } else { + for (int i = 0; i < file_indexes.length; i++) { + int file_index = file_indexes[i]; + if (file_index < 0 || file_index >= files.length) { + continue; + } + TreeMap map = new TreeMap(); + map.put("index", file_index); + File fileInfo = files.get(file_index); + Long length = lengths.get(i); + long remaining = remainings[i]; + torrentGet_fileStats(map, file_fields, fileInfo); + torrentGet_files(map, file_fields, host, baseURL, download, fileInfo); + hashAndAdd(map, file_list, listHCs, i); + } + } +****/ + return file_list; + } + + /** metainfo and storage must be non-null */ + private void torrentGet_files(Map obj, List sortedFields, + String host, String baseURL, Snark download, File file, Long length, long remaining) { + boolean all = sortedFields == null || sortedFields.size() == 0; + if (all + || Collections.binarySearch(sortedFields, + FIELD_FILESTATS_BYTES_COMPLETED) >= 0) { + long downloaded = length.longValue() - remaining; + obj.put(FIELD_FILESTATS_BYTES_COMPLETED, downloaded); // this must be a spec error... + } + if (all || Collections.binarySearch(sortedFields, FIELD_FILES_LENGTH) >= 0) { + obj.put(FIELD_FILES_LENGTH, length); + } + if (all || Collections.binarySearch(sortedFields, FIELD_FILES_NAME) >= 0) { + String absolutePath = file.getAbsolutePath(); + String savePath = download.getStorage().getBase().getAbsolutePath(); + MetaInfo torrent = download.getMetaInfo(); + boolean simpleTorrent = torrent == null ? false : torrent.getLengths() == null; + if (simpleTorrent) { + obj.put(FIELD_FILES_NAME, file.getName()); + } else { + if (absolutePath.startsWith(savePath)) { + // TODO: .dnd_az parent.. + //String dnd_sf = dm.getDownloadState().getAttribute( SnarkManagerState.AT_DND_SUBFOLDER ); + // + 1 to remove the dir separator + obj.put(FIELD_FILES_NAME, absolutePath.substring(savePath.length() + 1)); + } else { + obj.put(FIELD_FILES_NAME, absolutePath); + } + } + } + // Vuze specific, don't clutter transmission clients with these (they don't + // have sortedFields param) +/**** + if (sortedFields != null) { + boolean showAllVuze = sortedFields.size() == 0; + if (showAllVuze + || Collections.binarySearch(sortedFields, FIELD_FILES_CONTENT_URL) >= 0) { + URL f_stream_url = PlayUtils.getMediaServerContentURL(file); + if (f_stream_url != null) { + String s = adjustURL(host, f_stream_url); + if (baseURL != null && s.startsWith(baseURL)) { + s = s.substring(baseURL.length(), s.length()); + } + obj.put(FIELD_FILES_CONTENT_URL, s); + } + } + if (showAllVuze + || Collections.binarySearch(sortedFields, FIELD_FILES_FULL_PATH) >= 0) { + obj.put(FIELD_FILES_FULL_PATH, file.getFile().toString()); + } + } +****/ + } + +/**** + private int[] getFileIndexes(Map args, long download_id) { + Object file_ids = args.get("file-indexes-" + download_id); + int[] file_indexes = null; + if (file_ids instanceof Number) { + file_indexes = new int[] { + ((Number) file_ids).intValue() + }; + } else if (file_ids instanceof List) { + List listFileIDs = (List) file_ids; + file_indexes = new int[listFileIDs.size()]; + for (int i = 0; i < listFileIDs.size(); i++) { + Object o = listFileIDs.get(i); + if (o instanceof Number) { + file_indexes[i] = ((Number) o).intValue(); + } + } + } + return file_indexes; + } +****/ + + /** + * The last time we uploaded or downloaded piece data on this torrent. + */ +/**** + private Object torrentGet_activityDate(SnarkManager download, boolean relative) { + int state = download.getState(); + if (state == SnarkManager.STATE_SEEDING || state == SnarkManager.STATE_DOWNLOADING) { + int r = download.getStats().getTimeSinceLastDataReceivedInSeconds(); + int s = download.getStats().getTimeSinceLastDataSentInSeconds(); + long l; + if (r > 0 && s > 0) { + l = Math.min(r, s); + } else if (r < 0) { + l = s; + } else { + l = r; + } + if (relative) { + return -l; + } + // XXX THIS IS STUPID! Time on this machine won't be the same as the client.. + return (_context.clock().now() / 1000) - l; + } + return 0; + } +****/ + + /** + * True if the torrent is running, but has been idle for long enough + * to be considered stalled. + */ + private boolean torrentGet_isStalled(Snark download) { + // will return false if seeding, although vuze could return true + return !download.isStopped() && download.getNeededLength() > 0 && download.getDownloadRate() == 0; + } + + private List torrentGet_peers(Snark download) { + // peers | array of objects, each containing: | + // +-------------------------+------------+ + // | address | string | tr_peer_stat + // | clientName | string | tr_peer_stat + // | clientIsChoked | boolean | tr_peer_stat + // | clientIsInterested | boolean | tr_peer_stat + // | flagStr | string | tr_peer_stat + // | isDownloadingFrom | boolean | tr_peer_stat + // | isEncrypted | boolean | tr_peer_stat + // | isIncoming | boolean | tr_peer_stat + // | isUploadingTo | boolean | tr_peer_stat + // | isUTP | boolean | tr_peer_stat + // | peerIsChoked | boolean | tr_peer_stat + // | peerIsInterested | boolean | tr_peer_stat + // | port | number | tr_peer_stat + // | progress | double | tr_peer_stat + // | rateToClient (B/s) | number | tr_peer_stat + // | rateToPeer (B/s) | number | tr_peer_stat + if (download.isStopped()) { + return Collections.EMPTY_LIST; + } + List peers = new ArrayList(); + List peerList = download.getPeerList(); + for (Peer peer : peerList) { + Map map = new HashMap(); + peers.add(map); + long dlRate = peer.getDownloadRate(); + long ulRate = peer.getUploadRate(); + boolean isDownloadingFrom = !peer.isChoked() && dlRate > 0; + boolean isUploadingTo = !peer.isChoking() && ulRate > 0; + Destination dest = peer.getDestination(); + String b32 = (dest != null) ? dest.toBase32() : ""; + map.put("address", b32); + String client = UIUtil.getClientName(peer.getPeerID()); + map.put("clientName", client); + map.put("clientIsChoked", peer.isChoking()); + map.put("clientIsInterested", peer.isInterested()); + // flagStr + // "O": "Optimistic unchoke" + // "D": "Downloading from this peer" + // "d": "We would download from this peer if they'd let us" + // "U": "Uploading to peer" + // "u": "We would upload to this peer if they'd ask" + // "K": "Peer has unchoked us, but we're not interested" + // "?": "We unchoked this peer, but they're not interested" + // "E": "Encrypted Connection" + // "H": "Peer was discovered through Distributed Hash Table (DHT)" + // "X": "Peer was discovered through Peer Exchange (PEX)" + // "I": "Peer is an incoming connection" + // "T": "Peer is connected via uTP" + StringBuilder flagStr = new StringBuilder(8); + if (isDownloadingFrom) { + flagStr.append('D'); + } else if (peer.isInteresting()) { + flagStr.append('d'); + } else if (peer.isChoked()) { + flagStr.append('K'); + } + if (isUploadingTo) { + flagStr.append('U'); + } else if (peer.isInterested()) { + flagStr.append('u'); + } else if (peer.isChoking()) { + flagStr.append('?'); + } + flagStr.append('E'); + if (peer.isIncoming()) { + flagStr.append('I'); + } + map.put("flagStr", flagStr.toString()); + // code, name + //String[] countryDetails = PeerUtils.getCountryDetails(peer); + //if (countryDetails != null && countryDetails.length > 0) { + // map.put("cc", countryDetails[0]); + //} + map.put("isDownloadingFrom", isDownloadingFrom); + // peer.connection.getTransport().isEncrypted + map.put("isEncrypted", Boolean.TRUE); + map.put("isIncoming", peer.isIncoming()); + map.put("isUploadingTo", isUploadingTo); + // RPC v13 + map.put("isUTP", Boolean.FALSE); + map.put("peerIsChoked", peer.isChoked()); + map.put("peerIsInterested", peer.isInteresting()); + // RPC v3 + map.put("port", TrackerClient.PORT); + // appx + float pct = ((float) peer.completed()) / ((float) download.getMetaInfo().getPieces()); + map.put("progress", pct); + map.put("rateToClient", dlRate); + map.put("rateToPeer", ulRate); + } + return peers; + } + + protected List + getAllDownloads( + boolean include_magnet_dowloads ) + { + Collection downloads1 = _manager.getTorrents(); + List result = new ArrayList( downloads1.size() ); + if (!include_magnet_dowloads) { + for (Snark snark : downloads1) { + if (snark.getMetaInfo() != null) + result.add(snark); + } + } else { + result.addAll( downloads1 ); + } + return( result ); + } + + protected List + getDownloads( + Object ids, + boolean include_magnet_dowloads ) + { + List downloads = new ArrayList(); + List all_downloads = getAllDownloads( include_magnet_dowloads ); + List selected_ids = new ArrayList(); + List selected_hashes = new ArrayList(); + if ( ids == null ) { + } else if ( ids instanceof String ) { + ids = null; + } else if ( ids instanceof Number ) { + selected_ids.add(((Number)ids).longValue()); + } else if ( ids instanceof List ) { + List l = (List)ids; + for (Object o: l ) { + if ( o instanceof Number ) { + selected_ids.add(((Number)o).longValue()); + } else if ( o instanceof String ) { + selected_hashes.add((String)o); + } + } + } + boolean hide_ln = hide_ln_param; + for( Snark download : all_downloads ) { + try { +/**** + if ( hide_ln && download.getFlag( Snark.FLAG_LOW_NOISE )) { + continue; + } + if ( download.getFlag( Snark.FLAG_METADATA_DOWNLOAD )) { + continue; + } +****/ + if ( ids == null ) { + downloads.add( download ); + } else { + long id = getID( download, true ); + if ( selected_ids.contains( id )) { + downloads.add( download ); + } else if (!selected_hashes.isEmpty()) { + if ( selected_hashes.contains( I2PSnarkUtil.toHex( download.getInfoHash()))) { + downloads.add( download ); + } + } + } + } catch( Throwable e ) { + //Debug.out( e ); + } + } + if (ids != null && downloads.isEmpty()) + throw new TextualException("No matching torrents found"); + Collections.sort( + downloads, + new Comparator() + { + public int + compare( + Snark arg0, + Snark arg1 ) + { + long res = getID( arg0, true ) - getID( arg1, true ); + if ( res < 0 ) { + return( -1 ); + } else if ( res > 0 ) { + return( 1 ); + } else { + return( 0 ); + } + } + }); + return( downloads ); + } + +/**** + public List + getSnarkManagerListFromIDs( + GlobalManager gm, + Object ids ) + { + List downloads = getDownloads(ids,false); + ArrayList list = new ArrayList(downloads.size()); + for ( Snark downloadStub: downloads ) { + try { + Snark download = destubbify( downloadStub ); + if (download != null) { + SnarkManager dm = PluginCoreUtils.unwrap(download); + if (dm != null) { + list.add(dm); + } + } + } catch( Throwable e ) { + Debug.out( "Failed to get dm '" + downloadStub.getName() + "'", e ); + } + } + return list; + } +****/ + + private static List + getList( + Object o ) + { + if ( o instanceof List ) { + return (List) o; + } else { + return Collections.EMPTY_LIST; + } + } + + private static boolean + getBoolean( + Object o ) + { + return getBoolean(o, Boolean.FALSE); + } + + private static Boolean + getBoolean( + Object o, + Boolean defaultVal ) + { + if ( o instanceof Boolean ) { + return((Boolean)o); + } else if ( o instanceof String ) { + return( Boolean.valueOf((String)o) ); + } else if ( o instanceof Number ) { + return(((Number)o).intValue()!=0); + } else { + return( defaultVal ); + } + } + + protected long + getID( + Snark download_stub, + boolean allocate_if_new ) + { + synchronized( this ) { + if ( check_ids_outstanding ) { + check_ids_outstanding = false; + List all_downloads = getAllDownloads( true ); + Set all_ids = new HashSet(); + List dups = new ArrayList(); + long max_id = 0; + for( Snark d: all_downloads ) { + long id = getID( d, false ); + if ( id <= 0 ) { + continue; + } + max_id = Math.max( max_id, id ); + if ( all_ids.contains( id )) { + dups.add( d ); + } else { + all_ids.add( id ); + } + } + } + } + // I was trying to be clever and allocate unique ids for downloads. however, + // the webui assumes they are consecutive and give a queue index. ho hum + // return( d.getIndex()); + long id = download_stub.getRPCID(); + //System.out.println( download.getName() + " -> " + id ); + return( id ); + } + +/**** + private void + processVuzeTorrentGet( + HttpServletRequest request, + Map args, + Map result) + { + Object ids = args.get( "ids" ); + List downloads = getDownloads( ids, true ); + List torrents = new ArrayList( downloads.size()); + result.put( "torrents", torrents ); + List requested_files = (List)args.get( "files" ); + String host = (String)request.getHeader( "host" ); + for ( Snark download_stub: downloads ) { + Map torrent = new HashMap(); + torrents.add( torrent ); + long id = getID( download_stub, true ); + torrent.put( "id", id ); + if ( download_stub.isStub()) { + continue; + } + try { + Snark download = download_stub.destubbify(); + SnarkManager dm = PluginCoreUtils.unwrap( download ); + if ( dm == null ) { + continue; + } + DiskManagerFileInfo file = null; + try { + file = PluginCoreUtils.wrap(dm.getDownloadState().getPrimaryFile()); + } catch( DownloadException e ) { + continue; + } + if ( file == null ) { + continue; + } + URL stream_url = PlayUtils.getMediaServerContentURL( file ); + if ( stream_url != null ) { + torrent.put( "contentURL", adjustURL( host, stream_url )); + } + TOTorrent to_torrent = dm.getTorrent(); + if ( to_torrent != null ) { + String url = PlatformTorrentUtils.getContentThumbnailUrl( to_torrent ); + if ( url != null ) { + torrent.put( "thumbnailURL", url ); + } else { + byte[] data = PlatformTorrentUtils.getContentThumbnail( to_torrent ); + if ( data != null ) { + torrent.put( "thumbnailURL", getThumbnailResourceURL( id )); + } + } + } + if ( requested_files != null ) { + List file_info = new ArrayList(); + torrent.put( "files", file_info ); + DiskManagerFileInfo[] files = download.getDiskManagerFileInfo(); + if ( requested_files.size() == 0 ) { + for ( DiskManagerFileInfo f: files ) { + Map f_map = new HashMap(); + file_info.add( f_map ); + f_map.put( "index", f.getIndex()); + URL f_stream_url = PlayUtils.getMediaServerContentURL( f ); + if ( f_stream_url != null ) { + f_map.put( "contentURL", adjustURL( host, f_stream_url )); + } + } + } else { + for ( Number num: requested_files ) { + int index = num.intValue(); + if ( index >= 0 && index < files.length ) { + DiskManagerFileInfo f = files[index]; + Map f_map = new HashMap(); + file_info.add( f_map ); + f_map.put( "index", f.getIndex()); + URL f_stream_url = PlayUtils.getMediaServerContentURL( f ); + if ( f_stream_url != null ) { + f_map.put( "contentURL", adjustURL( host, f_stream_url )); + } + } + } + } + } + } catch( Throwable e ) { + Debug.out( e ); + } + } + } +****/ + + private static final int RT_THUMBNAIL = 0; + + private static String + getThumbnailResourceURL( + long id ) + { + Map map = new HashMap(); + map.put( "type", RT_THUMBNAIL ); + map.put( "id", id ); + String json = JSONUtils.encodeToJSON( map ); + //return( "/vuze/resource?json=" + UrlUtils.encode( json )); + return( "/vuze/resource?json=busted"); + } + + private boolean + processResourceRequest( + HttpServletRequest request, + HttpServletResponse response, + Map request_json ) + throws IOException + { + int type = ((Number)request_json.get( "type" )).intValue(); + if ( type == RT_THUMBNAIL ) { + long id = ((Number)request_json.get( "id" )).longValue(); + List list = getDownloads( id, false ); + if ( list == null || list.size() != 1 ) { + throw( new IOException( "Unknown download id: " + id )); + } + try { + throw new TextualException("unimplemented"); +/**** + Snark download = list.get(0).destubbify(); + MetaInfo torrent = download.getTorrent(); + byte[] data = PlatformTorrentUtils.getContentThumbnail( PluginCoreUtils.unwrap( torrent )); + // TODO: handle image types + response.setContentType( "image/jpeg" ); + response.getOutputStream().write( data ); +****/ + } catch( Throwable e ) { + throw( new IOException( "Failed to get thumbnail", e)); + } +// return( true ); + } else { + throw( new IOException( "Unknown resource type: " + type )); + } + } + + private static String + adjustURL( + String host, + URL url ) + { + if ( host == null || host.length() == 0 ) { + return( url.toExternalForm()); + } + int pos = host.indexOf( ':' ); + if ( pos != -1 ) { + host = host.substring( 0, pos ).trim(); + } + return( setHost( url, host ).toExternalForm()); + } + +/**** + private void + processVuzeLifecycle( + Map args, + Map result ) + throws IOException + { + checkUpdatePermissions(); + String cmd = (String)args.get( "cmd" ); + if ( cmd == null ) { + throw( new IOException( "cmd missing" )); + } + try { + if ( cmd.equals( "status" )) { + synchronized( lifecycle_lock ) { + result.put( "state", lifecycle_state ); + } + } else if ( cmd.equals( "close" )) { + synchronized( lifecycle_lock ) { + if ( lifecycle_state < 2 ) { + lifecycle_state = 2; + } else { + return; + } + } + PluginManager.stopAzureus(); + } else if ( cmd.equals( "restart" )) { + synchronized( lifecycle_lock ) { + if ( lifecycle_state < 2 ) { + lifecycle_state = 3; + } else { + return; + } + } + PluginManager.restartAzureus(); + } else if ( cmd.equals( "update-check" )) { + synchronized( lifecycle_lock ) { + if ( lifecycle_state != 1 ) { + throw( new IOException( "update check can't currently be performed" )); + } + if ( update_in_progress ) { + throw( new IOException( "update operation in progress" )); + } + update_in_progress = true; + } + try { + UpdateManager update_manager = plugin_interface.getUpdateManager(); + final UpdateCheckInstance checker = update_manager.createUpdateCheckInstance(); + final List l_updates = new ArrayList(); + final AESemaphore sem = new AESemaphore( "uc-wait" ); + checker.addListener( + new UpdateCheckInstanceListener() + { + public void + cancelled( + UpdateCheckInstance instance ) + { + sem.release(); + } + public void + complete( + UpdateCheckInstance instance ) + { + try { + Update[] updates = instance.getUpdates(); + for (int i=0;i args, + Map result ) + throws IOException + { + checkUpdatePermissions(); + try { + String cmd = (String)args.get( "cmd" ); + if ( cmd == null ) { + throw( new IOException( "cmd missing" )); + } + PairingManager pm = PairingManagerFactory.getSingleton(); + if ( cmd.equals( "status" )) { + result.put( "status", pm.getStatus()); + boolean enabled = pm.isEnabled(); + result.put( "enabled", enabled ); + if ( enabled ) { + result.put( "access_code", pm.peekAccessCode()); + } + boolean srp_enabled = pm.isSRPEnabled(); + result.put( "srp_enabled", srp_enabled ); + if ( srp_enabled ) { + result.put( "srp_status", pm.getSRPStatus()); + } + } else if ( cmd.equals( "set-enabled" )) { + boolean enabled = (Boolean)args.get( "enabled" ); + if ( enabled != pm.isEnabled()) { + pm.setEnabled( enabled ); + } + } else if ( cmd.equals( "set-srp-enabled" )) { + boolean enabled = (Boolean)args.get( "enabled" ); + if ( enabled != pm.isSRPEnabled()) { + if ( enabled ) { + String pw = (String)args.get( "password" ); + if ( pw == null ) { + throw( new IOException( "Password required when enabling SRP" )); + } + pm.setSRPEnabled( true ); + pm.setSRPPassword( pw.toCharArray()); + } else { + pm.setSRPEnabled( false ); + } + } + } else { + throw( new IOException( "Unknown cmd: " + cmd )); + } + } catch( IOException e ) { + throw( e ); + } + } +****/ + + private static class + PermissionDeniedException + extends IOException + { + private static final long serialVersionUID = -344396020759893604L; + } + + private static String + escapeXML( + String str ) + { + if ( str == null ) { + return( "" ); + } + str = str.replaceAll( "&", "&" ); + str = str.replaceAll( ">", ">" ); + str = str.replaceAll( "<", "<" ); + str = str.replaceAll( "\"", """ ); + str = str.replaceAll( "--", "--" ); + return( str ); + } + +/**** + private static Number getTrackerID(TrackerPeerSource source) { + return Long.valueOf((source.getName().hashCode() << 4l) + source.getType()); + } +****/ + + // Copy of RelatedContentManager.getURLList, except with MetaInfo (not TOTorrent) + private static List + getURLList( + MetaInfo torrent, + String key ) + { +/**** + Object obj = torrent.getAdditionalProperty( key ); + if ( obj instanceof byte[] ) { + List l = new ArrayList(); + l.add(obj); + return( l ); + } else if ( obj instanceof List ) { + return (List)BEncoder.clone(obj); + } else { +****/ + return( Collections.EMPTY_LIST ); +/**** + } +****/ + } + + private static void hashAndAdd(SortedMap map, List addToList, List hcMatchList, + int i) { + long hashCode = longHashSimpleMap(map); + // hex string shorter than long in json, even with quotes + String hc = Long.toHexString(hashCode); + boolean remove = hcMatchList != null && i < hcMatchList.size() + && hc.equals(hcMatchList.get(i)); + if (!remove) { + map.put("hc", hc); + addToList.add(map); + } + } + + /** + * Very simple 64 bit hash of a map's keys (assumed String, esp on JSON map), + * and values (JSON types -- String, Number, Map (object), List (array), Boolean. + */ + private static long longHashSimpleMap(SortedMap map) { + long hash = 0; + Object hc = map.get("hc"); + if (hc instanceof String) { + return Long.parseLong((String) hc, 16); + } + for (Object key : map.keySet()) { + Object value = map.get(key); + hash = (hash * 31) + hash(key.toString()); + if (value instanceof String) { + hash = (hash * 31) + hash((String) value); + } else if (value instanceof Number) { + // not sure about this + hash = (hash * 31) + ((Number) value).hashCode(); + } else if (value instanceof SortedMap) { + hash = (hash * 31) + longHashSimpleMap((SortedMap) value); + } else if (value instanceof Collection) { + hash = (hash * 31) + longHashSimpleList((List) value); + } else if (value instanceof Boolean) { + hash = (hash * 31) + ((Boolean) value ? 1231 : 1237); + } else { + // else skip all other values since we can't be sure how they hash + //System.out.println("Warning: Unhashed Value. key '" + key + "' Value: " + value); + } + } + return hash; + } + + private static long longHashSimpleList(Collection list) { + long hash = 0; + for (Object value : list) { + if (value instanceof String) { + hash = (hash * 31) + hash((String) value); + } else if (value instanceof Number) { + // not sure about this + hash = (hash * 31) + ((Number) value).hashCode(); + } else if (value instanceof SortedMap) { + hash = (hash * 31) + longHashSimpleMap((SortedMap) value); + } else if (value instanceof Collection) { + hash = (hash * 31) + longHashSimpleList((Collection) value); + } else if (value instanceof Boolean) { + hash = (hash * 31) + ((Boolean) value ? 1231 : 1237); + } // else skip all other values since we can't be sure how they hash + } + return hash; + } + + // FROM http://stackoverflow.com/questions/1660501/what-is-a-good-64bit-hash-function-in-java-for-textual-strings + //adapted from String.hashCode() + private static long hash(String string) { + long h = 1125899906842597L; // prime + int len = string.length(); + for (int i = 0; i < len; i++) { + h = 31 * h + string.charAt(i); + } + return h; + } + + ////////////////////////////////////////////////////////////////////////////// + private class + RecentlyRemovedData + { + private final long id; + private final long create_time = _context.clock().now(); + //private final Set sessions = new HashSet(); + + private + RecentlyRemovedData( + long _id ) + { + id = _id; + } + + private long + getID() + { + return( id ); + } + + private long + getCreateTime() + { + return( create_time ); + } + + private boolean + hasSession( + String session ) + { + /* + * Actually it seems the webui doesn't consistently handle the removed-ids so just + * return the ID for a time period to ensure that it is processed. + * Update - might be that multiple clients in the same browser are using the same session id + * so going to go with reporting 'recently-removed' for a time period instead of just once + * per session + * + synchronized( sessions ) { + if ( sessions.contains( session )) { + return( true ); + } else { + sessions.add( session ); + return( false ); + } + } + */ + return( false ); + } + } + +/**** + private class + MagnetDownload + implements Snark + { + private URL magnet_url; + private String name; + private byte[] hash; + private long create_time; + private Map attributes = new HashMap(); + private String temp_dir = _util.getTempDir().getAbsolutePath(); + private Throwable error; + + private + MagnetDownload( + URL _magnet, + String friendlyName ) + { + create_time = _context.clock().now(); + magnet_url = _magnet; + String str = magnet_url.toExternalForm(); + int pos = str.indexOf( '?' ); + if ( pos != -1 ) { + str = str.substring( pos+1 ); + } + String[] args = str.split( "&" ); + Map arg_map = new HashMap(); + for ( String arg: args ) { + String[] bits = arg.split( "=" ); + if ( bits.length == 2 ) { + try { + String lhs = bits[0].trim().toLowerCase( Locale.US ); + String rhs = URLDecoder.decode( bits[1].trim(), "UTF-8"); + if ( lhs.equals( "xt" )) { + if ( rhs.toLowerCase( Locale.US ).startsWith( "urn:btih:" )) { + arg_map.put( lhs, rhs ); + } else { + String existing = arg_map.get( "xt" ); + if ( existing == null || + ( !existing.toLowerCase( Locale.US ).startsWith( "urn:btih:" ) && rhs.startsWith( "urn:sha1:" ))) { + arg_map.put( lhs, rhs ); + } + } + } else { + arg_map.put( lhs, rhs ); + } + } catch( Throwable e ) { + } + } + } + hash = new byte[0]; + String hash_str = arg_map.get( "xt" ); + if ( hash_str != null ) { + hash_str = hash_str.toLowerCase( Locale.US ); + if ( hash_str.startsWith( "urn:btih:" ) || hash_str.startsWith( "urn:sha1" )) { + hash = UrlUtils.decodeSHA1Hash( hash_str.substring( 9 )); + } + } + name = arg_map.get( "dn" ); + if ( name == null ) { + if ( friendlyName != null ) { + name = friendlyName; + } else if ( hash == null ) { + name = magnet_url.toExternalForm(); + } else { + name = Base32.encode( hash ); + } + } + name = "Magnet download for '" + name + "'"; + getID( this, true ); + } + + private long + getCreateTime() + { + return( create_time ); + } + + private URL + getMagnetURL() + { + return( magnet_url ); + } + + public boolean + isStub() + { + return( true ); + } + + public Snark + destubbify() + throws DownloadException + { + throw( new DownloadException( "Not supported" )); + } + + public String + getName() + { + return( name ); + } + + public byte[] + getInfoHash() + { + return( hash ); + } + + public MetaInfo + getTorrent() + { + return( null ); + } + + public long + getTorrentSize() + { + return( 16*1024 ); // dont know the size + } + + public String + getSavePath() + { + return( temp_dir ); + } + + private void + setError( + Throwable e ) + { + error = e; + } + + private Throwable + getError() + { + return( error ); + } + + public SnarkFile[] + getStubFiles() + { + return( new SnarkFile[0]); + } + + public long + getLongAttribute( + TorrentAttribute attribute ) + { + Long l = attributes.get( attribute ); + return( l==null?0:l ); + } + + public void + setLongAttribute( + TorrentAttribute attribute, + long value) + { + attributes.put( attribute, value ); + } + + public void + remove() + throws DownloadException + { + } + } +****/ + + /** + * Copied from Vuze UrlUtils.java + */ + private static URL + setHost( + URL u, + String host ) + { + StringBuilder result = new StringBuilder(); + result.append(u.getProtocol()); + result.append(":"); + String authority=u.getAuthority(); + if (authority != null && authority.length() > 0) { + result.append("//"); + int pos = authority.indexOf( '@' ); + if ( pos != -1 ) { + result.append(authority.substring(0,pos+1)); + authority = authority.substring(pos+1); + } + pos = authority.lastIndexOf(':'); + if ( pos == -1 ) { + result.append(host ); + } else { + result.append(host).append(authority.substring(pos)); + } + } + if (u.getPath() != null) { + result.append(u.getPath()); + } + if (u.getQuery() != null) { + result.append('?'); + result.append(u.getQuery()); + } + if (u.getRef() != null) { + result.append("#"); + result.append(u.getRef()); + } + try { + return( new URL( result.toString())); + } catch( Throwable e ) { + return(u); + } + } + + /** translate */ + private String _t(String s) { + return _util.getString(s); + } + + protected void log(String str) { + _log.debug(str); + } + + protected void log(String str, Throwable t) { + _log.debug(str, t); + } +} diff --git a/src/jsp/WEB-INF/web.xml b/src/jsp/WEB-INF/web.xml new file mode 100644 index 0000000..63a948a --- /dev/null +++ b/src/jsp/WEB-INF/web.xml @@ -0,0 +1,30 @@ + + + + + + org.klomp.snark.rpc.RPCServlet + org.klomp.snark.rpc.RPCServlet + + + + org.klomp.snark.rpc.RPCServlet + + /rpc + /rpc/* + /upload + /upload/* + + /web/transmission/rpc + /web/transmission/rpc/* + /web/transmission/upload + /web/transmission/upload/* + /web/vuze/rpc + /web/vuze/rpc/* + /web/vuze/upload + /web/vuze/upload/* + + + diff --git a/src/jsp/index.html b/src/jsp/index.html new file mode 100644 index 0000000..a4e27ea --- /dev/null +++ b/src/jsp/index.html @@ -0,0 +1,29 @@ + + +

i2psnark-rpc Plugin Version 0.1

+

+This is a Transmission- and Vuze- compatible RPC server for i2psnark, +with the Vuze-modified Transmission web UI. + +

+To access the web UI, go to /transmission/web/ in your router console. + +

+To use any compatible RPC client software, such as transmission-remote, +specify port 7657. For example, to list the torrents: + +

+transmission-remote 7657 -l
+
+ +

+Most basic features are supported. Several advanced features are not supported. +Some may be added in a future release. +Please report bugs on zzz.i2p. + +

+Web UI +
+Licenses + + diff --git a/src/jsp/licenses.html b/src/jsp/licenses.html new file mode 100644 index 0000000..2402ec5 --- /dev/null +++ b/src/jsp/licenses.html @@ -0,0 +1,45 @@ + + +

i2psnark-rpc licenses

+

+New code: GPLv2 +
+See LICENSE-GPLv2.txt + +

+Contains code from Vuze trunk and from xmwebui plugin, +which includes code from Transmission and JSON-simple. + +

+Vuze Web Remote Plugin (xmwebui): GPLv2 +
+Modified from v0.6.5 from SVN +
+Copyright 2009 Vuze, Inc. All rights reserved. +
+See LICENSE-GPLv2.txt + +

+Vuze: GPLv2 +
+Modified from v5.7.5.1 from SVN +
+Copyright 2009 Vuze, Inc. All rights reserved. +
+See LICENSE-GPLv2.txt + +

+Transmission: +
+Copyright (c) Transmission authors and contributors +
+See Transmission.txt + +

+JSON: +
+LGPLv2.1 +
+See JSON.txt + + diff --git a/src/jsp/web/easyXDM/hash.html b/src/jsp/web/easyXDM/hash.html new file mode 100644 index 0000000..e8cd53a --- /dev/null +++ b/src/jsp/web/easyXDM/hash.html @@ -0,0 +1,25 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/jsp/web/images/favicon.ico b/src/jsp/web/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..efce6d5be210f402c4f553479e05bc404f766122 GIT binary patch literal 1150 zcma)+Ye_>UX%4Vy~3Kd%{ib#TrN-0asOfreEpaYc_9;RvK zxuvGjN$8+xl9Xv-Bte;I8rF1`*pE(XWbE#J-sPC!kG}Ejd-uY9^Bk^cg5bnHGMT`> zXA65Jf}j=zA&>>TVmG#X_L}xd@%JN32j!6wsKS*< zZ`@oX$88q3^m4qaIPECK*q33no=!mttLYM}r;zes9#jkUYibk*8ceWS zt#M$|-by8}7zcyu}Cc-x7@@0&V#VXEN%k~-Q+UJJuWH-D{n!$ZKlYQ~6r_HXV!`O%g zH}o2w!)4Y>VvIO_veOlP2VL+k%@w@|T$z{6qwm7DDSGTm@`IBNVo^((Y@b|*U@9# zgrDX&oQdz|9@H5`&S~Y!fBH~`wIfk#F&}m29)sx;^s`#JSTCrjG#8rf!&pDukI`^W zYZ>dbFU4%j(9;iOq#(OMDPx#3`{(`$9WMdwVFz%W%?Z L+P{VWx+LvipPO}$ literal 0 HcmV?d00001 diff --git a/src/jsp/web/images/favicon.png b/src/jsp/web/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..c635990b5dd89827fc7beeab787aacc502b6ae87 GIT binary patch literal 1660 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Vuze Remote + + + +

+ + + + + + + + + +
+
    +
    + + + + + + + + + + + + + + + + + diff --git a/src/jsp/web/javascript/common.js b/src/jsp/web/javascript/common.js new file mode 100644 index 0000000..56c556b --- /dev/null +++ b/src/jsp/web/javascript/common.js @@ -0,0 +1,358 @@ +/* Transmission Revision 14025 */ +/** + * Copyright © Dave Perrett and Malcolm Jarvis + * + * This file is licensed under the GPLv2. + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ + +var transmission, + dialog, + /* Vuze: Force never isMobileDevice! + isMobileDevice = RegExp("(iPhone|iPod|Android)").test(navigator.userAgent), + */ + isMobileDevice = false, + scroll_timeout; + +if (!Array.indexOf){ + Array.prototype.indexOf = function(obj){ + var i, len; + for (i=0, len=this.length; i a').css({'float':'none','padding':'0'}); + var tabul = this.find('ul:first'); + this.parent().addClass('ui-tabs').prepend(tabul).draggable('option','handle',tabul); + this.siblings('.ui-dialog-titlebar').remove(); + tabul.addClass('ui-dialog-titlebar'); +} + +$(document).ready(function() { + + // IE8 and below don’t support ES5 Date.now() + if (!Date.now) { + Date.now = function() { + return +new Date(); + }; + } + + /* Vuze: Removed browser specific calls + // IE specific fixes here + if ($.browser.msie) { + try { + document.execCommand("BackgroundImageCache", false, true); + } catch(err) {} + $('.dialog_container').css('height',$(window).height()+'px'); + } + */ + + /* Vuze Removed (no idea why) + if ($.browser.safari) { + // Move search field's margin down for the styled input + $('#torrent_search').css('margin-top', 3); + } + */ + if (isMobileDevice){ + window.onload = function(){ 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 + $('body div#torrent_container').css('min-height', '338px'); + $("label[for=torrent_upload_url]").text("URL: "); + } else { + // Fix for non-Safari-3 browsers: dark borders to replace shadows. + // Opera messes up the menu if we use a border on .trans_menu + // div.outerbox so use ul instead + $('.trans_menu ul, div#jqContextMenu, div.dialog_container div.dialog_window').css('border', '1px solid #777'); + // and this kills the border we used to have + $('.trans_menu div.outerbox').css('border', 'none'); + } + + // Initialise the dialog controller + dialog = new Dialog(); + + // Initialise the main Transmission controller + transmission = new Transmission(); +}); + +/** + * Checks to see if the content actually changed before poking the DOM. + */ +function setInnerHTML(e, html) +{ + if (!e) + return; + + /* innerHTML is listed as a string, but the browser seems to change it. + * For example, "∞" gets changed to "∞" somewhere down the line. + * So, let's use an arbitrary different field to test our state... */ + if (e.currentHTML != html) + { + e.currentHTML = html; + e.innerHTML = html; + } +}; + +function sanitizeText(text) +{ + return text.replace(//g, ">"); +}; + +/** + * Many of our text changes are triggered by periodic refreshes + * on torrents whose state hasn't changed since the last update, + * so see if the text actually changed before poking the DOM. + */ +function setTextContent(e, text) +{ + if (e && (e.textContent != text)) + e.textContent = text; +}; + +/* + * Given a numerator and denominator, return a ratio string + */ +Math.ratio = function(numerator, denominator) { + var result = Math.floor(100 * numerator / denominator) / 100; + + // check for special cases + if (result==Number.POSITIVE_INFINITY || result==Number.NEGATIVE_INFINITY) result = -2; + else if (isNaN(result)) result = -1; + + return result; +}; + + +/** + * Round a string of a number to a specified number of decimal places + */ +Number.prototype.toTruncFixed = function(place) { + var ret = Math.floor(this * Math.pow (10, place)) / Math.pow(10, place); + return ret.toFixed(place); +} + +Number.prototype.toStringWithCommas = function() { + return this.toString().replace(/\B(?=(?:\d{3})+(?!\d))/g, ","); +} + + +/* + * Trim whitespace from a string + */ +String.prototype.trim = function () { + return this.replace(/^\s*/, "").replace(/\s*$/, ""); +} + +/*** +**** Preferences +***/ + +function Prefs() { } +Prefs.prototype = { }; + +Prefs._RefreshRate = 'refresh_rate'; + +Prefs._FilterMode = 'filter'; +Prefs._FilterAll = 'all'; +Prefs._FilterActive = 'active'; +Prefs._FilterSeeding = 'seeding'; +Prefs._FilterDownloading = 'downloading'; +Prefs._FilterPaused = 'paused'; +Prefs._FilterFinished = 'finished'; +// >> Vuze +Prefs._FilterComplete = 'complete'; +Prefs._FilterIncomplete = 'incomplete'; +// << Vuze + +Prefs._SortDirection = 'sort_direction'; +Prefs._SortAscending = 'ascending'; +Prefs._SortDescending = 'descending'; + +Prefs._SortMethod = 'sort_method'; +Prefs._SortByAge = 'age'; +Prefs._SortByActivity = 'activity'; +Prefs._SortByName = 'name'; +Prefs._SortByQueue = 'queue_order'; +Prefs._SortBySize = 'size'; +Prefs._SortByProgress = 'percent_completed'; +Prefs._SortByRatio = 'ratio'; +Prefs._SortByState = 'state'; + +Prefs._CompactDisplayState= 'compact_display_state'; + +Prefs._Defaults = +{ + /* >> Vuze */ + 'auto-start-torrents': true, + /* << Vuze */ + 'filter': 'all', + 'refresh_rate' : 15, + 'sort_direction': 'ascending', + 'sort_method': 'name', + 'turtle-state' : false, + 'compact_display_state' : false +}; + +/* + * Set a preference option + */ +Prefs.setValue = function(key, val) +{ + if (!(key in Prefs._Defaults)) + console.warn("unrecognized preference key '%s'", key); + + var date = new Date(); + date.setFullYear (date.getFullYear() + 1); + var valStr = JSON.stringify([ val ]); + document.cookie = key+"="+valStr+"; expires="+date.toGMTString()+"; path=/"; +}; + +/** + * Get a preference option + * + * @param key the preference's key + * @param fallback if the option isn't set, return this instead + */ +Prefs.getValue = function(key, fallback) +{ + var val; + + if (!(key in Prefs._Defaults)) + console.warn("unrecognized preference key '%s'", key); + + var lines = document.cookie.split(';'); + for (var i=0, len=lines.length; i= 48 && key <= 57 || + // Numeric keypad + key >= 96 && key <= 105 || + // comma, period and minus, . on keypad + key === 190 || key === 188 || key === 109 || key === 110 || + // 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; + }); + }); +} + +jQuery.fn.center = function () { + this.height("auto"); + if (this.outerHeight() > $(window).height()) { + this.outerHeight($(window).height()); + } + this.css("position","absolute"); + this.css("top", Math.max(0, (($(window).height() - $(this).outerHeight()) / 2) + + $(window).scrollTop()) + "px"); + this.css("left", Math.max(0, (($(window).width() - $(this).outerWidth()) / 2) + + $(window).scrollLeft()) + "px"); + return this; +} + +/** + * http://blog.stevenlevithan.com/archives/parseuri + * + * parseUri 1.2.2 + * (c) Steven Levithan + * MIT License + */ +function parseUri (str) { + var o = parseUri.options, + m = o.parser[o.strictMode ? "strict" : "loose"].exec(str), + uri = {}, + 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*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ + } +}; diff --git a/src/jsp/web/javascript/dialog.js b/src/jsp/web/javascript/dialog.js new file mode 100644 index 0000000..b607616 --- /dev/null +++ b/src/jsp/web/javascript/dialog.js @@ -0,0 +1,126 @@ +/* Transmission Revision 14025 */ +/** + * Copyright © Dave Perrett and Malcolm Jarvis + * + * This file is licensed under the GPLv2. + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ + +function Dialog(){ + this.initialize(); +} + +Dialog.prototype = { + + /* + * Constructor + */ + initialize: function() { + + /* + * Private Interface Variables + */ + 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_function = ''; + this._callback_data = null; + + // Observe the buttons + this._cancel_button.bind('click', {dialog: this}, this.onCancelClicked); + this._confirm_button.bind('click', {dialog: this}, this.onConfirmClicked); + }, + + + + + + /*-------------------------------------------- + * + * E V E N T F U N C T I O N S + * + *--------------------------------------------*/ + + hideDialog: function() + { + $('body.dialog_showing').removeClass('dialog_showing'); + this._container.hide(); + transmission.hideMobileAddressbar(); + transmission.updateButtonStates(); + }, + + onCancelClicked: function(event) + { + event.data.dialog.hideDialog(); + }, + + onConfirmClicked: function(event) + { + var dialog = event.data.dialog; + eval(dialog._callback_function + "(dialog._callback_data)"); + dialog.hideDialog(); + }, + + /*-------------------------------------------- + * + * I N T E R F A C E F U N C T I O N S + * + *--------------------------------------------*/ + + /* + * Display a confirm dialog + */ + confirm: function(dialog_heading, dialog_message, confirm_button_label, + callback_function, callback_data, cancel_button_label) + { + if (!isMobileDevice) + $('.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._cancel_button.button(); + this._confirm_button.button(); + + this._confirm_button.show(); + this._callback_function = callback_function; + this._callback_data = callback_data; + $('body').addClass('dialog_showing'); + $(document).scrollTop(this._container.offset()); + this._container.show(); + // >> Vuze: Add transmission var check because loadDaemonPrefs on init of transmission will show dialog + // on connection error, and 'transmission' isn't set yet.. + if (typeof transmission !== 'undefined') { + 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); + this._cancel_button.button(); + + // Just in case + $('#upload_container').hide(); + $('#move_container').hide(); + $('body').addClass('dialog_showing'); + transmission.updateButtonStates(); + if (isMobileDevice) + transmission.hideMobileAddressbar(); + this._container.show(); + } + + +} diff --git a/src/jsp/web/javascript/easyXDM-2.4.18.4.min.js b/src/jsp/web/javascript/easyXDM-2.4.18.4.min.js new file mode 100644 index 0000000..015768e --- /dev/null +++ b/src/jsp/web/javascript/easyXDM-2.4.18.4.min.js @@ -0,0 +1,24 @@ +/** + * easyXDM + * http://easyxdm.net/ + * Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +(function(N,d,p,K,k,H){var b=this;var n=Math.floor(Math.random()*10000);var q=Function.prototype;var Q=/^((http.?:)\/\/([^:\/\s]+)(:\d+)*)/;var R=/[\-\w]+\/\.\.\//;var F=/([^:])\/\//g;var I="";var o={};var M=N.easyXDM;var U="easyXDM_";var E;var y=false;var i;var h;function C(X,Z){var Y=typeof X[Z];return Y=="function"||(!!(Y=="object"&&X[Z]))||Y=="unknown"}function u(X,Y){return !!(typeof(X[Y])=="object"&&X[Y])}function r(X){return Object.prototype.toString.call(X)==="[object Array]"}function c(){var Z="Shockwave Flash",ad="application/x-shockwave-flash";if(!t(navigator.plugins)&&typeof navigator.plugins[Z]=="object"){var ab=navigator.plugins[Z].description;if(ab&&!t(navigator.mimeTypes)&&navigator.mimeTypes[ad]&&navigator.mimeTypes[ad].enabledPlugin){i=ab.match(/\d+/g)}}if(!i){var Y;try{Y=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");i=Array.prototype.slice.call(Y.GetVariable("$version").match(/(\d+),(\d+),(\d+),(\d+)/),1);Y=null}catch(ac){}}if(!i){return false}var X=parseInt(i[0],10),aa=parseInt(i[1],10);h=X>9&&aa>0;return true}var v,x;if(C(N,"addEventListener")){v=function(Z,X,Y){Z.addEventListener(X,Y,false)};x=function(Z,X,Y){Z.removeEventListener(X,Y,false)}}else{if(C(N,"attachEvent")){v=function(X,Z,Y){X.attachEvent("on"+Z,Y)};x=function(X,Z,Y){X.detachEvent("on"+Z,Y)}}else{throw new Error("Browser not supported")}}var W=false,J=[],L;if("readyState" in d){L=d.readyState;W=L=="complete"||(~navigator.userAgent.indexOf("AppleWebKit/")&&(L=="loaded"||L=="interactive"))}else{W=!!d.body}function s(){if(W){return}W=true;for(var X=0;X')}else{ac=d.createElement("IFRAME");ac.name=Y.props.name}ac.id=ac.name=Y.props.name;delete Y.props.name;if(typeof Y.container=="string"){Y.container=d.getElementById(Y.container)}if(!Y.container){T(ac.style,{position:"absolute",top:"-2000px",left:"0px"});Y.container=d.body}var ab=Y.props.src;Y.props.src="javascript:false";T(ac,Y.props);ac.border=ac.frameBorder=0;ac.allowTransparency=true;Y.container.appendChild(ac);if(Y.onLoad){v(ac,"load",Y.onLoad)}if(Y.usePost){var aa=Y.container.appendChild(d.createElement("form")),X;aa.target=ac.name;aa.action=ab;aa.method="POST";if(typeof(Y.usePost)==="object"){for(var Z in Y.usePost){if(Y.usePost.hasOwnProperty(Z)){if(E){X=d.createElement('')}else{X=d.createElement("INPUT");X.name=Z}X.value=Y.usePost[Z];aa.appendChild(X)}}}aa.submit();aa.parentNode.removeChild(aa)}else{ac.src=ab}Y.props.src=ab;return ac}function V(aa,Z){if(typeof aa=="string"){aa=[aa]}var Y,X=aa.length;while(X--){Y=aa[X];Y=new RegExp(Y.substr(0,1)=="^"?Y:("^"+Y.replace(/(\*)/g,".$1").replace(/\?/g,".")+"$"));if(Y.test(Z)){return true}}return false}function l(Z){var ae=Z.protocol,Y;Z.isHost=Z.isHost||t(S.xdm_p);y=Z.hash||false;if(!Z.props){Z.props={}}if(!Z.isHost){Z.channel=S.xdm_c.replace(/["'<>\\]/g,"");Z.secret=S.xdm_s;Z.remote=S.xdm_e.replace(/["'<>\\]/g,"");ae=S.xdm_p;if(Z.acl&&!V(Z.acl,Z.remote)){throw new Error("Access denied for "+Z.remote)}}else{Z.remote=B(Z.remote);Z.channel=Z.channel||"default"+n++;Z.secret=Math.random().toString(16).substring(2);if(t(ae)){if(j(p.href)==j(Z.remote)){ae="4"}else{if(C(N,"postMessage")||C(d,"postMessage")){ae="1"}else{if(Z.swf&&C(N,"ActiveXObject")&&c()){ae="6"}else{if(navigator.product==="Gecko"&&"frameElement" in N&&navigator.userAgent.indexOf("WebKit")==-1){ae="5"}else{if(Z.remoteHelper){ae="2"}else{ae="0"}}}}}}}Z.protocol=ae;switch(ae){case"0":T(Z,{interval:100,delay:2000,useResize:true,useParent:false,usePolling:false},true);if(Z.isHost){if(!Z.local){var ac=p.protocol+"//"+p.host,X=d.body.getElementsByTagName("img"),ad;var aa=X.length;while(aa--){ad=X[aa];if(ad.src.substring(0,ac.length)===ac){Z.local=ad.src;break}}if(!Z.local){Z.local=N}}var ab={xdm_c:Z.channel,xdm_p:0};if(Z.local===N){Z.usePolling=true;Z.useParent=true;Z.local=p.protocol+"//"+p.host+p.pathname+p.search;ab.xdm_e=Z.local;ab.xdm_pa=1}else{ab.xdm_e=B(Z.local)}if(Z.container){Z.useResize=false;ab.xdm_po=1}Z.remote=P(Z.remote,ab)}else{T(Z,{channel:S.xdm_c,remote:S.xdm_e,useParent:!t(S.xdm_pa),usePolling:!t(S.xdm_po),useResize:Z.useParent?false:Z.useResize})}Y=[new o.stack.HashTransport(Z),new o.stack.ReliableBehavior({}),new o.stack.QueueBehavior({encode:true,maxLength:4000-Z.remote.length}),new o.stack.VerifyBehavior({initiate:Z.isHost})];break;case"1":Y=[new o.stack.PostMessageTransport(Z)];break;case"2":Z.remoteHelper=B(Z.remoteHelper);Y=[new o.stack.NameTransport(Z),new o.stack.QueueBehavior(),new o.stack.VerifyBehavior({initiate:Z.isHost})];break;case"3":Y=[new o.stack.NixTransport(Z)];break;case"4":Y=[new o.stack.SameOriginTransport(Z)];break;case"5":Y=[new o.stack.FrameElementTransport(Z)];break;case"6":if(!i){c()}Y=[new o.stack.FlashTransport(Z)];break}Y.push(new o.stack.QueueBehavior({lazy:Z.lazy,remove:true}));return Y}function D(aa){var ab,Z={incoming:function(ad,ac){this.up.incoming(ad,ac)},outgoing:function(ac,ad){this.down.outgoing(ac,ad)},callback:function(ac){this.up.callback(ac)},init:function(){this.down.init()},destroy:function(){this.down.destroy()}};for(var Y=0,X=aa.length;Y<\/script>')}}};(function(){var X={};o.Fn={set:function(Y,Z){X[Y]=Z},get:function(Z,Y){var aa=X[Z];if(Y){delete X[Z]}return aa}}}());o.Socket=function(Y){var X=D(l(Y).concat([{incoming:function(ab,aa){Y.onMessage(ab,aa)},callback:function(aa){if(Y.onReady){Y.onReady(aa)}}}])),Z=j(Y.remote);this.origin=j(Y.remote);this.destroy=function(){X.destroy()};this.postMessage=function(aa){X.outgoing(aa,Z)};X.init()};o.Rpc=function(Z,Y){if(Y.local){for(var ab in Y.local){if(Y.local.hasOwnProperty(ab)){var aa=Y.local[ab];if(typeof aa==="function"){Y.local[ab]={method:aa}}}}}var X=D(l(Z).concat([new o.stack.RpcBehavior(this,Y),{callback:function(ac){if(Z.onReady){Z.onReady(ac)}}}]));this.origin=j(Z.remote);this.destroy=function(){X.destroy()};X.init()};o.stack.SameOriginTransport=function(Y){var Z,ab,aa,X;return(Z={outgoing:function(ad,ae,ac){aa(ad);if(ac){ac()}},destroy:function(){if(ab){ab.parentNode.removeChild(ab);ab=null}},onDOMReady:function(){X=j(Y.remote);if(Y.isHost){T(Y.props,{src:P(Y.remote,{xdm_e:p.protocol+"//"+p.host+p.pathname,xdm_c:Y.channel,xdm_p:4}),name:U+Y.channel+"_provider"});ab=A(Y);o.Fn.set(Y.channel,function(ac){aa=ac;K(function(){Z.up.callback(true)},0);return function(ad){Z.up.incoming(ad,X)}})}else{aa=m().Fn.get(Y.channel,true)(function(ac){Z.up.incoming(ac,X)});K(function(){Z.up.callback(true)},0)}},init:function(){G(Z.onDOMReady,Z)}})};o.stack.FlashTransport=function(aa){var ac,X,ab,ad,Y,ae;function af(ah,ag){K(function(){ac.up.incoming(ah,ad)},0)}function Z(ah){var ag=aa.swf+"?host="+aa.isHost;var aj="easyXDM_swf_"+Math.floor(Math.random()*10000);o.Fn.set("flash_loaded"+ah.replace(/[\-.]/g,"_"),function(){o.stack.FlashTransport[ah].swf=Y=ae.firstChild;var ak=o.stack.FlashTransport[ah].queue;for(var al=0;al
    "}return(ac={outgoing:function(ah,ai,ag){Y.postMessage(aa.channel,ah.toString());if(ag){ag()}},destroy:function(){try{Y.destroyChannel(aa.channel)}catch(ag){}Y=null;if(X){X.parentNode.removeChild(X);X=null}},onDOMReady:function(){ad=aa.remote;o.Fn.set("flash_"+aa.channel+"_init",function(){K(function(){ac.up.callback(true)})});o.Fn.set("flash_"+aa.channel+"_onMessage",af);aa.swf=B(aa.swf);var ah=z(aa.swf);var ag=function(){o.stack.FlashTransport[ah].init=true;Y=o.stack.FlashTransport[ah].swf;Y.createChannel(aa.channel,aa.secret,j(aa.remote),aa.isHost);if(aa.isHost){if(h&&aa.swfNoThrottle){T(aa.props,{position:"fixed",right:0,top:0,height:"20px",width:"20px"})}T(aa.props,{src:P(aa.remote,{xdm_e:j(p.href),xdm_c:aa.channel,xdm_p:6,xdm_s:aa.secret}),name:U+aa.channel+"_provider"});X=A(aa)}};if(o.stack.FlashTransport[ah]&&o.stack.FlashTransport[ah].init){ag()}else{if(!o.stack.FlashTransport[ah]){o.stack.FlashTransport[ah]={queue:[ag]};Z(ah)}else{o.stack.FlashTransport[ah].queue.push(ag)}}},init:function(){G(ac.onDOMReady,ac)}})};o.stack.PostMessageTransport=function(aa){var ac,ad,Y,Z;function X(ae){if(ae.origin){return j(ae.origin)}if(ae.uri){return j(ae.uri)}if(ae.domain){return p.protocol+"//"+ae.domain}throw"Unable to retrieve the origin of the event"}function ab(af){var ae=X(af);if(ae==Z&&af.data.substring(0,aa.channel.length+1)==aa.channel+" "){ac.up.incoming(af.data.substring(aa.channel.length+1),ae)}}return(ac={outgoing:function(af,ag,ae){Y.postMessage(aa.channel+" "+af,ag||Z);if(ae){ae()}},destroy:function(){x(N,"message",ab);if(ad){Y=null;ad.parentNode.removeChild(ad);ad=null}},onDOMReady:function(){Z=j(aa.remote);if(aa.isHost){var ae=function(af){if(af.data==aa.channel+"-ready"){Y=("postMessage" in ad.contentWindow)?ad.contentWindow:ad.contentWindow.document;x(N,"message",ae);v(N,"message",ab);K(function(){ac.up.callback(true)},0)}};v(N,"message",ae);T(aa.props,{src:P(aa.remote,{xdm_e:j(p.href),xdm_c:aa.channel,xdm_p:1}),name:U+aa.channel+"_provider"});ad=A(aa)}else{v(N,"message",ab);Y=("postMessage" in N.parent)?N.parent:N.parent.document;Y.postMessage(aa.channel+"-ready",Z);K(function(){ac.up.callback(true)},0)}},init:function(){G(ac.onDOMReady,ac)}})};o.stack.FrameElementTransport=function(Y){var Z,ab,aa,X;return(Z={outgoing:function(ad,ae,ac){aa.call(this,ad);if(ac){ac()}},destroy:function(){if(ab){ab.parentNode.removeChild(ab);ab=null}},onDOMReady:function(){X=j(Y.remote);if(Y.isHost){T(Y.props,{src:P(Y.remote,{xdm_e:j(p.href),xdm_c:Y.channel,xdm_p:5}),name:U+Y.channel+"_provider"});ab=A(Y);ab.fn=function(ac){delete ab.fn;aa=ac;K(function(){Z.up.callback(true)},0);return function(ad){Z.up.incoming(ad,X)}}}else{if(d.referrer&&j(d.referrer)!=S.xdm_e){N.top.location=S.xdm_e}aa=N.frameElement.fn(function(ac){Z.up.incoming(ac,X)});Z.up.callback(true)}},init:function(){G(Z.onDOMReady,Z)}})};o.stack.NameTransport=function(ab){var ac;var ae,ai,aa,ag,ah,Y,X;function af(al){var ak=ab.remoteHelper+(ae?"#_3":"#_2")+ab.channel;ai.contentWindow.sendMessage(al,ak)}function ad(){if(ae){if(++ag===2||!ae){ac.up.callback(true)}}else{af("ready");ac.up.callback(true)}}function aj(ak){ac.up.incoming(ak,Y)}function Z(){if(ah){K(function(){ah(true)},0)}}return(ac={outgoing:function(al,am,ak){ah=ak;af(al)},destroy:function(){ai.parentNode.removeChild(ai);ai=null;if(ae){aa.parentNode.removeChild(aa);aa=null}},onDOMReady:function(){ae=ab.isHost;ag=0;Y=j(ab.remote);ab.local=B(ab.local);if(ae){o.Fn.set(ab.channel,function(al){if(ae&&al==="ready"){o.Fn.set(ab.channel,aj);ad()}});X=P(ab.remote,{xdm_e:ab.local,xdm_c:ab.channel,xdm_p:2});T(ab.props,{src:X+"#"+ab.channel,name:U+ab.channel+"_provider"});aa=A(ab)}else{ab.remoteHelper=ab.remote;o.Fn.set(ab.channel,aj)}var ak=function(){var al=ai||this;x(al,"load",ak);o.Fn.set(ab.channel+"_load",Z);(function am(){if(typeof al.contentWindow.sendMessage=="function"){ad()}else{K(am,50)}}())};ai=A({props:{src:ab.local+"#_4"+ab.channel},onLoad:ak})},init:function(){G(ac.onDOMReady,ac)}})};o.stack.HashTransport=function(Z){var ac;var ah=this,af,aa,X,ad,am,ab,al;var ag,Y;function ak(ao){if(!al){return}var an=Z.remote+"#"+(am++)+"_"+ao;((af||!ag)?al.contentWindow:al).location=an}function ae(an){ad=an;ac.up.incoming(ad.substring(ad.indexOf("_")+1),Y)}function aj(){if(!ab){return}var an=ab.location.href,ap="",ao=an.indexOf("#");if(ao!=-1){ap=an.substring(ao)}if(ap&&ap!=ad){ae(ap)}}function ai(){aa=setInterval(aj,X)}return(ac={outgoing:function(an,ao){ak(an)},destroy:function(){N.clearInterval(aa);if(af||!ag){al.parentNode.removeChild(al)}al=null},onDOMReady:function(){af=Z.isHost;X=Z.interval;ad="#"+Z.channel;am=0;ag=Z.useParent;Y=j(Z.remote);if(af){T(Z.props,{src:Z.remote,name:U+Z.channel+"_provider"});if(ag){Z.onLoad=function(){ab=N;ai();ac.up.callback(true)}}else{var ap=0,an=Z.delay/50;(function ao(){if(++ap>an){throw new Error("Unable to reference listenerwindow")}try{ab=al.contentWindow.frames[U+Z.channel+"_consumer"]}catch(aq){}if(ab){ai();ac.up.callback(true)}else{K(ao,50)}}())}al=A(Z)}else{ab=N;ai();if(ag){al=parent;ac.up.callback(true)}else{T(Z,{props:{src:Z.remote+"#"+Z.channel+new Date(),name:U+Z.channel+"_consumer"},onLoad:function(){ac.up.callback(true)}});al=A(Z)}}},init:function(){G(ac.onDOMReady,ac)}})};o.stack.ReliableBehavior=function(Y){var aa,ac;var ab=0,X=0,Z="";return(aa={incoming:function(af,ad){var ae=af.indexOf("_"),ag=af.substring(0,ae).split(",");af=af.substring(ae+1);if(ag[0]==ab){Z="";if(ac){ac(true);ac=null}}if(af.length>0){aa.down.outgoing(ag[1]+","+ab+"_"+Z,ad);if(X!=ag[1]){X=ag[1];aa.up.incoming(af,ad)}}},outgoing:function(af,ad,ae){Z=af;ac=ae;aa.down.outgoing(X+","+(++ab)+"_"+af,ad)}})};o.stack.QueueBehavior=function(Z){var ac,ad=[],ag=true,aa="",af,X=0,Y=false,ab=false;function ae(){if(Z.remove&&ad.length===0){w(ac);return}if(ag||ad.length===0||af){return}ag=true;var ah=ad.shift();ac.down.outgoing(ah.data,ah.origin,function(ai){ag=false;if(ah.callback){K(function(){ah.callback(ai)},0)}ae()})}return(ac={init:function(){if(t(Z)){Z={}}if(Z.maxLength){X=Z.maxLength;ab=true}if(Z.lazy){Y=true}else{ac.down.init()}},callback:function(ai){ag=false;var ah=ac.up;ae();ah.callback(ai)},incoming:function(ak,ai){if(ab){var aj=ak.indexOf("_"),ah=parseInt(ak.substring(0,aj),10);aa+=ak.substring(aj+1);if(ah===0){if(Z.encode){aa=k(aa)}ac.up.incoming(aa,ai);aa=""}}else{ac.up.incoming(ak,ai)}},outgoing:function(al,ai,ak){if(Z.encode){al=H(al)}var ah=[],aj;if(ab){while(al.length!==0){aj=al.substring(0,X);al=al.substring(aj.length);ah.push(aj)}while((aj=ah.shift())){ad.push({data:ah.length+"_"+aj,origin:ai,callback:ah.length===0?ak:null})}}else{ad.push({data:al,origin:ai,callback:ak})}if(Y){ac.down.init()}else{ae()}},destroy:function(){af=true;ac.down.destroy()}})};o.stack.VerifyBehavior=function(ab){var ac,aa,Y,Z=false;function X(){aa=Math.random().toString(16).substring(2);ac.down.outgoing(aa)}return(ac={incoming:function(af,ad){var ae=af.indexOf("_");if(ae===-1){if(af===aa){ac.up.callback(true)}else{if(!Y){Y=af;if(!ab.initiate){X()}ac.down.outgoing(af)}}}else{if(af.substring(0,ae)===Y){ac.up.incoming(af.substring(ae+1),ad)}}},outgoing:function(af,ad,ae){ac.down.outgoing(aa+"_"+af,ad,ae)},callback:function(ad){if(ab.initiate){X()}}})};o.stack.RpcBehavior=function(ad,Y){var aa,af=Y.serializer||O();var ae=0,ac={};function X(ag){ag.jsonrpc="2.0";aa.down.outgoing(af.stringify(ag))}function ab(ag,ai){var ah=Array.prototype.slice;return function(){var aj=arguments.length,al,ak={method:ai};if(aj>0&&typeof arguments[aj-1]==="function"){if(aj>1&&typeof arguments[aj-2]==="function"){al={success:arguments[aj-2],error:arguments[aj-1]};ak.params=ah.call(arguments,0,aj-2)}else{al={success:arguments[aj-1]};ak.params=ah.call(arguments,0,aj-1)}ac[""+(++ae)]=al;ak.id=ae}else{ak.params=ah.call(arguments,0)}if(ag.namedParams&&ak.params.length===1){ak.params=ak.params[0]}X(ak)}}function Z(an,am,ai,al){if(!ai){if(am){X({id:am,error:{code:-32601,message:"Procedure not found."}})}return}var ak,ah;if(am){ak=function(ao){ak=q;X({id:am,result:ao})};ah=function(ao,ap){ah=q;var aq={id:am,error:{code:-32099,message:ao}};if(ap){aq.error.data=ap}X(aq)}}else{ak=ah=q}if(!r(al)){al=[al]}try{var ag=ai.method.apply(ai.scope,al.concat([ak,ah]));if(!t(ag)){ak(ag)}}catch(aj){ah(aj.message)}}return(aa={incoming:function(ah,ag){var ai=af.parse(ah);if(ai.method){if(Y.handle){Y.handle(ai,X)}else{Z(ai.method,ai.id,Y.local[ai.method],ai.params)}}else{var aj=ac[ai.id];if(ai.error){if(aj.error){aj.error(ai.error)}}else{if(aj.success){aj.success(ai.result)}}delete ac[ai.id]}},init:function(){if(Y.remote){for(var ag in Y.remote){if(Y.remote.hasOwnProperty(ag)){ad[ag]=ab(Y.remote[ag],ag)}}}aa.down.init()},destroy:function(){for(var ag in Y.remote){if(Y.remote.hasOwnProperty(ag)&&ad.hasOwnProperty(ag)){delete ad[ag]}}aa.down.destroy()}})};b.easyXDM=o})(window,document,location,window.setTimeout,decodeURIComponent,encodeURIComponent); \ No newline at end of file diff --git a/src/jsp/web/javascript/easyxdm-2.4.18.4.swf b/src/jsp/web/javascript/easyxdm-2.4.18.4.swf new file mode 100644 index 0000000000000000000000000000000000000000..eafb2695c2bb61a1761e82a90e827c5a7a90f2d7 GIT binary patch literal 1770 zcmVOmEzIcY2_J3;sjL!6}Z+(F7?n$zoCbUn@4D#LYc5ioo zySI02L*`e2Y#ZRz5ahKt_;+9!@NaDhKX*qb@r$FiYIV)3eP=aAFtcjeF1#5%JVsJK z{GR#-7^yZLZ_{$1?R(ObJ8c1vEyu1~Qh+1errd(smgVQYx>%6VB8_6H_^7zBQha)U z397#5NpJ(D4t0+u+MX|=W;xC#8b_t=RS^W}#ERN55N>_dYD3L)T~U*^=hCe0i-x@o zFaw@%H*uHQS#Z22NWUY%3|=(mOb}ME^JHmtW=U*znlsY3Y62RL6>Ndnmcn;2j{D)D z=;CjK1jrE(uC?jdZquxJtyagiYZloxU)XZXg!P?3iWd4+>-e_Zfh{T9bAW2zizO8i zkBcr#h@~jXK!$;VRj=oxc#$uR%Ym9N&BAiJg!*}}hVsk4)ock@2C(8`?D?4Gc*8#8 zki`0nMlwt_Mz*E5E`5{-Oi&o7FKWVmF6w4W0cOMZS|&`JV!Mr7;(`L&9n48A+^{hr zxT5@mjSbjP>!LlK@B5y96v9ySCo4?=wN6ioD5~vB-I_G zK9ar&n(ekb63ii5&#9(f>>N;;pl_x`dmMxwijekb zm?cy;GU=f*%?y@2P(s7ZXQ-O0FkfBtC>A=nkC2@d8rer^DJgVlAEDDpp~HI!DNZ;Z zTr@^A_kISLr{P$J>MYOW@o0>39*HAVRN!y|shEI);PLxTf#oOr!Q%I5AsJaA8G#Z% z7S4~cc(#Z}#>1M|F5>4E)k61hl%-bCglj;G@9G+FcZAiC7bsSZVm-mYd%Rzczb7kM zwsk7uNW{#s+k10=ngO#kkUjjKDaJIMeZ^ej=v*b+J%J8`jxI(QODb%>FbTZ_(P=Zr zXfIu>WP52)EaReaK9kzp_4je-Sv;h4xZ^8J!wb$A5(Y1(=+5~qO^O$CdsoO&tPu1@ zdrMs)Qy-xIAV(u$Oc@u|{1R(@SfLQe@4lp3nXb%JP?r@pm5`_W& zO663z-O1iC7&FE!dxgksfl2I-jVrl0Emz~TTvH|X-_)+fYF7)YYvMt;&dw~(6Fi>l z^Gc}k(LL3WaZgYU6obAIt<6(>H+iP(#t2ek(BLfoJJ%hjdZuCtpJI1pQ+N6fa>LV&Tiq$VO!%sZ$}>f8fv|@3_|C)o zhp!DxX@e7Xflu9>o?_xY;sGL!Hc zqnqS~)m;m;lX9cfos9ab?KZsf<}-R+Wto#Qa3qaar1jyEK_^0xL?&~nLnk9lQ#zR@wm!;9{UXWiw{OZ}(*KvN58M@x-*#8T&zb11 zxCU|7bZsshH?MJpu;Q1>vGG_aY3|8e%+dN+1BIo6ds;}oLU{7^zZUkGTGsR-WpU#+ zWs_f8P=>pyMFfAyA+Y*rVLe}6T_~17ZrOqQ4>Gq=ew|Uz%Z3l=$fOqDb6KG0?cV?Z M0RR630E2!MA%=Ewi2wiq literal 0 HcmV?d00001 diff --git a/src/jsp/web/javascript/file-row.js b/src/jsp/web/javascript/file-row.js new file mode 100644 index 0000000..1281de4 --- /dev/null +++ b/src/jsp/web/javascript/file-row.js @@ -0,0 +1,213 @@ +/* Transmission Revision 14025 */ +/** + * Copyright © Mnemosyne LLC + * + * This file is licensed under the GPLv2. + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ + +function FileRow(torrent, depth, name, indices, even) +{ + var fields = { + have: 0, + indices: [], + isWanted: true, + priorityLow: false, + priorityNormal: false, + priorityHigh: false, + me: this, + size: 0, + torrent: null + }, + + elements = { + priority_low_button: null, + priority_normal_button: null, + priority_high_button: null, + progress: null, + root: null + }, + + initialize = function(torrent, depth, name, indices, even) { + fields.torrent = torrent; + fields.indices = indices; + createRow(torrent, depth, name, even); + }, + + refreshWantedHTML = function() + { + var e = $(elements.root); + e.toggleClass('skip', !fields.isWanted); + e.toggleClass('complete', isDone()); + $(e[0].checkbox).prop('disabled', !isEditable()); + $(e[0].checkbox).prop('checked', fields.isWanted); + }, + refreshProgressHTML = function() + { + var pct = 100 * (fields.size ? (fields.have / fields.size) : 1.0), + c = [ Transmission.fmt.size(fields.have), + ' of ', + Transmission.fmt.size(fields.size), + ' (', + Transmission.fmt.percentString(pct), + '%)' ].join(''); + setTextContent(elements.progress, c); + }, + refreshImpl = function() { + var i, + file, + have = 0, + size = 0, + wanted = false, + low = false, + normal = false, + high = false; + + // loop through the file_indices that affect this row + for (i=0; i= fields.size; + }, + isEditable = function () { + return (fields.torrent.getFileCount()>1) && !isDone(); + }, + + createRow = function(torrent, depth, name, even) { + var e, root, box; + + root = document.createElement('li'); + root.className = 'inspector_torrent_file_list_entry' + (even?'even':'odd'); + elements.root = root; + + e = document.createElement('input'); + e.type = 'checkbox'; + e.className = "file_wanted_control"; + e.title = 'Download file'; + $(e).change(function(ev){ fireWantedChanged( $(ev.currentTarget).prop('checked')); }); + root.checkbox = e; + root.appendChild(e); + + e = document.createElement('div'); + e.className = 'file-priority-radiobox'; + box = e; + + e = document.createElement('div'); + e.className = 'low'; + e.title = 'Low Priority'; + $(e).click(function(){ firePriorityChanged(-1); }); + elements.priority_low_button = e; + box.appendChild(e); + + e = document.createElement('div'); + e.className = 'normal'; + e.title = 'Normal Priority'; + $(e).click(function(){ firePriorityChanged(0); }); + elements.priority_normal_button = e; + box.appendChild(e); + + e = document.createElement('div'); + e.title = 'High Priority'; + e.className = 'high'; + $(e).click(function(){ firePriorityChanged(1); }); + elements.priority_high_button = e; + box.appendChild(e); + + root.appendChild(box); + + e = document.createElement('div'); + e.className = "inspector_torrent_file_list_entry_name"; + setTextContent(e, name); + $(e).click(function(){ fireNameClicked(-1); }); + root.appendChild(e); + + e = document.createElement('div'); + e.className = "inspector_torrent_file_list_entry_progress"; + root.appendChild(e); + $(e).click(function(){ fireNameClicked(-1); }); + elements.progress = e; + + $(root).css('margin-left', '' + (depth*16) + 'px'); + + refreshImpl(); + return root; + }, + + fireWantedChanged = function(do_want) { + // >> Vuze: Show user they changed it (refresh will set it to the correct value) + var e = $(elements.root); + $(e[0].checkbox).prop('checked', do_want); + // << Vuze + $(fields.me).trigger('wantedToggled',[ fields.indices, do_want ]); + }, + firePriorityChanged = function(priority) { + // >> Vuze: Select priority user just clicked. 2 items will temporarily + // be selected until the refresh kicks in + if (priority == -1) { + $(elements.priority_low_button).toggleClass('selected', true); + } + + if (priority == 0) { + $(elements.priority_normal_button).toggleClass('selected', true); + } + + if (priority == 1) { + $(elements.priority_high_button).toggleClass('selected', true); + } + // << Vuze + $(fields.me).trigger('priorityToggled',[ fields.indices, priority ]); + }, + fireNameClicked = function() { + $(fields.me).trigger('nameClicked',[ fields.me, fields.indices ]); + }; + + /*** + **** PUBLIC + ***/ + + this.getElement = function() { + return elements.root; + }; + this.refresh = function() { + refreshImpl(); + }; + + initialize(torrent, depth, name, indices, even); +}; diff --git a/src/jsp/web/javascript/formatter.js b/src/jsp/web/javascript/formatter.js new file mode 100644 index 0000000..b9946b7 --- /dev/null +++ b/src/jsp/web/javascript/formatter.js @@ -0,0 +1,428 @@ +/* Transmission Revision 14025 */ +/** + * Copyright © Mnemosyne LLC + * + * This file is licensed under the GPLv2. + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ + +Transmission.fmt = (function() +{ + var speed_K = 1000; + var speed_B_str = 'B/s'; + // Vuze: k should be lowercase + var speed_K_str = 'kB/s'; + var speed_M_str = 'MB/s'; + var speed_G_str = 'GB/s'; + var speed_T_str = 'TB/s'; + + var size_K = 1000; + var size_B_str = 'B'; + // Vuze: k should be lowercase + var size_K_str = 'kB'; + var size_M_str = 'MB'; + var size_G_str = 'GB'; + var size_T_str = 'TB'; + + var mem_K = 1024; + var mem_B_str = 'B'; + var mem_K_str = 'KiB'; + var mem_M_str = 'MiB'; + var mem_G_str = 'GiB'; + var mem_T_str = 'TiB'; + + return { + + updateUnits: function(u) + { +/* + speed_K = u['speed-bytes']; + speed_K_str = u['speed-units'][0]; + speed_M_str = u['speed-units'][1]; + speed_G_str = u['speed-units'][2]; + speed_T_str = u['speed-units'][3]; + + size_K = u['size-bytes']; + size_K_str = u['size-units'][0]; + size_M_str = u['size-units'][1]; + size_G_str = u['size-units'][2]; + size_T_str = u['size-units'][3]; + + mem_K = u['memory-bytes']; + mem_K_str = u['memory-units'][0]; + mem_M_str = u['memory-units'][1]; + mem_G_str = u['memory-units'][2]; + mem_T_str = u['memory-units'][3]; +*/ + }, + + /* + * 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 ratio to a string + */ + ratioString: function(x) { + if (x === -1) + return "None"; + if (x === -2) + return '∞'; + return this.percentString(x); + }, + + /** + * Formats the a memory size into a human-readable string + * @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; + var unit; + + if (bytes < Math.pow(mem_K, 2)) + { + 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; + } + + // 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(' '); + + var convertedSize; + var unit; + + if (bytes < Math.pow(size_K, 2)) + { + 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; + } + + // 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(' '); + }, + + speedBps: function(Bps) + { + return this.speed(this.toKBps(Bps)); + }, + + toKBps: function(Bps) + { + return Math.floor(Bps / speed_K); + }, + + speed: function(KBps) + { + var speed = KBps; + + if (speed <= 999.95) // 0 KBps to 999 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(' '); + + // insane speeds + speed /= speed_K; + return [ speed.toTruncFixed(2), speed_G_str ].join(' '); + }, + + timeInterval: function(seconds) + { + var days = Math.floor (seconds / 86400), + hours = Math.floor ((seconds % 86400) / 3600), + 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("" + flag + ""); + } + } + return formattedFlags.join(''); + } + } +})(); + + +/* + * Converts file & folder byte size values to more + * readable values (bytes, KB, MB, GB or TB). + * + * @param integer bytes + * @returns string + */ +Math.formatBytes = function(bytes) { + var size; + var unit; + + // Terabytes (TB). + if ( bytes >= 1099511627776 ) { + size = bytes / 1099511627776; + unit = ' TB'; + + // Gigabytes (GB). + } else if ( bytes >= 1073741824 ) { + size = bytes / 1073741824; + unit = ' GB'; + + // Megabytes (MB). + } else if ( bytes >= 1048576 ) { + size = bytes / 1048576; + unit = ' MB'; + + // Kilobytes (KB). + } else if ( bytes >= 1024 ) { + size = bytes / 1024; + unit = ' KB'; + + // The file is less than one KB + } else { + size = bytes; + unit = ' bytes'; + } + + // Single-digit numbers have greater precision + var precision = 1; + if (size < 10) { + precision = 2; + } + size = Math.roundWithPrecision(size, precision); + + // Add the decimal if this is an integer + if ((size % 1) == 0 && unit != ' bytes') { + size = size + '.0'; + } + + return size + unit; +}; + + +/* + * Converts seconds to more readable units (hours, minutes etc). + * + * @param integer seconds + * @returns string + */ +Math.formatSeconds = function(seconds) +{ + var result; + var days = Math.floor(seconds / 86400); + var hours = Math.floor((seconds % 86400) / 3600); + var minutes = Math.floor((seconds % 3600) / 60); + var seconds = Math.floor((seconds % 3600) % 60); + + if (days > 0 && hours == 0) + result = days + ' days'; + else if (days > 0 && hours > 0) + result = days + ' days ' + hours + ' hr'; + else if (hours > 0 && minutes == 0) + result = hours + ' hr'; + else if (hours > 0 && minutes > 0) + result = hours + ' hr ' + minutes + ' min'; + else if (minutes > 0 && seconds == 0) + result = minutes + ' min'; + else if (minutes > 0 && seconds > 0) + result = minutes + ' min ' + seconds + ' seconds'; + else + result = seconds + ' seconds'; + + return result; +}; + + +/* + * Converts a unix timestamp to a human readable value + * + * @param integer seconds + * @returns string + */ +Math.formatTimestamp = function(seconds) { + var myDate = new Date(seconds*1000); + return myDate.toGMTString(); +}; + +/* + * Round a float to a specified number of decimal + * places, stripping trailing zeroes + * + * @param float floatnum + * @param integer precision + * @returns float + */ +Math.roundWithPrecision = function(floatnum, precision) { + return Math.round ( floatnum * Math.pow ( 10, precision ) ) / Math.pow ( 10, precision ); +}; + + diff --git a/src/jsp/web/javascript/inspector.js b/src/jsp/web/javascript/inspector.js new file mode 100644 index 0000000..949414c --- /dev/null +++ b/src/jsp/web/javascript/inspector.js @@ -0,0 +1,883 @@ +/** + * Copyright © Jordan Lee, Dave Perrett, Malcolm Jarvis and Bruno Bierbaumer + * + * This file is licensed under the GPLv2. + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ + +function Inspector(controller) { + + var data = { + controller: null, + elements: { }, + torrents: [ ] + }, + + needsExtraInfo = function (torrents) { + var i, id, tor; + + for (i = 0; tor = torrents[i]; i++) + if (!tor.hasExtraInfo()) + return true; + + return false; + }, + + refreshTorrents = function () { + var fields, + ids = $.map(data.torrents.slice(0), function (t) {return t.getId();}); + + if (ids && ids.length) + { + fields = ['id'].concat(Torrent.Fields.StatsExtra); + + if (needsExtraInfo(data.torrents)) + $.merge(fields, Torrent.Fields.InfoExtra); + + data.controller.updateTorrents(ids, fields); + } + }, + + onTabClicked = function (ev) { + var tab = ev.currentTarget; + + ev.stopPropagation(); + ev.preventDefault(); + // ev.preventDefault doesn't always cancel click event.. + // ignore clicks if tap has been pressed once + if (!this.skipClick) { + if (ev.type == 'tap') { + this.skipClick = true; + } + } else { + if (ev.type == 'click') { + return; + } + } + + // select this tab and deselect the others + $(tab).siblings().removeClass('selected'); + $(tab).addClass('selected'); + + // show this tab and hide the others + $('#' + tab.id.replace('tab','page')).show().siblings('.inspector-page').hide(); + + vz.torrentInfoShown(data.torrents[0].getId(), "Inspector-" + $(tab)[0].title); + updateInspector(); + }, + + updateInspector = function () { + var e = data.elements, + torrents = data.torrents, + name; + + // update the name, which is shown on all the pages + if (!torrents || !torrents.length) + name = 'No Selection'; + else if(torrents.length === 1) + name = torrents[0].getName(); + else + name = '' + torrents.length+' Transfers Selected'; + setTextContent(e.name_lb, name || na); + + // update the visible page + if ($(e.info_page).is(':visible')) + updateInfoPage(); + else if ($(e.peers_page).is(':visible')) + updatePeersPage(); + else if ($(e.trackers_page).is(':visible')) + updateTrackersPage(); + else if ($(e.files_page).is(':visible')) + updateFilesPage(); + }, + + /**** + ***** GENERAL INFO PAGE + ****/ + + updateInfoPage = function () { + var torrents = data.torrents, + e = data.elements, + fmt = Transmission.fmt, + none = 'None', + mixed = 'Mixed', + unknown = 'Unknown', + isMixed, allPaused, allFinished, + str, + baseline, it, s, i, t, + sizeWhenDone = 0, + leftUntilDone = 0, + available = 0, + haveVerified = 0, + haveUnverified = 0, + verifiedPieces = 0, + stateString, + latest, + pieces, + size, + pieceSize, + creator, mixed_creator, + date, mixed_date, + v, u, f, d, pct, + now = Date.now(); + + // + // state_lb + // + + if(torrents.length <1) + str = none; + else { + isMixed = false; + allPaused = true; + allFinished = true; + + baseline = torrents[0].getStatus(); + for(i=0; t=torrents[i]; ++i) { + it = t.getStatus(); + if(it != baseline) + isMixed = true; + if(!t.isStopped()) + allPaused = allFinished = false; + if(!t.isFinished()) + allFinished = false; + } + if( isMixed ) + str = mixed; + else if( allFinished ) + str = 'Finished'; + else if( allPaused ) + str = 'Paused'; + else + str = torrents[0].getStateString(); + } + setTextContent(e.state_lb, str); + stateString = str; + + // + // have_lb + // + + if(torrents.length < 1) + str = none; + else { + baseline = torrents[0].getStatus(); + for(i=0; t=torrents[i]; ++i) { + if(!t.needsMetaData()) { + haveUnverified += t.getHaveUnchecked(); + v = t.getHaveValid(); + haveVerified += v; + if(t.getPieceSize()) + verifiedPieces += v / t.getPieceSize(); + sizeWhenDone += t.getSizeWhenDone(); + leftUntilDone += t.getLeftUntilDone(); + available += (t.getHave()) + t.getDesiredAvailable(); + } + } + + d = 100.0 * ( sizeWhenDone ? ( sizeWhenDone - leftUntilDone ) / sizeWhenDone : 1 ); + str = fmt.percentString( d ); + + if( !haveUnverified && !leftUntilDone ) + str = fmt.size(haveVerified) + ' (100%)'; + else if( !haveUnverified ) + str = fmt.size(haveVerified) + ' of ' + fmt.size(sizeWhenDone) + ' (' + str +'%)'; + else + str = fmt.size(haveVerified) + ' of ' + fmt.size(sizeWhenDone) + ' (' + str +'%), ' + fmt.size(haveUnverified) + ' Unverified'; + } + setTextContent(e.have_lb, str); + + // + // availability_lb + // + + if(torrents.length < 1) + str = none; + else if( sizeWhenDone == 0 ) + str = none; + else + str = '' + fmt.percentString( ( 100.0 * available ) / sizeWhenDone ) + '%'; + setTextContent(e.availability_lb, str); + + // + // downloaded_lb + // + + if(torrents.length < 1) + str = none; + else { + d = f = 0; + for(i=0; t=torrents[i]; ++i) { + d += t.getDownloadedEver(); + f += t.getFailedEver(); + } + if(f) + str = fmt.size(d) + ' (' + fmt.size(f) + ' corrupt)'; + else + str = fmt.size(d); + } + setTextContent(e.downloaded_lb, str); + + // + // uploaded_lb + // + + if(torrents.length < 1) + str = none; + else { + d = u = 0; + if(torrents.length == 1) { + d = torrents[0].getDownloadedEver(); + u = torrents[0].getUploadedEver(); + + if (d == 0) + d = torrents[0].getHaveValid(); + } + else { + for(i=0; t=torrents[i]; ++i) { + d += t.getDownloadedEver(); + u += t.getUploadedEver(); + } + } + str = fmt.size(u) + ' (Ratio: ' + fmt.ratioString( Math.ratio(u,d))+')'; + } + setTextContent(e.uploaded_lb, str); + + // + // running time + // + + if(torrents.length < 1) + str = none; + else { + allPaused = true; + baseline = torrents[0].getStartDate(); + for(i=0; t=torrents[i]; ++i) { + if(baseline != t.getStartDate()) + baseline = 0; + if(!t.isStopped()) + allPaused = false; + } + if(allPaused) + str = stateString; // paused || finished + else if(!baseline) + str = mixed; + else + str = fmt.timeInterval(now/1000 - baseline); + } + setTextContent(e.running_time_lb, str); + + // + // remaining time + // + + str = ''; + if(torrents.length < 1) + str = none; + else { + baseline = torrents[0].getETA(); + for(i=0; t=torrents[i]; ++i) { + if(baseline != t.getETA()) { + str = mixed; + break; + } + } + } + if(!str.length) { + if(baseline < 0) + str = unknown; + else + str = fmt.timeInterval(baseline); + } + setTextContent(e.remaining_time_lb, str); + + // + // last activity + // + + latest = -1; + if(torrents.length < 1) + str = none; + else { + baseline = torrents[0].getLastActivity(); + for(i=0; t=torrents[i]; ++i) { + d = t.getLastActivity(); + if(latest < d) + latest = d; + } + d = now/1000 - latest; // seconds since last activity + if(d < 0) + str = none; + else if(d < 5) + str = 'Active now'; + else + str = fmt.timeInterval(d) + ' ago'; + } + setTextContent(e.last_activity_lb, str); + + // + // error + // + + if(torrents.length < 1) + str = none; + else { + str = torrents[0].getErrorString(); + for(i=0; t=torrents[i]; ++i) { + if(str != t.getErrorString()) { + str = mixed; + break; + } + } + } + setTextContent(e.error_lb, str || none); + + // + // size + // + + if(torrents.length < 1) + str = none; + else { + pieces = 0; + size = 0; + pieceSize = torrents[0].getPieceSize(); + for(i=0; t=torrents[i]; ++i) { + pieces += t.getPieceCount(); + size += t.getTotalSize(); + if(pieceSize != t.getPieceSize()) + pieceSize = 0; + } + if(!size) + str = none; + else if(pieceSize > 0) + str = fmt.size(size) + ' (' + pieces.toStringWithCommas() + ' pieces @ ' + fmt.mem(pieceSize) + ')'; + else + str = fmt.size(size) + ' (' + pieces.toStringWithCommas() + ' pieces)'; + } + setTextContent(e.size_lb, str); + + // + // hash + // + + if(torrents.length < 1) + str = none; + else { + str = torrents[0].getHashString(); + for(i=0; t=torrents[i]; ++i) { + if(str != t.getHashString()) { + str = mixed; + break; + } + } + } + setTextContent(e.hash_lb, str); + + // + // privacy + // + + if(torrents.length < 1) + str = none; + else { + baseline = torrents[0].getPrivateFlag(); + str = baseline ? 'Private to this tracker -- DHT and PEX disabled' : 'Public torrent'; + for(i=0; t=torrents[i]; ++i) { + if(baseline != t.getPrivateFlag()) { + str = mixed; + break; + } + } + } + setTextContent(e.privacy_lb, str); + + // + // comment + // + + if(torrents.length < 1) + str = none; + else { + str = torrents[0].getComment(); + for(i=0; t=torrents[i]; ++i) { + if(str != t.getComment()) { + str = mixed; + break; + } + } + } + if(!str) + str = none; + setTextContent(e.comment_lb, str); + + /* >> Vuze: Tags */ + if(torrents.length < 1) + str = none; + else { + if (transmission.tags === undefined) { + str = 'Loading..'; + transmission.updateTagList(updateInfoPage); + } else { + str = ''; + var first = true; + tagUIDS = torrents[0].getTagUIDs(); + if (tagUIDS !== undefined) { + tagUIDS.forEach(function(uid) { + if (first) { + first = false; + } else { + str += ", "; + } + if (transmission.tags[uid] === undefined) { + str += uid; + } else { + str += transmission.tags[uid].name; + } + }); + } + } + } + setTextContent(e.tags_lb, str); + /* << Vuze */ + + // + // origin + // + + if(torrents.length < 1) + str = none; + else { + mixed_creator = false; + mixed_date = false; + creator = torrents[0].getCreator(); + date = torrents[0].getDateCreated(); + for(i=0; t=torrents[i]; ++i) { + if(creator != t.getCreator()) + mixed_creator = true; + if(date != t.getDateCreated()) + mixed_date = true; + } + if(mixed_creator && mixed_date) + str = mixed; + else if(mixed_date && creator.length) + str = 'Created by ' + creator; + else if(mixed_creator && date) + str = 'Created on ' + (new Date(date*1000)).toDateString(); + else + str = 'Created by ' + creator + ' on ' + (new Date(date*1000)).toDateString(); + } + setTextContent(e.origin_lb, str); + + // + // foldername + // + + if(torrents.length < 1) + str = none; + else { + str = torrents[0].getDownloadDir(); + for(i=0; t=torrents[i]; ++i) { + if(str != t.getDownloadDir()) { + str = mixed; + break; + } + } + } + setTextContent(e.foldername_lb, str); + }, + + /**** + ***** FILES PAGE + ****/ + + changeFileCommand = function(fileIndices, command) { + var torrentId = data.file_torrent.getId(); + data.controller.changeFileCommand(torrentId, fileIndices, command); + }, + + onFileWantedToggled = function(ev, fileIndices, want) { + changeFileCommand(fileIndices, want?'files-wanted':'files-unwanted'); + }, + + onFilePriorityToggled = function(ev, fileIndices, priority) { + var command; + switch(priority) { + case -1: command = 'priority-low'; break; + case 1: command = 'priority-high'; break; + default: command = 'priority-normal'; break; + } + changeFileCommand(fileIndices, command); + }, + + onNameClicked = function(ev, fileRow, fileIndices) { + $(fileRow.getElement()).siblings().slideToggle(); + }, + + clearFileList = function() { + $(data.elements.file_list).empty(); + delete data.file_torrent; + delete data.file_torrent_n; + delete data.file_rows; + }, + + createFileTreeModel = function (tor) { + var i, j, n, name, tokens, walk, tree, token, sub, + leaves = [ ], + tree = { children: { }, file_indices: [ ] }; + + n = tor.getFileCount(); + for (i=0; i'); + if (torrents.length > 1) { + html.push('
    ', sanitizeText(tor.getName()), '
    '); + } + if (!peers || !peers.length) { + html.push('
    '); // firefox won't paint the top border if the div is empty + continue; + } + html.push('', + '', + '', + '', + '', + '', + '', + '', + '', + ''); + for (i=0; peer=peers[i]; ++i) { + parity = (i%2) ? 'odd' : 'even'; + html.push('', + '', + '', + '', + '', + '', + '', + '', + ''); + } + html.push('
    UpDown%StatusAddressClient
    ', (peer.isEncrypted ? '
    ' + : '
    '), '
    ', '
    ', (peer.rateToPeer ? fmt.speedBps(peer.rateToPeer) : ''), '', (peer.rateToClient ? fmt.speedBps(peer.rateToClient) : ''), '', Math.floor(peer.progress*100), '%', '', fmt.peerStatus(peer.flagStr), '', sanitizeText(peer.address), '', sanitizeText(peer.clientName), '
    '); + } + + setInnerHTML(peers_list, html.join('')); + }, + + /**** + ***** TRACKERS PAGE + ****/ + + getAnnounceState = function(tracker) { + var timeUntilAnnounce, s = ''; + switch (tracker.announceState) { + case Torrent._TrackerActive: + s = 'Announce in progress'; + break; + case Torrent._TrackerWaiting: + timeUntilAnnounce = tracker.nextAnnounceTime - ((new Date()).getTime() / 1000); + if (timeUntilAnnounce < 0) { + timeUntilAnnounce = 0; + } + s = 'Next announce in ' + Transmission.fmt.timeInterval(timeUntilAnnounce); + break; + case Torrent._TrackerQueued: + s = 'Announce is queued'; + break; + case Torrent._TrackerInactive: + s = tracker.isBackup ? + 'Tracker will be used as a backup' : + 'Announce not scheduled'; + break; + default: + s = 'unknown announce state: ' + tracker.announceState; + } + return s; + }, + + lastAnnounceStatus = function(tracker) { + + var lastAnnounceLabel = 'Last Announce', + lastAnnounce = [ 'N/A' ], + lastAnnounceTime; + + if (tracker.hasAnnounced) { + lastAnnounceTime = Transmission.fmt.timestamp(tracker.lastAnnounceTime); + if (tracker.lastAnnounceSucceeded) { + lastAnnounce = [ lastAnnounceTime, ' (got ', Transmission.fmt.countString('peer','peers',tracker.lastAnnouncePeerCount), ')' ]; + } else { + lastAnnounceLabel = 'Announce error'; + lastAnnounce = [ (tracker.lastAnnounceResult ? (tracker.lastAnnounceResult + ' - ') : ''), lastAnnounceTime ]; + } + } + return { 'label':lastAnnounceLabel, 'value':lastAnnounce.join('') }; + }, + + lastScrapeStatus = function(tracker) { + + var lastScrapeLabel = 'Last Scrape', + lastScrape = 'N/A', + lastScrapeTime; + + if (tracker.hasScraped) { + lastScrapeTime = Transmission.fmt.timestamp(tracker.lastScrapeTime); + if (tracker.lastScrapeSucceeded) { + lastScrape = lastScrapeTime; + } else { + lastScrapeLabel = 'Scrape error'; + lastScrape = (tracker.lastScrapeResult ? tracker.lastScrapeResult + ' - ' : '') + lastScrapeTime; + } + } + return {'label':lastScrapeLabel, 'value':lastScrape}; + }, + + updateTrackersPage = function() { + var i, j, tier, tracker, trackers, tor, + html, parity, lastAnnounceStatusHash, + announceState, lastScrapeStatusHash, + na = 'N/A', + trackers_list = data.elements.trackers_list, + torrents = data.torrents; + + // By building up the HTML as as string, then have the browser + // turn this into a DOM tree, this is a fast operation. + html = []; + for (i=0; tor=torrents[i]; ++i) + { + html.push ('
    '); + + if (torrents.length > 1) + html.push('
    ', tor.getName(), '
    '); + + tier = -1; + trackers = tor.getTrackers(); + for (j=0; tracker=trackers[j]; ++j) + { + if (tier != tracker.tier) + { + if (tier !== -1) // close previous tier + html.push('
    '); + + tier = tracker.tier; + + html.push('
    ', + 'Tier ', tier+1, '
    ', + '
      '); + } + + // Display construction + lastAnnounceStatusHash = lastAnnounceStatus(tracker); + announceState = getAnnounceState(tracker); + lastScrapeStatusHash = lastScrapeStatus(tracker); + parity = (j%2) ? 'odd' : 'even'; + html.push('
    • ', + sanitizeText(tracker.host || tracker.announce), '
      ', + '
      ', + '
      ', lastAnnounceStatusHash['label'], ': ', lastAnnounceStatusHash['value'], '
      ', + '
      ', announceState, '
      ', + '
      ', lastScrapeStatusHash['label'], ': ', lastScrapeStatusHash['value'], '
      ', + '
      ', + '', + '', + '', + '
      Seeders:', (tracker.seederCount > -1 ? tracker.seederCount : na), '
      Leechers:', (tracker.leecherCount > -1 ? tracker.leecherCount : na), '
      Downloads:', (tracker.downloadCount > -1 ? tracker.downloadCount : na), '
    • '); + } + if (tier !== -1) // close last tier + html.push('
    '); + + html.push(''); // inspector_group + } + + setInnerHTML (trackers_list, html.join('')); + }, + + initialize = function (controller) { + + var ti = '#torrent_inspector_'; + + data.controller = controller; + + this.skipClick = false; + + $('.inspector-tab').click(onTabClicked); + // Vuze: add tap, as it doesn't have a 300ms delay on mobiles + $('.inspector-tab').bind('tap', onTabClicked); + + data.elements.info_page = $('#inspector-page-info')[0]; + data.elements.files_page = $('#inspector-page-files')[0]; + data.elements.peers_page = $('#inspector-page-peers')[0]; + data.elements.trackers_page = $('#inspector-page-trackers')[0]; + + data.elements.file_list = $('#inspector_file_list')[0]; + data.elements.peers_list = $('#inspector_peers_list')[0]; + data.elements.trackers_list = $('#inspector_trackers_list')[0]; + + data.elements.have_lb = $('#inspector-info-have')[0]; + data.elements.availability_lb = $('#inspector-info-availability')[0]; + data.elements.downloaded_lb = $('#inspector-info-downloaded')[0]; + data.elements.uploaded_lb = $('#inspector-info-uploaded')[0]; + data.elements.state_lb = $('#inspector-info-state')[0]; + data.elements.running_time_lb = $('#inspector-info-running-time')[0]; + data.elements.remaining_time_lb = $('#inspector-info-remaining-time')[0]; + data.elements.last_activity_lb = $('#inspector-info-last-activity')[0]; + data.elements.error_lb = $('#inspector-info-error')[0]; + data.elements.size_lb = $('#inspector-info-size')[0]; + data.elements.foldername_lb = $('#inspector-info-location')[0]; + data.elements.hash_lb = $('#inspector-info-hash')[0]; + data.elements.privacy_lb = $('#inspector-info-privacy')[0]; + data.elements.origin_lb = $('#inspector-info-origin')[0]; + data.elements.comment_lb = $('#inspector-info-comment')[0]; + // Vuze: Tags + data.elements.tags_lb = $('#inspector-info-tags')[0]; + data.elements.name_lb = $('#torrent_inspector_name')[0]; + + // force initial 'N/A' updates on all the pages + updateInspector(); + updateInfoPage(); + updatePeersPage(); + updateTrackersPage(); + updateFilesPage(); + }; + + /**** + ***** PUBLIC FUNCTIONS + ****/ + + this.setTorrents = function (torrents) { + var d = data; + + clearFileList(); + + if (torrents.length > 0) { + var page = "Inspector"; + var selectedPage = $('.inspector-tab.selected'); + if (selectedPage.length > 0) { + page += "-" + selectedPage[0].title; + } + vz.torrentInfoShown(torrents[0].getId(), page); + } + + // update the inspector when a selected torrent's data changes. + $(d.torrents).unbind('dataChanged.inspector'); + $(torrents).bind('dataChanged.inspector', $.proxy(updateInspector,this)); + d.torrents = torrents; + + // periodically ask for updates to the inspector's torrents + clearInterval(d.refreshInterval); + msec = controller[Prefs._RefreshRate] * 1000; + if (!controller.uiPaused && msec > 0) { + d.refreshInterval = setInterval($.proxy(refreshTorrents,this), msec); + } + refreshTorrents(); + + // refresh the inspector's UI + updateInspector(); + }; + + this.getTorrents = function() { + return data.torrents; + } + + initialize (controller); +}; diff --git a/src/jsp/web/javascript/jquery/jquery-1.10.2.min.js b/src/jsp/web/javascript/jquery/jquery-1.10.2.min.js new file mode 100644 index 0000000..da41706 --- /dev/null +++ b/src/jsp/web/javascript/jquery/jquery-1.10.2.min.js @@ -0,0 +1,6 @@ +/*! jQuery v1.10.2 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license +//@ sourceMappingURL=jquery-1.10.2.min.map +*/ +(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.2",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=st(),k=st(),E=st(),S=!1,A=function(e,t){return e===t?(S=!0,0):0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=mt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+yt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return kt(e.replace(z,"$1"),t,n,i)}function st(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function lt(e){return e[b]=!0,e}function ut(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ct(e,t){var n=e.split("|"),r=e.length;while(r--)o.attrHandle[n[r]]=t}function pt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function ft(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function dt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ht(e){return lt(function(t){return t=+t,lt(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.defaultView;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.attachEvent&&i!==i.top&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ut(function(e){return e.className="i",!e.getAttribute("className")}),r.getElementsByTagName=ut(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ut(function(e){return e.innerHTML="
    ",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ut(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=K.test(n.querySelectorAll))&&(ut(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ut(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=K.test(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ut(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=K.test(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return pt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?pt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:lt,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=mt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?lt(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:lt(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?lt(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:lt(function(e){return function(t){return at(e,t).length>0}}),contains:lt(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:lt(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:ht(function(){return[0]}),last:ht(function(e,t){return[t-1]}),eq:ht(function(e,t,n){return[0>n?n+t:n]}),even:ht(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:ht(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:ht(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:ht(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}},o.pseudos.nth=o.pseudos.eq;for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=ft(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=dt(n);function gt(){}gt.prototype=o.filters=o.pseudos,o.setFilters=new gt;function mt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function yt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function vt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function bt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function wt(e,t,n,r,i,o){return r&&!r[b]&&(r=wt(r)),i&&!i[b]&&(i=wt(i,o)),lt(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||Nt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:xt(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=xt(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=xt(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function Tt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=vt(function(e){return e===t},s,!0),p=vt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[vt(bt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return wt(l>1&&bt(f),l>1&&yt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&Tt(e.slice(l,r)),i>r&&Tt(e=e.slice(r)),i>r&&yt(e))}f.push(n)}return bt(f)}function Ct(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=xt(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?lt(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=mt(e)),n=t.length;while(n--)o=Tt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Ct(i,r))}return o};function Nt(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function kt(e,t,n,i){var a,s,u,c,p,f=mt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&yt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}r.sortStable=b.split("").sort(A).join("")===b,r.detectDuplicates=S,p(),r.sortDetached=ut(function(e){return 1&e.compareDocumentPosition(f.createElement("div"))}),ut(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||ct("type|href|height|width",function(e,n,r){return r?t:e.getAttribute(n,"type"===n.toLowerCase()?1:2)}),r.attributes&&ut(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||ct("value",function(e,n,r){return r||"input"!==e.nodeName.toLowerCase()?t:e.defaultValue}),ut(function(e){return null==e.getAttribute("disabled")})||ct(B,function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&i.specified?i.value:e[n]===!0?n.toLowerCase():null}),x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!l||i&&!u||(t=t||[],t=[e,t.slice?t.slice():t],n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
    a",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="
    t
    ",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
    ",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null)}),n=s=l=u=r=o=null,t +}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var t,r=0,o=x(this),a=e.match(T)||[];while(t=a[r++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
    ","
    "],area:[1,"",""],param:[1,"",""],thead:[1,"","
    "],tr:[2,"","
    "],col:[2,"","
    "],td:[3,"","
    "],_default:x.support.htmlSerialize?[0,"",""]:[1,"X
    ","
    "]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle); +u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){nn(this)?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x(""); + $("#remotesearch").attr({src: search_url}); + } else { + search_url = "http://search.vuze.com/xsearch/?q=" + search_input + "&xdmv=2.4.17.1&mode=plus&goo=//" + vz.mode + "&search_source=" + encodeURIComponent(window.location.href); + if( vz.searchQuery != search_url ) { + $("#remotesearch_container").text(""); + vz.remote = null; + vz.createRemote( search_url ); + } + } + vz.searchQuery = search_url; + $("#torrent_container").hide(); + $("#remotesearch_container").show(); +}; + +vz.backFromSearch = function(){ + $("#torrent_container").show(); + $("#remotesearch_container").hide(); +}; + +vz.createRemote = function(remote_url){ + vz.remote = new easyXDM.Rpc(/** The channel configuration */{ + local: "../easyXDM/hash.html", + swf: "easyxdm-2.4.18.4.swf", + remote: remote_url, + container: document.getElementById("remotesearch_container") + }, /** The interface configuration */ { + remote: { + postMessage: {}, + noOp: {} + }, + local: { + alertMessage: { + method: function(msg){ + alert(msg); + }, + isVoid: true + }, + download: { + method: function(url){ + /*make sure call isn't made several times*/ + if( vz.dls[url] != null && (new Date().getTime() - vz.dls[url].ts < 2000) ) return + vz.ui.toggleRemoteSearch(); + transmission.setFilterMode(Prefs._FilterIncomplete); + transmission.setSortMethod( 'age' ); + transmission.setSortDirection( 'descending' ); + transmission.remote.addTorrentByUrl( url, {} ); + vz.dls[url] = { + url: url, + ts: new Date().getTime() + }; + }, + isVoid: true + }, + noOp: { + method: function(){ + //alert('done') + }, + isVoid: true + } + } + }); +}; + +vz.ui = {}; + +vz.ui.toggleRemoteSearch = function(){ + if( $(".toolbar-main").is(":visible") ) { + $(".toolbar-main").hide(); + $(".toolbar-vuze").show(); + //$("#toolbar").addClass("search") + vz.executeSearch(); + $("#search_input").focus(); + } else { + $(".toolbar-vuze").hide(); + $(".toolbar-main").show(); + //$("#toolbar").removeClass("search"); + vz.backFromSearch(); + } +}; + +vz.dls = {}; +vz.utils = {}; + +vz.utils = { + selectOnFocus: function(){ + $("#search_input").focus(function(){ + this.select(); + }); + } +}; + +vz.logout = function() { + if (vz.hasExternalOSFunctions()) { + externalOSFunctions.logout(); + } else { + window.location.href = "/pairedServiceLogout?redirect_to=http://remote.vuze.com/logout.php"; + } +}; + +vz.showOpenTorrentDialog = function() { + if (vz.hasExternalOSFunctions()) { + try { + externalOSFunctions.showOpenTorrentDialog(); + return true; + } catch(e) { + console.log(e); + } + } + return false; +}; + +vz.handleConnectionError = function(errNo, msg, status) { + if (vz.hasExternalOSFunctions()) { + try { + return externalOSFunctions.handleConnectionError(errNo, msg, status); + } catch(e) { + console.log(e); + } + } + return false; +}; + +vz.showConfirmDeleteDialog = function(torrent) { + if (vz.hasExternalOSFunctions()) { + try { + return externalOSFunctions.showConfirmDeleteDialog(torrent.getName(), torrent.getId()); + } catch(e) { + console.log(e); + } + } + return false; +}; + +vz.handleTapHold = function() { + if (vz.hasExternalOSFunctions()) { + try { + return externalOSFunctions.handleTapHold(); + } catch(e) { + console.log(e); + } + } + return false; +}; + +vz.uiReady = function() { + if (vz.hasExternalOSFunctions()) { + try { + externalOSFunctions.uiReady(); + } catch(e) { + console.log(e); + } + } +}; + +vz.updateSpeed = function(downSpeed, upSpeed) { + if (vz.hasExternalOSFunctions()) { + try { + externalOSFunctions.updateSpeed(downSpeed, upSpeed); + } catch(e) { + console.log(e); + } + } +}; + +vz.updateTorrentStates = function(haveActive, havePaused, haveActiveSel, havePausedSel) { + if (vz.hasExternalOSFunctions()) { + try { + var t = String(haveActive) + String(havePaused) + + String(haveActiveSel) + String(havePausedSel); + if (t !== vz.lastTorrenStates) { + vz.lastTorrenStates = t; + externalOSFunctions.updateTorrentStates(haveActive, havePaused, + haveActiveSel, havePausedSel); + } + } catch(e) { + console.log(e); + } + } +}; + + +vz.updateTorrentCount= function(total) { + if (vz.hasExternalOSFunctions()) { + try { + externalOSFunctions.updateTorrentCount(total); + } catch(e) { + console.log(e); + } + } +}; + +vz.selectionChanged = function(selectedTorrents, haveActiveSel, havePausedSel) { + + if (vz.hasExternalOSFunctions()) { + try { + var t = String(haveActiveSel) + String(havePausedSel) + + selectedTorrents.map(function(elem) {return elem.id;}).join(","); + if (t !== vz.lastSelectionChanged) { + vz.lastSelectionChanged = t; + externalOSFunctions.selectionChanged(JSON.stringify(selectedTorrents), + haveActiveSel, havePausedSel); + } + + } catch (e) { + console.log(e); + } + } +}; + +vz.updateSessionProperties = function(sessionProperties) { + if (vz.hasExternalOSFunctions()) { + try { + externalOSFunctions.updateSessionProperties(JSON.stringify(sessionProperties)); + } catch(e) { + console.log(e); + } + } +}; + +vz.torrentInfoShown = function(id, page) { + if (vz.hasExternalOSFunctions()) { + try { + externalOSFunctions.torrentInfoShown(id, page); + } catch(e) { + console.log(e); + } + } +}; + +vz.slowAjax = function(id) { + if (vz.hasExternalOSFunctions()) { + try { + externalOSFunctions.slowAjax(id); + } catch(e) { + console.log(e); + } + } +} + +vz.slowAjaxDone = function(id, ms) { + if (vz.hasExternalOSFunctions()) { + try { + externalOSFunctions.slowAjaxDone(id, ms); + } catch(e) { + console.log(e); + } + } +} + +vz.goBack = function() { + if ($('#ul_torrent_context_menu').is(':visible')) { + externalOSFunctions.cancelGoBack(true); + $('#ul_torrent_context_menu').hide(); + return false; + } + + var visibleDialog = $('.ui-dialog-content:visible'); + if (visibleDialog.length) { + externalOSFunctions.cancelGoBack(true); + visibleDialog.dialog('close'); + return false; + } + + visibleDialog = $(".dialog_container:visible"); + if (visibleDialog.length) { + externalOSFunctions.cancelGoBack(true); + visibleDialog.hide(); + return false; + } + + externalOSFunctions.cancelGoBack(false); + return true; +}; + +vz.hasExternalOSFunctions = function() { + return typeof externalOSFunctions !== 'undefined'; +}; + +function isTouchDevice(){ + try{ + document.createEvent("TouchEvent"); + return true; + }catch(e){ + return false; + } +} + +// From http://chris-barr.com/2010/05/scrolling_a_overflowauto_element_on_a_touch_screen_device/#comment-65 +function touchScroll(selector) { + if (isTouchDevice()) { + var scrollStartPosY=0; + var scrollStartPosX=0; + $('body').delegate(selector, 'touchstart', function(e) { + scrollStartPosY=this.scrollTop+e.originalEvent.touches[0].pageY; + scrollStartPosX=this.scrollLeft+e.originalEvent.touches[0].pageX; + }); + $('body').delegate(selector, 'touchmove', function(e) { + if ((this.scrollTop < this.scrollHeight-this.offsetHeight && + this.scrollTop+e.originalEvent.touches[0].pageY < scrollStartPosY-5) || + (this.scrollTop != 0 && this.scrollTop+e.originalEvent.touches[0].pageY > scrollStartPosY+5)) + e.preventDefault(); + if ((this.scrollLeft < this.scrollWidth-this.offsetWidth && + this.scrollLeft+e.originalEvent.touches[0].pageX < scrollStartPosX-5) || + (this.scrollLeft != 0 && this.scrollLeft+e.originalEvent.touches[0].pageX > scrollStartPosX+5)) + e.preventDefault(); + this.scrollTop=scrollStartPosY-e.originalEvent.touches[0].pageY; + this.scrollLeft=scrollStartPosX-e.originalEvent.touches[0].pageX; + }); + } +} + +function vuzeOnResize() { + var h = ($(window).height() - 80); + $('#remotesearch_container').height(h); + if ($(window).width() > 900) { + $("#torrent_logo").show(); + } else { + $("#torrent_logo").hide(); + } +} + +function getWebkitVersion() { + var result = /AppleWebKit\/([\d.]+)/.exec(navigator.userAgent); + if (result) { + return parseFloat(result[1]); + } + return null; +} + +$(document).ready( function(){ + + if (!vz.hasExternalOSFunctions() && $.url().param("testAND") != "1") { + $(window).resize(vuzeOnResize); + vuzeOnResize(); + } + + vz.utils.selectOnFocus(); + // WebKit 533.1 (Android 2.3.3) needs scrollable divs hack + // WebKit 533.17.9 (iPhone OS 4_2_1) needs scrollable divs hack + // + // WebKit 534.13 can do scrollable divs + // WebKit 534.30 (Android 4.1.2) can do scrollable divs + // WebKit 535.19 (Chrome 18.0.1025.166) can do scrollable divs + // Assumed: 534 added scrollable Divs! + var webkitVersion = getWebkitVersion(); + if (webkitVersion != null && webkitVersion < 534) { + touchScroll(".scrollable"); + } + + var ua = navigator.userAgent; + if (ua.indexOf("iPhone OS 4_") !== -1 || ua.indexOf("iPhone OS 3_") !== -1) { + // older iPods crash on search results + $("#toolbar-search").hide(); + } +}); + +if (vz.hasExternalOSFunctions() || $.url().param("testAND") == "1") { + var fileref=document.createElement("link"); + fileref.setAttribute("rel", "stylesheet"); + fileref.setAttribute("type", "text/css"); + fileref.setAttribute("href", "./style/transmission/vuzeandroid.css"); + document.getElementsByTagName("head")[0].appendChild(fileref); +} diff --git a/src/jsp/web/style/jqueryui/images/animated-overlay.gif b/src/jsp/web/style/jqueryui/images/animated-overlay.gif new file mode 100644 index 0000000000000000000000000000000000000000..d441f75ebfbdf26a265dfccd670120d25c0a341c GIT binary patch literal 1738 zcmZ|OX;ji_6b5ixNYt8>l?gOuO)6lU%W(mxn(`>1S(XO;u`D+P%xqBvMr|w-Vyr1s z7R|Cn0b8|Hu<=Zmv1mFqh9Fj!NuZfKB2MP$e75`XJ@>=!y!Ux9xR3x;EW!q1^V>X| znVFuRUN`NqJ2)ybXh%e__h!!pv(M|S3+?9F%(K}zyE40MGyhWF5-IDgL&=%2-9`Nk z!1@8uk4t%_{(K~>N;sK&dzJbwJ=$kYTlL=$%#0Pfh>U{%i@~wWbvYsD_K-D`&+u1( z#Ma`>%q<^UhzGvi(hyE`zCD{-=2|zL5>wnB=DE!U?(CZG%q4@lDnCq_%&3DCla#(X zmBhDD+RN$aMWWHm?ig*>1Onn6~r?Ma~N2JKAxN>H%UtRyRqS)6Um!-Tz%-r=& zQmTb^JFIe3W^-kAm`}`2P|niMh>RYyd)S^f(dbrx965?rzbhP|XeP}o&&DSZ4|oYQ z)I{f!SfycYw?3=9W;o-B%U5xs(pP267X~9-7L|4WzaYexC0GtG8wWygm63rF{llCEraxzkc=IxvFQ-y37=_;e5 zJLq^gsSO0Ayz?a>E_?{dmUc+t#qv$)XN8$<<}rQ#)lsiw+pmL&J>~+hgpo>i$m+;l zZIa_ZRIfSeT$~v5d`EBV&*k`apPgjv&B|+d`Q!nyu{L4rs%ZfoF0*Kq8I%ByOcFpL zK=>wzofZo<+0GZLCnWM3oQ^pb(gRSf02;~cEn@LJ>~XB9IkEX{$N#Z`m%>S!U{uPx zloI%bLdo$Adxlh(Uv^yX7s5G&C zLwNRG>~T?G{kzupp8EcyLGPoPf)@&9Wqfw_l&uU-6cexk%5;uQg%wb=0k_733{i#& z1a2p)gV3S2+QG1-K9tZ}E~I<(P0r2aFFY-c{o?TUOz3Xjod#TLE2A_c?*T7t z=1>~%YW450{Qqno4t`}gvLnuMrcu8+#xEBoY%2_+Mb#Z6S38+r*M4O`-+!zl(@m`D zQsi|GA2l3gEy}LFe<#Hv8?$_L#u8E|3-bP$*La*E>B{X!Sy4i6?TKam!49aXCAW4S*P_O^H4^*DpiA40o}Uqw~Eo&veh1`|8i zD2$x+>_b^bXE4N;AW=5>iYak2%!JAh0j1*k1{p#iRCjbB7!cSws~U{1IA@acLII$t z$>X#A+^s6iJ5~DFG!xa?>z{=lxtdi1rzbM-(nqAu3D8h-&64xo6|E!p?pK0xT;qoK z`6%+SpBk+~M?nO}>2mTw!A{yZ6O>Z@kwSd4;8aWU5z!P~tQl?u==^+R`{OmOS}oZh zOXQ3{6kuz?Is^n^L7;9ieB9C+8B{>t+pDrlq4xGDDn#T#3T5$l1g`FTQkU;b-981j zNm{zC`$wn7etklM#qHI4=3m5gwa6DNS{?Z!vSObi_od{4eUo=_S2BKNpkSdiqe(k9WtkeM79;2-%CFbb)aB=&H1?i1}uwFzoZQ(38Kn1zBP ORn*B%u*Wk|4g3!*Rv{Mv literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/jqueryui/images/ui-bg_flat_0_aaaaaa_40x100.png b/src/jsp/web/style/jqueryui/images/ui-bg_flat_0_aaaaaa_40x100.png new file mode 100644 index 0000000000000000000000000000000000000000..75b5dbb4a71e0d2ec6c5a9ff562f2fdf1620a049 GIT binary patch literal 212 zcmeAS@N?(olHy`uVBq!ia0vp^8bF-F1SA+{?>A)!QcOwS?k)_>#w|r1Kptm-M`SUO z_5fqIli7AahM1>|V~EA+ zRdP`(kYX@0Ff`URu+%j&3NbLUG65n}T?2C~1B3P;v)?Eha`RI%(<*Um_{QJo4%EQl M>FVdQ&MBb@0FN*;b^rhX literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/jqueryui/images/ui-bg_flat_75_ffffff_40x100.png b/src/jsp/web/style/jqueryui/images/ui-bg_flat_75_ffffff_40x100.png new file mode 100644 index 0000000000000000000000000000000000000000..8e10b4cebaba1e9244b32b4cec5890476e81a495 GIT binary patch literal 208 zcmeAS@N?(olHy`uVBq!ia0vp^8bF-F2qYNp$opRhQcOwS?k)_Bce{j_0C}7R9+AaB z+5?Q;PG;Ky8A6^ejv*T7lYj6t@hpC#;TbB#aBAWwna#KLs)4eqC9V-ADTyViR>?)F zK#IZ0z|dINz*5)9D8#_X$^?i^bPdd{3=9-iZT(R+nc6a#?2AmP!?*K(O3p^r= zfwTu0yPeFo12TF&T^vI^j=w#x$i?I+((tf;UXnmgbH|3oY>pC!)f}(GR!16S-u+#{ ze6YEqRkW=8vGl=5qArKM<9}TC-}iEvB{zdaTcX5$wyRTK&ALKYk^7#LZZ7+D#c=^B_@85r!TSFK0Ukei>9nO2Eg!&#-dhk+UxJYD@< J);T3K0Ra1UGhhG! literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/jqueryui/images/ui-bg_glass_75_dadada_1x400.png b/src/jsp/web/style/jqueryui/images/ui-bg_glass_75_dadada_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..3bbdb55e28758503515fd56998954023d307f764 GIT binary patch literal 262 zcmeAS@N?(olHy`uVBq!ia0vp^j6gI&0LWmFTHNUZq?nSt-Ch3w7g=q17Rci)@Q5r1 z(jH*!b~4)z#PD=+46!(!T=8puqDZgOs>RXUCGx5b?-VBQkUm|IuXOmYJrBRJgj{Vx zMbNnqUkncy+qa2-mWYc>swkcIuvGK#>(0d)B7)5f`@$Ei28nH~0h*~=;u=wsl30>z zm0Xkxq!^4042^XSEOm{HLJW+oOn}H#*TCG$z@R^F*r-29Zxv`X9>zVY|D12r&s My85}Sb4q9e09W8nT>t<8 literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/jqueryui/images/ui-bg_glass_75_e6e6e6_1x400.png b/src/jsp/web/style/jqueryui/images/ui-bg_glass_75_e6e6e6_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..ba09b960c0115e8a7b082ed578647c51f9bb5444 GIT binary patch literal 262 zcmeAS@N?(olHy`uVBq!ia0vp^j6gI&0LWmFTHNUZq?nSt-Ch3w7g=q17Rci)@Q5r1 z(jH*!b~4)z#PD=+46!(!TrvH)L6@80)r*_cdCvDr%)6ghVL16=s@mbz7H!uRdGeDa z?kzLg)16i!f8fKx84s0>4mdKI;Vst09n9ImjD0& literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/jqueryui/images/ui-bg_glass_95_fef1ec_1x400.png b/src/jsp/web/style/jqueryui/images/ui-bg_glass_95_fef1ec_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..3462859ad9620662d2884b68674660cd0b9e5439 GIT binary patch literal 332 zcmeAS@N?(olHy`uVBq!ia0vp^j6gI&fCnc6a#?2AmP!?*K(O3p^r= zfwTu0yPeFo12VciT^vI^j=w#>k(V)1qW$CZ|6)SVV-&*#dav<$DMuV&n0Dbpw@aKYk^7#LZZ0FjBVfw`4|fugFd fKZ=Ij{FKbJO57R(Dq24SH86O(`njxgN@xNAYg}@E literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/jqueryui/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/src/jsp/web/style/jqueryui/images/ui-bg_highlight-soft_75_cccccc_1x100.png new file mode 100644 index 0000000000000000000000000000000000000000..2a27e939167ebe8d73f33039b13af57d52e07e71 GIT binary patch literal 280 zcmeAS@N?(olHy`uVBq!ia0vp^j6j?s03;ZUuHXC*q?nSt-Ch3w7g=q17Rci)@Q5r1 z(jH*!b~4)z$cXZEaSV~ToLo`U+vu0Ue0cG9p8hWqa?gxxGLm=1A1u)Cewe3oSeCaf zI$k30UHXoTXA5lSJe(zTcE%W-S*bfB&J`pw9sa4-R?IGW?p~6`>jMSP&M+u3 zY@9al)zrvpHlQu4C9V-ADTyViR>?)FK#IZ0z|dINz*5)9D8#_X$^?i^bq&m|3=GmdKI;Vst04<(cL;wH) literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/jqueryui/images/ui-icons_222222_256x240.png b/src/jsp/web/style/jqueryui/images/ui-icons_222222_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..c1cb1170c8b3795835b8831ab81fa9ae63b606b1 GIT binary patch literal 6922 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 QJ~;stWz}Trq%1=J3#jBGg8%>k literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/jqueryui/images/ui-icons_2e83ff_256x240.png b/src/jsp/web/style/jqueryui/images/ui-icons_2e83ff_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..84b601bf0f726bf95801da487deaf2344a32e4b8 GIT binary patch literal 4549 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 F{{S81aohj^ literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/jqueryui/images/ui-icons_454545_256x240.png b/src/jsp/web/style/jqueryui/images/ui-icons_454545_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..b6db1acdd433be80a472b045018f25c7f2cf7e08 GIT binary patch literal 6992 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 literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/jqueryui/images/ui-icons_cd0a0a_256x240.png b/src/jsp/web/style/jqueryui/images/ui-icons_cd0a0a_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..ed5b6b0930f672fa08e9b9bdbe5e55370fd1dc30 GIT binary patch literal 4549 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 DTfr$v literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/jqueryui/jquery-ui-1.10.3.css b/src/jsp/web/style/jqueryui/jquery-ui-1.10.3.css new file mode 100644 index 0000000..0d90240 --- /dev/null +++ b/src/jsp/web/style/jqueryui/jquery-ui-1.10.3.css @@ -0,0 +1,1188 @@ +/*! jQuery UI - v1.10.3 - 2013-05-03 +* http://jqueryui.com +* Includes: jquery.ui.core.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css, jquery.ui.theme.css +* Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */ +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { + display: none; +} +.ui-helper-hidden-accessible { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} +.ui-helper-reset { + margin: 0; + padding: 0; + border: 0; + outline: 0; + line-height: 1.3; + text-decoration: none; + font-size: 100%; + list-style: none; +} +.ui-helper-clearfix:before, +.ui-helper-clearfix:after { + content: ""; + display: table; + border-collapse: collapse; +} +.ui-helper-clearfix:after { + clear: both; +} +.ui-helper-clearfix { + min-height: 0; /* support: IE7 */ +} +.ui-helper-zfix { + width: 100%; + height: 100%; + top: 0; + left: 0; + position: absolute; + opacity: 0; + filter:Alpha(Opacity=0); +} + +.ui-front { + z-index: 100; +} + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { + cursor: default !important; +} + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { + display: block; + text-indent: -99999px; + overflow: hidden; + background-repeat: no-repeat; +} + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.ui-accordion .ui-accordion-header { + display: block; + cursor: pointer; + position: relative; + margin-top: 2px; + padding: .5em .5em .5em .7em; + min-height: 0; /* support: IE7 */ +} +.ui-accordion .ui-accordion-icons { + padding-left: 2.2em; +} +.ui-accordion .ui-accordion-noicons { + padding-left: .7em; +} +.ui-accordion .ui-accordion-icons .ui-accordion-icons { + padding-left: 2.2em; +} +.ui-accordion .ui-accordion-header .ui-accordion-header-icon { + position: absolute; + left: .5em; + top: 50%; + margin-top: -8px; +} +.ui-accordion .ui-accordion-content { + padding: 1em 2.2em; + border-top: 0; + overflow: auto; +} + +.ui-autocomplete { + position: absolute; + top: 0; + left: 0; + cursor: default; +} + +.ui-button { + display: inline-block; + position: relative; + padding: 0; + line-height: normal; + margin-right: .1em; + cursor: pointer; + vertical-align: middle; + text-align: center; + overflow: visible; /* removes extra width in IE */ +} +.ui-button, +.ui-button:link, +.ui-button:visited, +.ui-button:hover, +.ui-button:active { + text-decoration: none; +} +/* to make room for the icon, a width needs to be set here */ +.ui-button-icon-only { + width: 2.2em; +} +/* button elements seem to need a little more width */ +button.ui-button-icon-only { + width: 2.4em; +} +.ui-button-icons-only { + width: 3.4em; +} +button.ui-button-icons-only { + width: 3.7em; +} + +/* button text element */ +.ui-button .ui-button-text { + display: block; + line-height: normal; +} +.ui-button-text-only .ui-button-text { + padding: .4em 1em; +} +.ui-button-icon-only .ui-button-text, +.ui-button-icons-only .ui-button-text { + padding: .4em; + text-indent: -9999999px; +} +.ui-button-text-icon-primary .ui-button-text, +.ui-button-text-icons .ui-button-text { + padding: .4em 1em .4em 2.1em; +} +.ui-button-text-icon-secondary .ui-button-text, +.ui-button-text-icons .ui-button-text { + padding: .4em 2.1em .4em 1em; +} +.ui-button-text-icons .ui-button-text { + padding-left: 2.1em; + padding-right: 2.1em; +} +/* no icon support for input elements, provide padding by default */ +input.ui-button { + padding: .4em 1em; +} + +/* button icon element(s) */ +.ui-button-icon-only .ui-icon, +.ui-button-text-icon-primary .ui-icon, +.ui-button-text-icon-secondary .ui-icon, +.ui-button-text-icons .ui-icon, +.ui-button-icons-only .ui-icon { + position: absolute; + top: 50%; + margin-top: -8px; +} +.ui-button-icon-only .ui-icon { + left: 50%; + margin-left: -8px; +} +.ui-button-text-icon-primary .ui-button-icon-primary, +.ui-button-text-icons .ui-button-icon-primary, +.ui-button-icons-only .ui-button-icon-primary { + left: .5em; +} +.ui-button-text-icon-secondary .ui-button-icon-secondary, +.ui-button-text-icons .ui-button-icon-secondary, +.ui-button-icons-only .ui-button-icon-secondary { + right: .5em; +} + +/* button sets */ +.ui-buttonset { + margin-right: 7px; +} +.ui-buttonset .ui-button { + margin-left: 0; + margin-right: -.3em; +} + +/* workarounds */ +/* reset extra padding in Firefox, see h5bp.com/l */ +input.ui-button::-moz-focus-inner, +button.ui-button::-moz-focus-inner { + border: 0; + padding: 0; +} + +.ui-datepicker { + width: 17em; + padding: .2em .2em 0; + display: none; +} +.ui-datepicker .ui-datepicker-header { + position: relative; + padding: .2em 0; +} +.ui-datepicker .ui-datepicker-prev, +.ui-datepicker .ui-datepicker-next { + position: absolute; + top: 2px; + width: 1.8em; + height: 1.8em; +} +.ui-datepicker .ui-datepicker-prev-hover, +.ui-datepicker .ui-datepicker-next-hover { + top: 1px; +} +.ui-datepicker .ui-datepicker-prev { + left: 2px; +} +.ui-datepicker .ui-datepicker-next { + right: 2px; +} +.ui-datepicker .ui-datepicker-prev-hover { + left: 1px; +} +.ui-datepicker .ui-datepicker-next-hover { + right: 1px; +} +.ui-datepicker .ui-datepicker-prev span, +.ui-datepicker .ui-datepicker-next span { + display: block; + position: absolute; + left: 50%; + margin-left: -8px; + top: 50%; + margin-top: -8px; +} +.ui-datepicker .ui-datepicker-title { + margin: 0 2.3em; + line-height: 1.8em; + text-align: center; +} +.ui-datepicker .ui-datepicker-title select { + font-size: 1em; + margin: 1px 0; +} +.ui-datepicker select.ui-datepicker-month-year { + width: 100%; +} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { + width: 49%; +} +.ui-datepicker table { + width: 100%; + font-size: .9em; + border-collapse: collapse; + margin: 0 0 .4em; +} +.ui-datepicker th { + padding: .7em .3em; + text-align: center; + font-weight: bold; + border: 0; +} +.ui-datepicker td { + border: 0; + padding: 1px; +} +.ui-datepicker td span, +.ui-datepicker td a { + display: block; + padding: .2em; + text-align: right; + text-decoration: none; +} +.ui-datepicker .ui-datepicker-buttonpane { + background-image: none; + margin: .7em 0 0 0; + padding: 0 .2em; + border-left: 0; + border-right: 0; + border-bottom: 0; +} +.ui-datepicker .ui-datepicker-buttonpane button { + float: right; + margin: .5em .2em .4em; + cursor: pointer; + padding: .2em .6em .3em .6em; + width: auto; + overflow: visible; +} +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { + float: left; +} + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { + width: auto; +} +.ui-datepicker-multi .ui-datepicker-group { + float: left; +} +.ui-datepicker-multi .ui-datepicker-group table { + width: 95%; + margin: 0 auto .4em; +} +.ui-datepicker-multi-2 .ui-datepicker-group { + width: 50%; +} +.ui-datepicker-multi-3 .ui-datepicker-group { + width: 33.3%; +} +.ui-datepicker-multi-4 .ui-datepicker-group { + width: 25%; +} +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header, +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { + border-left-width: 0; +} +.ui-datepicker-multi .ui-datepicker-buttonpane { + clear: left; +} +.ui-datepicker-row-break { + clear: both; + width: 100%; + font-size: 0; +} + +/* RTL support */ +.ui-datepicker-rtl { + direction: rtl; +} +.ui-datepicker-rtl .ui-datepicker-prev { + right: 2px; + left: auto; +} +.ui-datepicker-rtl .ui-datepicker-next { + left: 2px; + right: auto; +} +.ui-datepicker-rtl .ui-datepicker-prev:hover { + right: 1px; + left: auto; +} +.ui-datepicker-rtl .ui-datepicker-next:hover { + left: 1px; + right: auto; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane { + clear: right; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane button { + float: left; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current, +.ui-datepicker-rtl .ui-datepicker-group { + float: right; +} +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header, +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { + border-right-width: 0; + border-left-width: 1px; +} + +.ui-dialog { + position: absolute; + top: 0; + left: 0; + padding: .2em; + outline: 0; +} +.ui-dialog .ui-dialog-titlebar { + padding: .4em 1em; + position: relative; +} +.ui-dialog .ui-dialog-title { + float: left; + margin: .1em 0; + white-space: nowrap; + width: 90%; + overflow: hidden; + text-overflow: ellipsis; +} +.ui-dialog .ui-dialog-titlebar-close { + position: absolute; + right: .3em; + top: 50%; + width: 21px; + margin: -10px 0 0 0; + padding: 1px; + height: 20px; +} +.ui-dialog .ui-dialog-content { + position: relative; + border: 0; + padding: .5em 1em; + background: none; + overflow: auto; +} +.ui-dialog .ui-dialog-buttonpane { + text-align: left; + border-width: 1px 0 0 0; + background-image: none; + margin-top: .5em; + padding: .3em 1em .5em .4em; +} +.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { + float: right; +} +.ui-dialog .ui-dialog-buttonpane button { + margin: .5em .4em .5em 0; + cursor: pointer; +} +.ui-dialog .ui-resizable-se { + width: 12px; + height: 12px; + right: -5px; + bottom: -5px; + background-position: 16px 16px; +} +.ui-draggable .ui-dialog-titlebar { + cursor: move; +} + +.ui-menu { + list-style: none; + padding: 2px; + margin: 0; + display: block; + outline: none; +} +.ui-menu .ui-menu { + margin-top: -3px; + position: absolute; +} +.ui-menu .ui-menu-item { + margin: 0; + padding: 0; + width: 100%; + /* support: IE10, see #8844 */ + list-style-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7); +} +.ui-menu .ui-menu-divider { + margin: 5px -2px 5px -2px; + height: 0; + font-size: 0; + line-height: 0; + border-width: 1px 0 0 0; +} +.ui-menu .ui-menu-item a { + text-decoration: none; + display: block; + padding: 2px .4em; + line-height: 1.5; + min-height: 0; /* support: IE7 */ + font-weight: normal; +} +.ui-menu .ui-menu-item a.ui-state-focus, +.ui-menu .ui-menu-item a.ui-state-active { + font-weight: normal; + margin: -1px; +} + +.ui-menu .ui-state-disabled { + font-weight: normal; + margin: .4em 0 .2em; + line-height: 1.5; +} +.ui-menu .ui-state-disabled a { + cursor: default; +} + +/* icon support */ +.ui-menu-icons { + position: relative; +} +.ui-menu-icons .ui-menu-item a { + position: relative; + padding-left: 2em; +} + +/* left-aligned */ +.ui-menu .ui-icon { + position: absolute; + top: .2em; + left: .2em; +} + +/* right-aligned */ +.ui-menu .ui-menu-icon { + position: static; + float: right; +} + +.ui-progressbar { + height: 2em; + text-align: left; + overflow: hidden; +} +.ui-progressbar .ui-progressbar-value { + margin: -1px; + height: 100%; +} +.ui-progressbar .ui-progressbar-overlay { + background: url("images/animated-overlay.gif"); + height: 100%; + filter: alpha(opacity=25); + opacity: 0.25; +} +.ui-progressbar-indeterminate .ui-progressbar-value { + background-image: none; +} + +.ui-resizable { + position: relative; +} +.ui-resizable-handle { + position: absolute; + font-size: 0.1px; + display: block; +} +.ui-resizable-disabled .ui-resizable-handle, +.ui-resizable-autohide .ui-resizable-handle { + display: none; +} +.ui-resizable-n { + cursor: n-resize; + height: 7px; + width: 100%; + top: -5px; + left: 0; +} +.ui-resizable-s { + cursor: s-resize; + height: 7px; + width: 100%; + bottom: -5px; + left: 0; +} +.ui-resizable-e { + cursor: e-resize; + width: 7px; + right: -5px; + top: 0; + height: 100%; +} +.ui-resizable-w { + cursor: w-resize; + width: 7px; + left: -5px; + top: 0; + height: 100%; +} +.ui-resizable-se { + cursor: se-resize; + width: 12px; + height: 12px; + right: 1px; + bottom: 1px; +} +.ui-resizable-sw { + cursor: sw-resize; + width: 9px; + height: 9px; + left: -5px; + bottom: -5px; +} +.ui-resizable-nw { + cursor: nw-resize; + width: 9px; + height: 9px; + left: -5px; + top: -5px; +} +.ui-resizable-ne { + cursor: ne-resize; + width: 9px; + height: 9px; + right: -5px; + top: -5px; +} + +.ui-selectable-helper { + position: absolute; + z-index: 100; + border: 1px dotted black; +} + +.ui-slider { + position: relative; + text-align: left; +} +.ui-slider .ui-slider-handle { + position: absolute; + z-index: 2; + width: 1.2em; + height: 1.2em; + cursor: default; +} +.ui-slider .ui-slider-range { + position: absolute; + z-index: 1; + font-size: .7em; + display: block; + border: 0; + background-position: 0 0; +} + +/* For IE8 - See #6727 */ +.ui-slider.ui-state-disabled .ui-slider-handle, +.ui-slider.ui-state-disabled .ui-slider-range { + filter: inherit; +} + +.ui-slider-horizontal { + height: .8em; +} +.ui-slider-horizontal .ui-slider-handle { + top: -.3em; + margin-left: -.6em; +} +.ui-slider-horizontal .ui-slider-range { + top: 0; + height: 100%; +} +.ui-slider-horizontal .ui-slider-range-min { + left: 0; +} +.ui-slider-horizontal .ui-slider-range-max { + right: 0; +} + +.ui-slider-vertical { + width: .8em; + height: 100px; +} +.ui-slider-vertical .ui-slider-handle { + left: -.3em; + margin-left: 0; + margin-bottom: -.6em; +} +.ui-slider-vertical .ui-slider-range { + left: 0; + width: 100%; +} +.ui-slider-vertical .ui-slider-range-min { + bottom: 0; +} +.ui-slider-vertical .ui-slider-range-max { + top: 0; +} + +.ui-spinner { + position: relative; + display: inline-block; + overflow: hidden; + padding: 0; + vertical-align: middle; +} +.ui-spinner-input { + border: none; + background: none; + color: inherit; + padding: 0; + margin: .2em 0; + vertical-align: middle; + margin-left: .4em; + margin-right: 22px; +} +.ui-spinner-button { + width: 16px; + height: 50%; + font-size: .5em; + padding: 0; + margin: 0; + text-align: center; + position: absolute; + cursor: default; + display: block; + overflow: hidden; + right: 0; +} +/* more specificity required here to overide default borders */ +.ui-spinner a.ui-spinner-button { + border-top: none; + border-bottom: none; + border-right: none; +} +/* vertical centre icon */ +.ui-spinner .ui-icon { + position: absolute; + margin-top: -8px; + top: 50%; + left: 0; +} +.ui-spinner-up { + top: 0; +} +.ui-spinner-down { + bottom: 0; +} + +/* TR overrides */ +.ui-spinner .ui-icon-triangle-1-s { + /* need to fix icons sprite */ + background-position: -65px -16px; +} + +.ui-tabs { + position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ + padding: .2em; +} +.ui-tabs .ui-tabs-nav { + margin: 0; + padding: .2em .2em 0; +} +.ui-tabs .ui-tabs-nav li { + list-style: none; + float: left; + position: relative; + top: 0; + margin: 1px .2em 0 0; + border-bottom-width: 0; + padding: 0; + white-space: nowrap; +} +.ui-tabs .ui-tabs-nav li a { + float: left; + padding: .5em 1em; + text-decoration: none; +} +.ui-tabs .ui-tabs-nav li.ui-tabs-active { + margin-bottom: -1px; + padding-bottom: 1px; +} +.ui-tabs .ui-tabs-nav li.ui-tabs-active a, +.ui-tabs .ui-tabs-nav li.ui-state-disabled a, +.ui-tabs .ui-tabs-nav li.ui-tabs-loading a { + cursor: text; +} +.ui-tabs .ui-tabs-nav li a, /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ +.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active a { + cursor: pointer; +} +.ui-tabs .ui-tabs-panel { + display: block; + border-width: 0; + padding: 1em 1.4em; + background: none; +} + +.ui-tooltip { + padding: 8px; + position: absolute; + z-index: 9999; + max-width: 300px; + -webkit-box-shadow: 0 0 5px #aaa; + box-shadow: 0 0 5px #aaa; +} +body .ui-tooltip { + border-width: 2px; +} + +/* Component containers +----------------------------------*/ +.ui-widget { + font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; + font-size: 1.1em/*{fsDefault}*/; +} +.ui-widget .ui-widget { + font-size: 1em; +} +.ui-widget input, +.ui-widget select, +.ui-widget textarea, +.ui-widget button { + font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; + font-size: 1em; +} +.ui-widget-content { + border: 1px solid #aaaaaa/*{borderColorContent}*/; + background: #ffffff/*{bgColorContent}*/ url(images/ui-bg_flat_75_ffffff_40x100.png)/*{bgImgUrlContent}*/ 50%/*{bgContentXPos}*/ 50%/*{bgContentYPos}*/ repeat-x/*{bgContentRepeat}*/; + color: #222222/*{fcContent}*/; +} +.ui-widget-content a { + color: #222222/*{fcContent}*/; +} +.ui-widget-header { + border: 1px solid #aaaaaa/*{borderColorHeader}*/; + background: #cccccc/*{bgColorHeader}*/ url(images/ui-bg_highlight-soft_75_cccccc_1x100.png)/*{bgImgUrlHeader}*/ 50%/*{bgHeaderXPos}*/ 50%/*{bgHeaderYPos}*/ repeat-x/*{bgHeaderRepeat}*/; + color: #222222/*{fcHeader}*/; + font-weight: bold; +} +.ui-widget-header a { + color: #222222/*{fcHeader}*/; +} + +/* Interaction states +----------------------------------*/ +.ui-state-default, +.ui-widget-content .ui-state-default, +.ui-widget-header .ui-state-default { + border: 1px solid #d3d3d3/*{borderColorDefault}*/; + background: #e6e6e6/*{bgColorDefault}*/ url(images/ui-bg_glass_75_e6e6e6_1x400.png)/*{bgImgUrlDefault}*/ 50%/*{bgDefaultXPos}*/ 50%/*{bgDefaultYPos}*/ repeat-x/*{bgDefaultRepeat}*/; + font-weight: normal/*{fwDefault}*/; + color: #555555/*{fcDefault}*/; +} +.ui-state-default a, +.ui-state-default a:link, +.ui-state-default a:visited { + color: #555555/*{fcDefault}*/; + text-decoration: none; +} +.ui-state-hover, +.ui-widget-content .ui-state-hover, +.ui-widget-header .ui-state-hover, +.ui-state-focus, +.ui-widget-content .ui-state-focus, +.ui-widget-header .ui-state-focus { + border: 1px solid #999999/*{borderColorHover}*/; + background: #dadada/*{bgColorHover}*/ url(images/ui-bg_glass_75_dadada_1x400.png)/*{bgImgUrlHover}*/ 50%/*{bgHoverXPos}*/ 50%/*{bgHoverYPos}*/ repeat-x/*{bgHoverRepeat}*/; + font-weight: normal/*{fwDefault}*/; + color: #212121/*{fcHover}*/; +} +.ui-state-hover a, +.ui-state-hover a:hover, +.ui-state-hover a:link, +.ui-state-hover a:visited { + color: #212121/*{fcHover}*/; + text-decoration: none; +} +.ui-state-active, +.ui-widget-content .ui-state-active, +.ui-widget-header .ui-state-active { + border: 1px solid #aaaaaa/*{borderColorActive}*/; + background: #ffffff/*{bgColorActive}*/ url(images/ui-bg_glass_65_ffffff_1x400.png)/*{bgImgUrlActive}*/ 50%/*{bgActiveXPos}*/ 50%/*{bgActiveYPos}*/ repeat-x/*{bgActiveRepeat}*/; + font-weight: normal/*{fwDefault}*/; + color: #212121/*{fcActive}*/; +} +.ui-state-active a, +.ui-state-active a:link, +.ui-state-active a:visited { + color: #212121/*{fcActive}*/; + text-decoration: none; +} + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, +.ui-widget-content .ui-state-highlight, +.ui-widget-header .ui-state-highlight { + border: 1px solid #fcefa1/*{borderColorHighlight}*/; + background: #fbf9ee/*{bgColorHighlight}*/ url(images/ui-bg_glass_55_fbf9ee_1x400.png)/*{bgImgUrlHighlight}*/ 50%/*{bgHighlightXPos}*/ 50%/*{bgHighlightYPos}*/ repeat-x/*{bgHighlightRepeat}*/; + color: #363636/*{fcHighlight}*/; +} +.ui-state-highlight a, +.ui-widget-content .ui-state-highlight a, +.ui-widget-header .ui-state-highlight a { + color: #363636/*{fcHighlight}*/; +} +.ui-state-error, +.ui-widget-content .ui-state-error, +.ui-widget-header .ui-state-error { + border: 1px solid #cd0a0a/*{borderColorError}*/; + background: #fef1ec/*{bgColorError}*/ url(images/ui-bg_glass_95_fef1ec_1x400.png)/*{bgImgUrlError}*/ 50%/*{bgErrorXPos}*/ 50%/*{bgErrorYPos}*/ repeat-x/*{bgErrorRepeat}*/; + color: #cd0a0a/*{fcError}*/; +} +.ui-state-error a, +.ui-widget-content .ui-state-error a, +.ui-widget-header .ui-state-error a { + color: #cd0a0a/*{fcError}*/; +} +.ui-state-error-text, +.ui-widget-content .ui-state-error-text, +.ui-widget-header .ui-state-error-text { + color: #cd0a0a/*{fcError}*/; +} +.ui-priority-primary, +.ui-widget-content .ui-priority-primary, +.ui-widget-header .ui-priority-primary { + font-weight: bold; +} +.ui-priority-secondary, +.ui-widget-content .ui-priority-secondary, +.ui-widget-header .ui-priority-secondary { + opacity: .7; + filter:Alpha(Opacity=70); + font-weight: normal; +} +.ui-state-disabled, +.ui-widget-content .ui-state-disabled, +.ui-widget-header .ui-state-disabled { + opacity: .35; + filter:Alpha(Opacity=35); + background-image: none; +} +.ui-state-disabled .ui-icon { + filter:Alpha(Opacity=35); /* For IE8 - See #6059 */ +} + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { + width: 16px; + height: 16px; +} +.ui-icon, +.ui-widget-content .ui-icon { + background-image: url(images/ui-icons_222222_256x240.png)/*{iconsContent}*/; +} +.ui-widget-header .ui-icon { + background-image: url(images/ui-icons_222222_256x240.png)/*{iconsHeader}*/; +} +.ui-state-default .ui-icon { + background-image: url(images/ui-icons_888888_256x240.png)/*{iconsDefault}*/; +} +.ui-state-hover .ui-icon, +.ui-state-focus .ui-icon { + background-image: url(images/ui-icons_454545_256x240.png)/*{iconsHover}*/; +} +.ui-state-active .ui-icon { + background-image: url(images/ui-icons_454545_256x240.png)/*{iconsActive}*/; +} +.ui-state-highlight .ui-icon { + background-image: url(images/ui-icons_2e83ff_256x240.png)/*{iconsHighlight}*/; +} +.ui-state-error .ui-icon, +.ui-state-error-text .ui-icon { + background-image: url(images/ui-icons_cd0a0a_256x240.png)/*{iconsError}*/; +} + +/* positioning */ +.ui-icon-blank { background-position: 16px 16px; } +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-on { background-position: -96px -144px; } +.ui-icon-radio-off { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-all, +.ui-corner-top, +.ui-corner-left, +.ui-corner-tl { + border-top-left-radius: 4px/*{cornerRadius}*/; +} +.ui-corner-all, +.ui-corner-top, +.ui-corner-right, +.ui-corner-tr { + border-top-right-radius: 4px/*{cornerRadius}*/; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-left, +.ui-corner-bl { + border-bottom-left-radius: 4px/*{cornerRadius}*/; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-right, +.ui-corner-br { + border-bottom-right-radius: 4px/*{cornerRadius}*/; +} + +/* Overlays */ +.ui-widget-overlay { + background: #aaaaaa/*{bgColorOverlay}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlOverlay}*/ 50%/*{bgOverlayXPos}*/ 50%/*{bgOverlayYPos}*/ repeat-x/*{bgOverlayRepeat}*/; + opacity: .3/*{opacityOverlay}*/; + filter: Alpha(Opacity=30)/*{opacityFilterOverlay}*/; +} +.ui-widget-shadow { + margin: -8px/*{offsetTopShadow}*/ 0 0 -8px/*{offsetLeftShadow}*/; + padding: 8px/*{thicknessShadow}*/; + background: #aaaaaa/*{bgColorShadow}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlShadow}*/ 50%/*{bgShadowXPos}*/ 50%/*{bgShadowYPos}*/ repeat-x/*{bgShadowRepeat}*/; + opacity: .3/*{opacityShadow}*/; + filter: Alpha(Opacity=30)/*{opacityFilterShadow}*/; + border-radius: 8px/*{cornerRadiusShadow}*/; +} diff --git a/src/jsp/web/style/jqueryui/jquery-ui-1.10.3.custom.min.css b/src/jsp/web/style/jqueryui/jquery-ui-1.10.3.custom.min.css new file mode 100644 index 0000000..de55499 --- /dev/null +++ b/src/jsp/web/style/jqueryui/jquery-ui-1.10.3.custom.min.css @@ -0,0 +1,7 @@ +/*! jQuery UI - v1.10.3 - 2013-09-22 +* http://jqueryui.com +* Includes: jquery.ui.core.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.button.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.tabs.css, jquery.ui.theme.css +* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=highlight_soft&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=flat&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=glass&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=glass&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=glass&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=glass&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px +* Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */ + +.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-button{display:inline-block;position:relative;padding:0;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2.2em}button.ui-button-icon-only{width:2.4em}.ui-button-icons-only{width:3.4em}button.ui-button-icons-only{width:3.7em}.ui-button .ui-button-text{display:block;line-height:normal}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button-icon-only .ui-button-text,.ui-button-icons-only .ui-button-text{padding:.4em;text-indent:-9999999px}.ui-button-text-icon-primary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 1em .4em 2.1em}.ui-button-text-icon-secondary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 2.1em .4em 1em}.ui-button-text-icons .ui-button-text{padding-left:2.1em;padding-right:2.1em}input.ui-button{padding:.4em 1em}.ui-button-icon-only .ui-icon,.ui-button-text-icon-primary .ui-icon,.ui-button-text-icon-secondary .ui-icon,.ui-button-text-icons .ui-icon,.ui-button-icons-only .ui-icon{position:absolute;top:50%;margin-top:-8px}.ui-button-icon-only .ui-icon{left:50%;margin-left:-8px}.ui-button-text-icon-primary .ui-button-icon-primary,.ui-button-text-icons .ui-button-icon-primary,.ui-button-icons-only .ui-button-icon-primary{left:.5em}.ui-button-text-icon-secondary .ui-button-icon-secondary,.ui-button-text-icons .ui-button-icon-secondary,.ui-button-icons-only .ui-button-icon-secondary{right:.5em}.ui-buttonset{margin-right:7px}.ui-buttonset .ui-button{margin-left:0;margin-right:-.3em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-dialog{position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:21px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-se{width:12px;height:12px;right:-5px;bottom:-5px;background-position:16px 16px}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-menu{list-style:none;padding:2px;margin:0;display:block;outline:none}.ui-menu .ui-menu{margin-top:-3px;position:absolute}.ui-menu .ui-menu-item{margin:0;padding:0;width:100%;list-style-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}.ui-menu .ui-menu-divider{margin:5px -2px 5px -2px;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-menu-item a{text-decoration:none;display:block;padding:2px .4em;line-height:1.5;min-height:0;font-weight:normal}.ui-menu .ui-menu-item a.ui-state-focus,.ui-menu .ui-menu-item a.ui-state-active{font-weight:normal;margin:-1px}.ui-menu .ui-state-disabled{font-weight:normal;margin:.4em 0 .2em;line-height:1.5}.ui-menu .ui-state-disabled a{cursor:default}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item a{position:relative;padding-left:2em}.ui-menu .ui-icon{position:absolute;top:.2em;left:.2em}.ui-menu .ui-menu-icon{position:static;float:right}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav li a{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active a,.ui-tabs .ui-tabs-nav li.ui-state-disabled a,.ui-tabs .ui-tabs-nav li.ui-tabs-loading a{cursor:text}.ui-tabs .ui-tabs-nav li a,.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active a{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-widget{font-family:Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #aaa;background:#fff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x;color:#222}.ui-widget-content a{color:#222}.ui-widget-header{border:1px solid #aaa;background:#ccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x;color:#222;font-weight:bold}.ui-widget-header a{color:#222}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #d3d3d3;background:#e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x;font-weight:normal;color:#555}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#555;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #999;background:#dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x;font-weight:normal;color:#212121}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited{color:#212121;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #aaa;background:#fff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x;font-weight:normal;color:#212121}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#212121;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fcefa1;background:#fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x;color:#363636}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x;color:#cd0a0a}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#cd0a0a}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#cd0a0a}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url(images/ui-icons_222222_256x240.png)}.ui-widget-header .ui-icon{background-image:url(images/ui-icons_222222_256x240.png)}.ui-state-default .ui-icon{background-image:url(images/ui-icons_888888_256x240.png)}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url(images/ui-icons_454545_256x240.png)}.ui-state-active .ui-icon{background-image:url(images/ui-icons_454545_256x240.png)}.ui-state-highlight .ui-icon{background-image:url(images/ui-icons_2e83ff_256x240.png)}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url(images/ui-icons_cd0a0a_256x240.png)}.ui-icon-blank{background-position:16px 16px}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:4px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:4px}.ui-widget-overlay{background:#aaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{margin:-8px 0 0 -8px;padding:8px;background:#aaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;opacity:.3;filter:Alpha(Opacity=30);border-radius:8px} \ No newline at end of file diff --git a/src/jsp/web/style/transmission/common.css b/src/jsp/web/style/transmission/common.css new file mode 100644 index 0000000..aa4e9f4 --- /dev/null +++ b/src/jsp/web/style/transmission/common.css @@ -0,0 +1,294 @@ +/*-------------------------------------- G L O B A L --------------------------------------*/ +html { margin: 0; padding: 0; height: 100%; } + +body { font: 62.5% "lucida grande", Tahoma, Verdana, Arial, Helvetica, sans-serif; /* Resets 1em to 10px */ color: #222; /* !important; */ background: #FFF; text-align: center; margin: 0 0 30px; overflow: hidden; } +body img { border: none; } +body a { outline: 0; } + +/*** +**** +**** ABOUT DIALOG +**** +***/ +#about-dialog > * { text-align: center; } +#about-dialog > #about-logo { background: transparent url("images/logo.png") top left no-repeat; width: 64px; height: 64px; margin-left: 100px; } +#about-dialog > #about-title { font-size: 1.3em; font-weight: bold; } + +/*** +**** +**** TOOLBAR +**** +***/ +div#toolbar-area { background-color: #cccccc; background-image: -webkit-gradient(linear, left top, left bottom, from(#dddddd), to(#bbbbbb)); background-image: -webkit-linear-gradient(top, #dddddd, #bbbbbb); background-image: -moz-linear-gradient(top, #dddddd, #bbbbbb); background-image: -ms-linear-gradient(top, #dddddd, #bbbbbb); background-image: -o-linear-gradient(top, #dddddd, #bbbbbb); background-image: linear-gradient(top, #dddddd, #bbbbbb); border-bottom: 1px solid #aaaaaa; } + +div#toolbar { width: 100%; height: 36px; margin: 0px; padding: 2px; border-bottom: 1px solid #aaaaaa; background-color: #cccccc; background-image: -webkit-gradient(linear, left top, left bottom, from(#dddddd), to(#bbbbbb)); background-image: -webkit-linear-gradient(top, #dddddd, #bbbbbb); background-image: -moz-linear-gradient(top, #dddddd, #bbbbbb); background-image: -ms-linear-gradient(top, #dddddd, #bbbbbb); background-image: -o-linear-gradient(top, #dddddd, #bbbbbb); background-image: linear-gradient(top, #dddddd, #bbbbbb); } +div#toolbar > * { width: 34px; height: 34px; float: left; border: none; padding: 0px 3px; } +div#toolbar > div#toolbar-separator { height: 25px; margin-top: 8px; margin-bottom: 8px; border-left: 1px solid #aaaaaa; width: 3px; } +div#toolbar div#toolbar-open { -moz-border-radius-topleft: 10px; -moz-border-radius-bottomleft: 10px; border-top-left-radius: 10px; border-bottom-left-radius: 10px; background-color: #dddddd; background-image: url("images/toolbar-add.png"); /* fallback */ background-image: url("images/toolbar-add.png"), -webkit-gradient(linear, left top, left bottom, from(white), to(#bbbbbb)); /* Saf4+, Chrome */ background-image: url("images/toolbar-add.png"), -webkit-linear-gradient(top, white, #bbbbbb); /* Chrome 10+, Saf5.1+ */ background-image: url("images/toolbar-add.png"), -moz-linear-gradient(top, white, #bbbbbb); /* FF3.6+ */ background-image: url("images/toolbar-add.png"), -ms-linear-gradient(top, white, #bbbbbb); /* IE10 */ background-image: url("images/toolbar-add.png"), -o-linear-gradient(top, white, #bbbbbb); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; margin-left: 4px; } +div#toolbar div#toolbar-open:active, div#toolbar div#toolbar-open.selected { background-color: #e6e6ff; background-image: url("images/toolbar-add.png"); /* fallback */ background-image: url("images/toolbar-add.png"), -webkit-gradient(linear, left top, left bottom, from(#cdcdff), to(white)); /* Saf4+, Chrome */ background-image: url("images/toolbar-add.png"), -webkit-linear-gradient(top, #cdcdff, white); /* Chrome 10+, Saf5.1+ */ background-image: url("images/toolbar-add.png"), -moz-linear-gradient(top, #cdcdff, white); /* FF3.6+ */ background-image: url("images/toolbar-add.png"), -ms-linear-gradient(top, #cdcdff, white); /* IE10 */ background-image: url("images/toolbar-add.png"), -o-linear-gradient(top, #cdcdff, white); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#toolbar > div#toolbar-remove { -moz-border-radius-topright: 10px; -moz-border-radius-bottomright: 10px; border-top-right-radius: 10px; border-bottom-right-radius: 10px; background-color: #dddddd; background-image: url("images/toolbar-remove.png"); /* fallback */ background-image: url("images/toolbar-remove.png"), -webkit-gradient(linear, left top, left bottom, from(white), to(#bbbbbb)); /* Saf4+, Chrome */ background-image: url("images/toolbar-remove.png"), -webkit-linear-gradient(top, white, #bbbbbb); /* Chrome 10+, Saf5.1+ */ background-image: url("images/toolbar-remove.png"), -moz-linear-gradient(top, white, #bbbbbb); /* FF3.6+ */ background-image: url("images/toolbar-remove.png"), -ms-linear-gradient(top, white, #bbbbbb); /* IE10 */ background-image: url("images/toolbar-remove.png"), -o-linear-gradient(top, white, #bbbbbb); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; margin-right: 4px; } +div#toolbar > div#toolbar-remove:active, div#toolbar > div#toolbar-remove.selected { background-color: #e6e6ff; background-image: url("images/toolbar-remove.png"); /* fallback */ background-image: url("images/toolbar-remove.png"), -webkit-gradient(linear, left top, left bottom, from(#cdcdff), to(white)); /* Saf4+, Chrome */ background-image: url("images/toolbar-remove.png"), -webkit-linear-gradient(top, #cdcdff, white); /* Chrome 10+, Saf5.1+ */ background-image: url("images/toolbar-remove.png"), -moz-linear-gradient(top, #cdcdff, white); /* FF3.6+ */ background-image: url("images/toolbar-remove.png"), -ms-linear-gradient(top, #cdcdff, white); /* IE10 */ background-image: url("images/toolbar-remove.png"), -o-linear-gradient(top, #cdcdff, white); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#toolbar > div#toolbar-start { background-color: #dddddd; background-image: url("images/toolbar-start.png"); /* fallback */ background-image: url("images/toolbar-start.png"), -webkit-gradient(linear, left top, left bottom, from(white), to(#bbbbbb)); /* Saf4+, Chrome */ background-image: url("images/toolbar-start.png"), -webkit-linear-gradient(top, white, #bbbbbb); /* Chrome 10+, Saf5.1+ */ background-image: url("images/toolbar-start.png"), -moz-linear-gradient(top, white, #bbbbbb); /* FF3.6+ */ background-image: url("images/toolbar-start.png"), -ms-linear-gradient(top, white, #bbbbbb); /* IE10 */ background-image: url("images/toolbar-start.png"), -o-linear-gradient(top, white, #bbbbbb); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#toolbar > div#toolbar-start:active, div#toolbar > div#toolbar-start.selected { background-color: #e6e6ff; background-image: url("images/toolbar-start.png"); /* fallback */ background-image: url("images/toolbar-start.png"), -webkit-gradient(linear, left top, left bottom, from(#cdcdff), to(white)); /* Saf4+, Chrome */ background-image: url("images/toolbar-start.png"), -webkit-linear-gradient(top, #cdcdff, white); /* Chrome 10+, Saf5.1+ */ background-image: url("images/toolbar-start.png"), -moz-linear-gradient(top, #cdcdff, white); /* FF3.6+ */ background-image: url("images/toolbar-start.png"), -ms-linear-gradient(top, #cdcdff, white); /* IE10 */ background-image: url("images/toolbar-start.png"), -o-linear-gradient(top, #cdcdff, white); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#toolbar > div#toolbar-pause { background-color: #dddddd; background-image: url("images/toolbar-stop.png"); /* fallback */ background-image: url("images/toolbar-stop.png"), -webkit-gradient(linear, left top, left bottom, from(white), to(#bbbbbb)); /* Saf4+, Chrome */ background-image: url("images/toolbar-stop.png"), -webkit-linear-gradient(top, white, #bbbbbb); /* Chrome 10+, Saf5.1+ */ background-image: url("images/toolbar-stop.png"), -moz-linear-gradient(top, white, #bbbbbb); /* FF3.6+ */ background-image: url("images/toolbar-stop.png"), -ms-linear-gradient(top, white, #bbbbbb); /* IE10 */ background-image: url("images/toolbar-stop.png"), -o-linear-gradient(top, white, #bbbbbb); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#toolbar > div#toolbar-pause:active, div#toolbar > div#toolbar-pause.selected { background-color: #e6e6ff; background-image: url("images/toolbar-stop.png"); /* fallback */ background-image: url("images/toolbar-stop.png"), -webkit-gradient(linear, left top, left bottom, from(#cdcdff), to(white)); /* Saf4+, Chrome */ background-image: url("images/toolbar-stop.png"), -webkit-linear-gradient(top, #cdcdff, white); /* Chrome 10+, Saf5.1+ */ background-image: url("images/toolbar-stop.png"), -moz-linear-gradient(top, #cdcdff, white); /* FF3.6+ */ background-image: url("images/toolbar-stop.png"), -ms-linear-gradient(top, #cdcdff, white); /* IE10 */ background-image: url("images/toolbar-stop.png"), -o-linear-gradient(top, #cdcdff, white); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#toolbar > div#toolbar-start-all { -moz-border-radius-topleft: 10px; -moz-border-radius-bottomleft: 10px; border-top-left-radius: 10px; border-bottom-left-radius: 10px; background-color: #dddddd; background-image: url("images/toolbar-start-all.png"); /* fallback */ background-image: url("images/toolbar-start-all.png"), -webkit-gradient(linear, left top, left bottom, from(white), to(#bbbbbb)); /* Saf4+, Chrome */ background-image: url("images/toolbar-start-all.png"), -webkit-linear-gradient(top, white, #bbbbbb); /* Chrome 10+, Saf5.1+ */ background-image: url("images/toolbar-start-all.png"), -moz-linear-gradient(top, white, #bbbbbb); /* FF3.6+ */ background-image: url("images/toolbar-start-all.png"), -ms-linear-gradient(top, white, #bbbbbb); /* IE10 */ background-image: url("images/toolbar-start-all.png"), -o-linear-gradient(top, white, #bbbbbb); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#toolbar > div#toolbar-start-all:active, div#toolbar > div#toolbar-start-all.selected { background-color: #e6e6ff; background-image: url("images/toolbar-start-all.png"); /* fallback */ background-image: url("images/toolbar-start-all.png"), -webkit-gradient(linear, left top, left bottom, from(#cdcdff), to(white)); /* Saf4+, Chrome */ background-image: url("images/toolbar-start-all.png"), -webkit-linear-gradient(top, #cdcdff, white); /* Chrome 10+, Saf5.1+ */ background-image: url("images/toolbar-start-all.png"), -moz-linear-gradient(top, #cdcdff, white); /* FF3.6+ */ background-image: url("images/toolbar-start-all.png"), -ms-linear-gradient(top, #cdcdff, white); /* IE10 */ background-image: url("images/toolbar-start-all.png"), -o-linear-gradient(top, #cdcdff, white); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#toolbar > div#toolbar-pause-all { -moz-border-radius-topright: 10px; -moz-border-radius-bottomright: 10px; border-top-right-radius: 10px; border-bottom-right-radius: 10px; background-color: #dddddd; background-image: url("images/toolbar-stop-all.png"); /* fallback */ background-image: url("images/toolbar-stop-all.png"), -webkit-gradient(linear, left top, left bottom, from(white), to(#bbbbbb)); /* Saf4+, Chrome */ background-image: url("images/toolbar-stop-all.png"), -webkit-linear-gradient(top, white, #bbbbbb); /* Chrome 10+, Saf5.1+ */ background-image: url("images/toolbar-stop-all.png"), -moz-linear-gradient(top, white, #bbbbbb); /* FF3.6+ */ background-image: url("images/toolbar-stop-all.png"), -ms-linear-gradient(top, white, #bbbbbb); /* IE10 */ background-image: url("images/toolbar-stop-all.png"), -o-linear-gradient(top, white, #bbbbbb); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#toolbar > div#toolbar-pause-all:active, div#toolbar > div#toolbar-pause-all.selected { background-color: #e6e6ff; background-image: url("images/toolbar-stop-all.png"); /* fallback */ background-image: url("images/toolbar-stop-all.png"), -webkit-gradient(linear, left top, left bottom, from(#cdcdff), to(white)); /* Saf4+, Chrome */ background-image: url("images/toolbar-stop-all.png"), -webkit-linear-gradient(top, #cdcdff, white); /* Chrome 10+, Saf5.1+ */ background-image: url("images/toolbar-stop-all.png"), -moz-linear-gradient(top, #cdcdff, white); /* FF3.6+ */ background-image: url("images/toolbar-stop-all.png"), -ms-linear-gradient(top, #cdcdff, white); /* IE10 */ background-image: url("images/toolbar-stop-all.png"), -o-linear-gradient(top, #cdcdff, white); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#toolbar > div#toolbar-search { -moz-border-radius: 10px; border-radius: 10px; background-color: #dddddd; background-image: url("images/toolbar-search.png"); /* fallback */ background-image: url("images/toolbar-search.png"), -webkit-gradient(linear, left top, left bottom, from(white), to(#bbbbbb)); /* Saf4+, Chrome */ background-image: url("images/toolbar-search.png"), -webkit-linear-gradient(top, white, #bbbbbb); /* Chrome 10+, Saf5.1+ */ background-image: url("images/toolbar-search.png"), -moz-linear-gradient(top, white, #bbbbbb); /* FF3.6+ */ background-image: url("images/toolbar-search.png"), -ms-linear-gradient(top, white, #bbbbbb); /* IE10 */ background-image: url("images/toolbar-search.png"), -o-linear-gradient(top, white, #bbbbbb); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; margin-left: 10px; } +div#toolbar > div#toolbar-search:active, div#toolbar > div#toolbar-search.selected { background-color: #e6e6ff; background-image: url("images/toolbar-search.png"); /* fallback */ background-image: url("images/toolbar-search.png"), -webkit-gradient(linear, left top, left bottom, from(#cdcdff), to(white)); /* Saf4+, Chrome */ background-image: url("images/toolbar-search.png"), -webkit-linear-gradient(top, #cdcdff, white); /* Chrome 10+, Saf5.1+ */ background-image: url("images/toolbar-search.png"), -moz-linear-gradient(top, #cdcdff, white); /* FF3.6+ */ background-image: url("images/toolbar-search.png"), -ms-linear-gradient(top, #cdcdff, white); /* IE10 */ background-image: url("images/toolbar-search.png"), -o-linear-gradient(top, #cdcdff, white); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#toolbar > div#toolbar-remote-search { -moz-border-radius: 10px; border-radius: 10px; background-color: #dddddd; background-image: url("images/toolbar-remote-search.png"); /* fallback */ background-image: url("images/toolbar-remote-search.png"), -webkit-gradient(linear, left top, left bottom, from(white), to(#bbbbbb)); /* Saf4+, Chrome */ background-image: url("images/toolbar-remote-search.png"), -webkit-linear-gradient(top, white, #bbbbbb); /* Chrome 10+, Saf5.1+ */ background-image: url("images/toolbar-remote-search.png"), -moz-linear-gradient(top, white, #bbbbbb); /* FF3.6+ */ background-image: url("images/toolbar-remote-search.png"), -ms-linear-gradient(top, white, #bbbbbb); /* IE10 */ background-image: url("images/toolbar-remote-search.png"), -o-linear-gradient(top, white, #bbbbbb); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; margin-left: 10px; margin-right: 10px; } +div#toolbar > div#toolbar-remote-search:active, div#toolbar > div#toolbar-remote-search.selected { background-color: #e6e6ff; background-image: url("images/toolbar-remote-search.png"); /* fallback */ background-image: url("images/toolbar-remote-search.png"), -webkit-gradient(linear, left top, left bottom, from(#cdcdff), to(white)); /* Saf4+, Chrome */ background-image: url("images/toolbar-remote-search.png"), -webkit-linear-gradient(top, #cdcdff, white); /* Chrome 10+, Saf5.1+ */ background-image: url("images/toolbar-remote-search.png"), -moz-linear-gradient(top, #cdcdff, white); /* FF3.6+ */ background-image: url("images/toolbar-remote-search.png"), -ms-linear-gradient(top, #cdcdff, white); /* IE10 */ background-image: url("images/toolbar-remote-search.png"), -o-linear-gradient(top, #cdcdff, white); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#toolbar > div#toolbar-remote { -moz-border-radius: 10px; border-radius: 10px; background-color: #dddddd; background-image: url("images/toolbar-remote.png"); /* fallback */ background-image: url("images/toolbar-remote.png"), -webkit-gradient(linear, left top, left bottom, from(white), to(#bbbbbb)); /* Saf4+, Chrome */ background-image: url("images/toolbar-remote.png"), -webkit-linear-gradient(top, white, #bbbbbb); /* Chrome 10+, Saf5.1+ */ background-image: url("images/toolbar-remote.png"), -moz-linear-gradient(top, white, #bbbbbb); /* FF3.6+ */ background-image: url("images/toolbar-remote.png"), -ms-linear-gradient(top, white, #bbbbbb); /* IE10 */ background-image: url("images/toolbar-remote.png"), -o-linear-gradient(top, white, #bbbbbb); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#toolbar > div#toolbar-remote:active, div#toolbar > div#toolbar-remote.selected { background-color: #e6e6ff; background-image: url("images/toolbar-remote.png"); /* fallback */ background-image: url("images/toolbar-remote.png"), -webkit-gradient(linear, left top, left bottom, from(#cdcdff), to(white)); /* Saf4+, Chrome */ background-image: url("images/toolbar-remote.png"), -webkit-linear-gradient(top, #cdcdff, white); /* Chrome 10+, Saf5.1+ */ background-image: url("images/toolbar-remote.png"), -moz-linear-gradient(top, #cdcdff, white); /* FF3.6+ */ background-image: url("images/toolbar-remote.png"), -ms-linear-gradient(top, #cdcdff, white); /* IE10 */ background-image: url("images/toolbar-remote.png"), -o-linear-gradient(top, #cdcdff, white); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#toolbar > div#toolbar-select { background-color: #dddddd; background-image: url("images/toolbar-pointer.png"); /* fallback */ background-image: url("images/toolbar-pointer.png"), -webkit-gradient(linear, left top, left bottom, from(white), to(#bbbbbb)); /* Saf4+, Chrome */ background-image: url("images/toolbar-pointer.png"), -webkit-linear-gradient(top, white, #bbbbbb); /* Chrome 10+, Saf5.1+ */ background-image: url("images/toolbar-pointer.png"), -moz-linear-gradient(top, white, #bbbbbb); /* FF3.6+ */ background-image: url("images/toolbar-pointer.png"), -ms-linear-gradient(top, white, #bbbbbb); /* IE10 */ background-image: url("images/toolbar-pointer.png"), -o-linear-gradient(top, white, #bbbbbb); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#toolbar > div#toolbar-select:active, div#toolbar > div#toolbar-select.selected { background-color: #e6e6ff; background-image: url("images/toolbar-pointer.png"); /* fallback */ background-image: url("images/toolbar-pointer.png"), -webkit-gradient(linear, left top, left bottom, from(#cdcdff), to(white)); /* Saf4+, Chrome */ background-image: url("images/toolbar-pointer.png"), -webkit-linear-gradient(top, #cdcdff, white); /* Chrome 10+, Saf5.1+ */ background-image: url("images/toolbar-pointer.png"), -moz-linear-gradient(top, #cdcdff, white); /* FF3.6+ */ background-image: url("images/toolbar-pointer.png"), -ms-linear-gradient(top, #cdcdff, white); /* IE10 */ background-image: url("images/toolbar-pointer.png"), -o-linear-gradient(top, #cdcdff, white); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#toolbar > div#toolbar-inspector { -moz-border-radius: 10px; border-radius: 10px; background-color: #dddddd; background-image: url("images/toolbar-info.png"); /* fallback */ background-image: url("images/toolbar-info.png"), -webkit-gradient(linear, left top, left bottom, from(white), to(#bbbbbb)); /* Saf4+, Chrome */ background-image: url("images/toolbar-info.png"), -webkit-linear-gradient(top, white, #bbbbbb); /* Chrome 10+, Saf5.1+ */ background-image: url("images/toolbar-info.png"), -moz-linear-gradient(top, white, #bbbbbb); /* FF3.6+ */ background-image: url("images/toolbar-info.png"), -ms-linear-gradient(top, white, #bbbbbb); /* IE10 */ background-image: url("images/toolbar-info.png"), -o-linear-gradient(top, white, #bbbbbb); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; float: right; margin-right: 4px; } +div#toolbar > div#toolbar-inspector:active, div#toolbar > div#toolbar-inspector.selected { background-color: #e6e6ff; background-image: url("images/toolbar-info.png"); /* fallback */ background-image: url("images/toolbar-info.png"), -webkit-gradient(linear, left top, left bottom, from(#cdcdff), to(white)); /* Saf4+, Chrome */ background-image: url("images/toolbar-info.png"), -webkit-linear-gradient(top, #cdcdff, white); /* Chrome 10+, Saf5.1+ */ background-image: url("images/toolbar-info.png"), -moz-linear-gradient(top, #cdcdff, white); /* FF3.6+ */ background-image: url("images/toolbar-info.png"), -ms-linear-gradient(top, #cdcdff, white); /* IE10 */ background-image: url("images/toolbar-info.png"), -o-linear-gradient(top, #cdcdff, white); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#toolbar > *.disabled { opacity: 0.25; } + +/*** +**** +**** STATUSBAR +**** +***/ +#statusbar { height: 26px; width: 100%; border-bottom: 1px solid #aaaaaa; overflow: hidden; position: relative; background-color: #cccccc; background-image: -webkit-gradient(linear, left top, left bottom, from(#dddddd), to(#bbbbbb)); background-image: -webkit-linear-gradient(top, #dddddd, #bbbbbb); background-image: -moz-linear-gradient(top, #dddddd, #bbbbbb); background-image: -ms-linear-gradient(top, #dddddd, #bbbbbb); background-image: -o-linear-gradient(top, #dddddd, #bbbbbb); background-image: linear-gradient(top, #dddddd, #bbbbbb); } +#statusbar #filter { float: left; margin-left: 5px; } +#statusbar #filter input#torrent_search { height: 18px; width: 100px; border-radius: 6px; } +#statusbar #filter input#torrent_search.blur { color: #999; } +#statusbar #filter #filter-count { margin-left: 8px; } + +#speed-info { float: right; margin-top: 5px; margin-right: 10px; } +#speed-info * { display: inline-block; } +#speed-info #speed-up-icon { margin-left: 8px; width: 8px; height: 8px; background: url("images/arrow-up.png") bottom no-repeat; } +#speed-info #speed-dn-icon { width: 8px; height: 8px; background: url("images/arrow-down.png") bottom no-repeat; } +#speed-info #speed-up-container, #speed-info #speed-dn-container { display: inline; } + +/*** +**** +**** TORRENT CONTAINER +**** +***/ +div#torrent_container { position: fixed; top: 68px; bottom: 22px; right: 0px; left: 0px; padding: 0px; margin: 0px; overflow: auto; -webkit-overflow-scrolling: touch; } + +ul.torrent_list { width: 100%; margin: 0; padding: 0; text-align: left; cursor: pointer; /** Progressbar Each progressbar has three elemens: a parent container and two children, complete and incomplete. The only thing needed to set the progressbar percentage is to set the complete child's width as a percentage. This is because incomplete is pinned to the full width and height of the parent, and complete is pinned to the left side of the parent and has a higher z-index. The progressbar has different colors depending on its state, so there are five 'decorator' classNames: paused, queued, magnet, leeching, seeding. */ } +ul.torrent_list li.torrent { border-bottom: 1px solid #cccccc; padding: 4px 30px 5px 14px; color: #666; background-color: white; } +ul.torrent_list li.torrent.compact { padding: 4px; } +ul.torrent_list li.torrent.even { background-color: #F7F7F7; } +ul.torrent_list li.torrent.selected { background-color: #cdcdff; } +ul.torrent_list li.torrent.compact div.torrent_name { color: black; } +ul.torrent_list li.torrent a { float: right; position: relative; right: -22px; top: 1px; } +ul.torrent_list li.torrent a img { position: relative; right: -10px; } +ul.torrent_list li.torrent a div { background: url("images/buttons/torrent_buttons.png"); height: 14px; width: 14px; } +ul.torrent_list li.torrent a div.torrent_pause { background-position: left top; } +ul.torrent_list li.torrent a div.torrent_resume { background-position: center top; } +ul.torrent_list li.torrent a:active div.torrent_pause { background-position: left bottom; } +ul.torrent_list li.torrent a:active div.torrent_resume { background-position: center bottom; } +ul.torrent_list li.torrent a:hover div.torrent_pause { background-position: left center; } +ul.torrent_list li.torrent a:hover div.torrent_resume { background-position: center center; } +ul.torrent_list li.torrent div.torrent_name { font-size: 1.3em; font-weight: bold; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #222; margin-top: 2px; margin-bottom: 2px; } +ul.torrent_list li.torrent div.torrent_name.compact { font-size: 1.0em; font-weight: normal; } +ul.torrent_list li.torrent div.torrent_name.paused { font-weight: normal; color: #777; } +ul.torrent_list li.torrent div.torrent_progress_details, ul.torrent_list li.torrent div.torrent_peer_details { clear: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +ul.torrent_list li.torrent div.torrent_progress_details.error, ul.torrent_list li.torrent div.torrent_peer_details.error { color: #F00; } +ul.torrent_list li.torrent.selected div.torrent_progress_details.error, ul.torrent_list li.torrent.selected div.torrent_peer_details.error { color: #FFF; } +ul.torrent_list div.torrent_progress_bar_container { height: 10px; position: relative; } +ul.torrent_list div.torrent_progress_bar_container.compact { width: 50px; position: absolute; right: 10px; margin-top: 2px; /*float: right;*/ } +ul.torrent_list div.torrent_progress_bar_container.full { margin-top: 2px; margin-bottom: 5px; } +ul.torrent_list div.torrent_peer_details.compact { margin-top: 2px; margin-right: 65px; /* leave room on the right for the progressbar */ float: right; /* pins it next to progressbar & forces torrent_name to ellipsize when it bumps up against this div */ } +ul.torrent_list div.torrent_progress_bar { height: 100%; position: absolute; top: 0px; left: 0px; background-image: url("images/progress.png"); background-repeat: repeat-x; border: 1px solid #888888; } +ul.torrent_list div.torrent_progress_bar.complete { z-index: 2; } +ul.torrent_list div.torrent_progress_bar.complete.paused { background-position: left -30px; border-color: #989898; } +ul.torrent_list div.torrent_progress_bar.complete.magnet { background-position: left -20px; border-color: #CFCFCF; } +ul.torrent_list div.torrent_progress_bar.complete.leeching { background-position: left 0px; border-color: #3D9DEA; } +ul.torrent_list div.torrent_progress_bar.complete.leeching.queued { background-position: left -70px; border-color: #889CA5; } +ul.torrent_list div.torrent_progress_bar.complete.seeding { background-position: left -40px; border-color: #269E30; } +ul.torrent_list div.torrent_progress_bar.complete.seeding.queued { background-position: left -60px; border-color: #8A998D; } +ul.torrent_list div.torrent_progress_bar.incomplete { z-index: 1; width: 100%; } +ul.torrent_list div.torrent_progress_bar.incomplete.paused { background-position: left -20px; border-color: #CFCFCF; } +ul.torrent_list div.torrent_progress_bar.incomplete.magnet { background-position: left -50px; border-color: #D47778; } +ul.torrent_list div.torrent_progress_bar.incomplete.leeching { background-position: left -20px; border-color: #CFCFCF; } +ul.torrent_list div.torrent_progress_bar.incomplete.leeching.queued { background-position: left -80px; border-color: #C4C4C4; } +ul.torrent_list div.torrent_progress_bar.incomplete.seeding { background-position: left -10px; border-color: #29AD35; } + +/*** +**** +**** PREFERENCES +**** +***/ +#prefs-dialog.ui-tabs .ui-tabs-panel { padding: 0px; -moz-user-select: none; -webkit-user-select: none; } + +.prefs-section { margin: 10px; text-align: left; } +.prefs-section > * { padding-top: 8px; padding-left: 8px; } +.prefs-section .title { font-weight: bold; font-size: larger; padding-left: 0px; } +.prefs-section .row .key { float: left; padding-top: 3px; } +.prefs-section .row .key > * { margin-left: 0px; } +.prefs-section .row .value { margin-left: 150px; } +.prefs-section .row .value > * { width: 100%; } +.prefs-section .checkbox-row > input { margin: 0px; } +.prefs-section .checkbox-row > label { margin-left: 5px; } +.prefs-section #alternative-speed-limits-title { padding-left: 18px; background: transparent url("images/blue-turtle.png") no-repeat; } + +/*** +**** +**** TORRENT INSPECTOR +**** +***/ +div#torrent_inspector { overflow: auto; -webkit-overflow-scrolling: touch; text-align: left; padding: 15px; top: 68px; position: fixed; width: 570px; z-index: 5; border-left: 1px solid #888888; bottom: 22px; right: 0px; /* Files Inspector Tab */ } +div#torrent_inspector #inspector-close { display: none; } +div#torrent_inspector #inspector-tabs-wrapper { width: 100%; overflow: hidden; text-align: center; } +div#torrent_inspector #inspector-tabs-wrapper #inspector-tabs { display: inline-block; } +div#torrent_inspector #inspector-tabs-wrapper #inspector-tabs > * { cursor: pointer; -moz-user-select: none; -webkit-user-select: none; display: inline-block; border-style: solid; border-color: #aaa; border-width: 1px; padding: 3px; width: 30px; height: 20px; } +div#torrent_inspector #inspector-tabs-wrapper #inspector-tabs > #inspector-tab-info { -moz-border-radius-topleft: 5px; -moz-border-radius-bottomleft: 5px; border-top-left-radius: 5px; border-bottom-left-radius: 5px; background-color: #dddddd; background-image: url("images/inspector-info.png"); /* fallback */ background-image: url("images/inspector-info.png"), -webkit-gradient(linear, left top, left bottom, from(white), to(#bbbbbb)); /* Saf4+, Chrome */ background-image: url("images/inspector-info.png"), -webkit-linear-gradient(top, white, #bbbbbb); /* Chrome 10+, Saf5.1+ */ background-image: url("images/inspector-info.png"), -moz-linear-gradient(top, white, #bbbbbb); /* FF3.6+ */ background-image: url("images/inspector-info.png"), -ms-linear-gradient(top, white, #bbbbbb); /* IE10 */ background-image: url("images/inspector-info.png"), -o-linear-gradient(top, white, #bbbbbb); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; border-left-width: 1px; } +div#torrent_inspector #inspector-tabs-wrapper #inspector-tabs > #inspector-tab-info:active, div#torrent_inspector #inspector-tabs-wrapper #inspector-tabs > #inspector-tab-info.selected { background-color: #e6e6ff; background-image: url("images/inspector-info.png"); /* fallback */ background-image: url("images/inspector-info.png"), -webkit-gradient(linear, left top, left bottom, from(#cdcdff), to(white)); /* Saf4+, Chrome */ background-image: url("images/inspector-info.png"), -webkit-linear-gradient(top, #cdcdff, white); /* Chrome 10+, Saf5.1+ */ background-image: url("images/inspector-info.png"), -moz-linear-gradient(top, #cdcdff, white); /* FF3.6+ */ background-image: url("images/inspector-info.png"), -ms-linear-gradient(top, #cdcdff, white); /* IE10 */ background-image: url("images/inspector-info.png"), -o-linear-gradient(top, #cdcdff, white); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#torrent_inspector #inspector-tabs-wrapper #inspector-tabs > #inspector-tab-peers { background-color: #dddddd; background-image: url("images/inspector-peers.png"); /* fallback */ background-image: url("images/inspector-peers.png"), -webkit-gradient(linear, left top, left bottom, from(white), to(#bbbbbb)); /* Saf4+, Chrome */ background-image: url("images/inspector-peers.png"), -webkit-linear-gradient(top, white, #bbbbbb); /* Chrome 10+, Saf5.1+ */ background-image: url("images/inspector-peers.png"), -moz-linear-gradient(top, white, #bbbbbb); /* FF3.6+ */ background-image: url("images/inspector-peers.png"), -ms-linear-gradient(top, white, #bbbbbb); /* IE10 */ background-image: url("images/inspector-peers.png"), -o-linear-gradient(top, white, #bbbbbb); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#torrent_inspector #inspector-tabs-wrapper #inspector-tabs > #inspector-tab-peers:active, div#torrent_inspector #inspector-tabs-wrapper #inspector-tabs > #inspector-tab-peers.selected { background-color: #e6e6ff; background-image: url("images/inspector-peers.png"); /* fallback */ background-image: url("images/inspector-peers.png"), -webkit-gradient(linear, left top, left bottom, from(#cdcdff), to(white)); /* Saf4+, Chrome */ background-image: url("images/inspector-peers.png"), -webkit-linear-gradient(top, #cdcdff, white); /* Chrome 10+, Saf5.1+ */ background-image: url("images/inspector-peers.png"), -moz-linear-gradient(top, #cdcdff, white); /* FF3.6+ */ background-image: url("images/inspector-peers.png"), -ms-linear-gradient(top, #cdcdff, white); /* IE10 */ background-image: url("images/inspector-peers.png"), -o-linear-gradient(top, #cdcdff, white); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#torrent_inspector #inspector-tabs-wrapper #inspector-tabs > #inspector-tab-trackers { background-color: #dddddd; background-image: url("images/inspector-trackers.png"); /* fallback */ background-image: url("images/inspector-trackers.png"), -webkit-gradient(linear, left top, left bottom, from(white), to(#bbbbbb)); /* Saf4+, Chrome */ background-image: url("images/inspector-trackers.png"), -webkit-linear-gradient(top, white, #bbbbbb); /* Chrome 10+, Saf5.1+ */ background-image: url("images/inspector-trackers.png"), -moz-linear-gradient(top, white, #bbbbbb); /* FF3.6+ */ background-image: url("images/inspector-trackers.png"), -ms-linear-gradient(top, white, #bbbbbb); /* IE10 */ background-image: url("images/inspector-trackers.png"), -o-linear-gradient(top, white, #bbbbbb); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#torrent_inspector #inspector-tabs-wrapper #inspector-tabs > #inspector-tab-trackers:active, div#torrent_inspector #inspector-tabs-wrapper #inspector-tabs > #inspector-tab-trackers.selected { background-color: #e6e6ff; background-image: url("images/inspector-trackers.png"); /* fallback */ background-image: url("images/inspector-trackers.png"), -webkit-gradient(linear, left top, left bottom, from(#cdcdff), to(white)); /* Saf4+, Chrome */ background-image: url("images/inspector-trackers.png"), -webkit-linear-gradient(top, #cdcdff, white); /* Chrome 10+, Saf5.1+ */ background-image: url("images/inspector-trackers.png"), -moz-linear-gradient(top, #cdcdff, white); /* FF3.6+ */ background-image: url("images/inspector-trackers.png"), -ms-linear-gradient(top, #cdcdff, white); /* IE10 */ background-image: url("images/inspector-trackers.png"), -o-linear-gradient(top, #cdcdff, white); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#torrent_inspector #inspector-tabs-wrapper #inspector-tabs > #inspector-tab-files { -moz-border-radius-topright: 5px; -moz-border-radius-bottomright: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; background-color: #dddddd; background-image: url("images/inspector-files.png"); /* fallback */ background-image: url("images/inspector-files.png"), -webkit-gradient(linear, left top, left bottom, from(white), to(#bbbbbb)); /* Saf4+, Chrome */ background-image: url("images/inspector-files.png"), -webkit-linear-gradient(top, white, #bbbbbb); /* Chrome 10+, Saf5.1+ */ background-image: url("images/inspector-files.png"), -moz-linear-gradient(top, white, #bbbbbb); /* FF3.6+ */ background-image: url("images/inspector-files.png"), -ms-linear-gradient(top, white, #bbbbbb); /* IE10 */ background-image: url("images/inspector-files.png"), -o-linear-gradient(top, white, #bbbbbb); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#torrent_inspector #inspector-tabs-wrapper #inspector-tabs > #inspector-tab-files:active, div#torrent_inspector #inspector-tabs-wrapper #inspector-tabs > #inspector-tab-files.selected { background-color: #e6e6ff; background-image: url("images/inspector-files.png"); /* fallback */ background-image: url("images/inspector-files.png"), -webkit-gradient(linear, left top, left bottom, from(#cdcdff), to(white)); /* Saf4+, Chrome */ background-image: url("images/inspector-files.png"), -webkit-linear-gradient(top, #cdcdff, white); /* Chrome 10+, Saf5.1+ */ background-image: url("images/inspector-files.png"), -moz-linear-gradient(top, #cdcdff, white); /* FF3.6+ */ background-image: url("images/inspector-files.png"), -ms-linear-gradient(top, #cdcdff, white); /* IE10 */ background-image: url("images/inspector-files.png"), -o-linear-gradient(top, #cdcdff, white); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div#torrent_inspector #inspector_header { margin-top: 8px; } +div#torrent_inspector #inspector_header #torrent_inspector_name { font-weight: bold; font-size: large; } +div#torrent_inspector ul.tier_list { margin: 2px 0 8px 0; width: 100%; padding-left: 0px; text-align: left; display: block; cursor: default; list-style-type: none; list-style: none; list-style-image: none; clear: both; } +div#torrent_inspector ul.tier_list li { overflow: hidden; } +div#torrent_inspector ul.tier_list .tracker_activity { float: left; color: #666; width: 330px; display: table; margin-top: 1px; } +div#torrent_inspector ul.tier_list .tracker_activity div { padding: 2px; } +div#torrent_inspector ul.tier_list table { float: right; color: #666; } +div#torrent_inspector ul.tier_list th { text-align: right; } +div#torrent_inspector li.inspector_tracker_entry { padding: 3px 0 3px 2px; display: block; } +div#torrent_inspector li.inspector_tracker_entry.odd { background-color: #EEEEEE; } +div#torrent_inspector div.tracker_host { font-size: 1.2em; font-weight: bold; color: #222; } +div#torrent_inspector #inspector_file_list { padding: 0 0 0 0; margin: 0 0 0 0; text-align: left; cursor: default; overflow: hidden; } +div#torrent_inspector #inspector_file_list { width: 100%; margin: 6px 0 0 0; padding-top: 6px; padding-bottom: 10px; text-align: left; display: block; cursor: default; list-style-type: none; list-style: none; list-style-image: none; clear: both; } +div#torrent_inspector li.inspector_torrent_file_list_entry { padding: 3px 0 3px 2px; display: block; } +div#torrent_inspector li.inspector_torrent_file_list_entry.skip { color: #666; } +div#torrent_inspector li.inspector_torrent_file_list_entry.even { background-color: #F7F7F7; } +div#torrent_inspector div.inspector_torrent_file_list_entry_name { font-size: 1.2em; color: black; display: inline; margin-left: 0px; } +div#torrent_inspector li.inspector_torrent_file_list_entry.skip > .inspector_torrent_file_list_entry_name { color: #999; } +div#torrent_inspector div.inspector_torrent_file_list_entry_progress { color: #999; margin-left: 20px; } +div#torrent_inspector ul.single_file li.inspector_torrent_file_list_entry > .file_wanted_control, div#torrent_inspector li.inspector_torrent_file_list_entry.complete > .file_wanted_control { cursor: default; } + +/* Peers Inspector Tab */ +#inspector_peers_list { padding: 0 0 0 0; margin: 0 0 0 0; text-align: left; cursor: default; overflow: hidden; } +#inspector_peers_list > div.inspector_group { padding-bottom: 0; margin-bottom: 0; } + +table.peer_list { width: 100%; border-collapse: collapse; text-align: left; cursor: default; clear: both; table-layout: fixed; } +table.peer_list .encryptedCol { width: 16px; } +table.peer_list .upCol { width: 70px; } +table.peer_list .downCol { width: 70px; } +table.peer_list .percentCol { width: 30px; padding-right: 5px; text-align: right; } +table.peer_list .statusCol { width: 40px; padding-right: 5px; } +table.peer_list .addressCol { width: 180px; } +table.peer_list .clientCol { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + +tr.inspector_peer_entry div.encrypted-peer-cell { width: 16px; height: 16px; background: transparent url("images/lock_icon.png") no-repeat; } +tr.inspector_peer_entry.odd { background-color: #EEEEEE; } + +/*** +**** File Priority Buttons +***/ +div.file-priority-radiobox { display: inline; float: right; margin: 4px; margin-top: 2px; } +div.file-priority-radiobox > * { cursor: pointer; -moz-user-select: none; -webkit-user-select: none; display: inline-block; border-style: solid; border-color: #aaa; border-width: 1px; padding: 3px; width: 20px; height: 12px; } +div.file-priority-radiobox > div.low { -moz-border-radius-topleft: 5px; -moz-border-radius-bottomleft: 5px; border-top-left-radius: 5px; border-bottom-left-radius: 5px; background-color: gainsboro; background-image: url("images/file-priority-low.png"); /* fallback */ background-image: url("images/file-priority-low.png"), -webkit-gradient(linear, left top, left bottom, from(#f1f1f1), to(#c8c8c8)); /* Saf4+, Chrome */ background-image: url("images/file-priority-low.png"), -webkit-linear-gradient(top, #f1f1f1, #c8c8c8); /* Chrome 10+, Saf5.1+ */ background-image: url("images/file-priority-low.png"), -moz-linear-gradient(top, #f1f1f1, #c8c8c8); /* FF3.6+ */ background-image: url("images/file-priority-low.png"), -ms-linear-gradient(top, #f1f1f1, #c8c8c8); /* IE10 */ background-image: url("images/file-priority-low.png"), -o-linear-gradient(top, #f1f1f1, #c8c8c8); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; border-right-width: 0px; } +div.file-priority-radiobox > div.low:active, div.file-priority-radiobox > div.low.selected { background-color: #e6e6ff; background-image: url("images/file-priority-low.png"); /* fallback */ background-image: url("images/file-priority-low.png"), -webkit-gradient(linear, left top, left bottom, from(#d7d7ff), to(#f5f5ff)); /* Saf4+, Chrome */ background-image: url("images/file-priority-low.png"), -webkit-linear-gradient(top, #d7d7ff, #f5f5ff); /* Chrome 10+, Saf5.1+ */ background-image: url("images/file-priority-low.png"), -moz-linear-gradient(top, #d7d7ff, #f5f5ff); /* FF3.6+ */ background-image: url("images/file-priority-low.png"), -ms-linear-gradient(top, #d7d7ff, #f5f5ff); /* IE10 */ background-image: url("images/file-priority-low.png"), -o-linear-gradient(top, #d7d7ff, #f5f5ff); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div.file-priority-radiobox > div.normal { background-color: gainsboro; background-image: url("images/file-priority-normal.png"); /* fallback */ background-image: url("images/file-priority-normal.png"), -webkit-gradient(linear, left top, left bottom, from(#f1f1f1), to(#c8c8c8)); /* Saf4+, Chrome */ background-image: url("images/file-priority-normal.png"), -webkit-linear-gradient(top, #f1f1f1, #c8c8c8); /* Chrome 10+, Saf5.1+ */ background-image: url("images/file-priority-normal.png"), -moz-linear-gradient(top, #f1f1f1, #c8c8c8); /* FF3.6+ */ background-image: url("images/file-priority-normal.png"), -ms-linear-gradient(top, #f1f1f1, #c8c8c8); /* IE10 */ background-image: url("images/file-priority-normal.png"), -o-linear-gradient(top, #f1f1f1, #c8c8c8); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div.file-priority-radiobox > div.normal:active, div.file-priority-radiobox > div.normal.selected { background-color: #e6e6ff; background-image: url("images/file-priority-normal.png"); /* fallback */ background-image: url("images/file-priority-normal.png"), -webkit-gradient(linear, left top, left bottom, from(#d7d7ff), to(#f5f5ff)); /* Saf4+, Chrome */ background-image: url("images/file-priority-normal.png"), -webkit-linear-gradient(top, #d7d7ff, #f5f5ff); /* Chrome 10+, Saf5.1+ */ background-image: url("images/file-priority-normal.png"), -moz-linear-gradient(top, #d7d7ff, #f5f5ff); /* FF3.6+ */ background-image: url("images/file-priority-normal.png"), -ms-linear-gradient(top, #d7d7ff, #f5f5ff); /* IE10 */ background-image: url("images/file-priority-normal.png"), -o-linear-gradient(top, #d7d7ff, #f5f5ff); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +div.file-priority-radiobox > div.high { -moz-border-radius-topright: 5px; -moz-border-radius-bottomright: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; background-color: gainsboro; background-image: url("images/file-priority-high.png"); /* fallback */ background-image: url("images/file-priority-high.png"), -webkit-gradient(linear, left top, left bottom, from(#f1f1f1), to(#c8c8c8)); /* Saf4+, Chrome */ background-image: url("images/file-priority-high.png"), -webkit-linear-gradient(top, #f1f1f1, #c8c8c8); /* Chrome 10+, Saf5.1+ */ background-image: url("images/file-priority-high.png"), -moz-linear-gradient(top, #f1f1f1, #c8c8c8); /* FF3.6+ */ background-image: url("images/file-priority-high.png"), -ms-linear-gradient(top, #f1f1f1, #c8c8c8); /* IE10 */ background-image: url("images/file-priority-high.png"), -o-linear-gradient(top, #f1f1f1, #c8c8c8); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; border-left-width: 0px; } +div.file-priority-radiobox > div.high:active, div.file-priority-radiobox > div.high.selected { background-color: #e6e6ff; background-image: url("images/file-priority-high.png"); /* fallback */ background-image: url("images/file-priority-high.png"), -webkit-gradient(linear, left top, left bottom, from(#d7d7ff), to(#f5f5ff)); /* Saf4+, Chrome */ background-image: url("images/file-priority-high.png"), -webkit-linear-gradient(top, #d7d7ff, #f5f5ff); /* Chrome 10+, Saf5.1+ */ background-image: url("images/file-priority-high.png"), -moz-linear-gradient(top, #d7d7ff, #f5f5ff); /* FF3.6+ */ background-image: url("images/file-priority-high.png"), -ms-linear-gradient(top, #d7d7ff, #f5f5ff); /* IE10 */ background-image: url("images/file-priority-high.png"), -o-linear-gradient(top, #d7d7ff, #f5f5ff); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } + +/**** +***** +***** MAIN WINDOW FOOTER +***** +****/ +div.torrent_footer { height: 22px; border-top: 1px solid #555555; bottom: 0; position: fixed; width: 100%; z-index: 3; background-color: #cccccc; background-image: -webkit-gradient(linear, left top, left bottom, from(#dddddd), to(#bbbbbb)); background-image: -webkit-linear-gradient(top, #dddddd, #bbbbbb); background-image: -moz-linear-gradient(top, #dddddd, #bbbbbb); background-image: -ms-linear-gradient(top, #dddddd, #bbbbbb); background-image: -o-linear-gradient(top, #dddddd, #bbbbbb); background-image: linear-gradient(top, #dddddd, #bbbbbb); } +div.torrent_footer > * { float: left; margin: 2px 4px; width: 18px; height: 12px; padding: 2px 8px; float: left; border: 1px solid #888888; -moz-user-select: none; -webkit-user-select: none; } + +/* Vuze: don't require things to be in footer */ +#settings_menu { -moz-border-radius: 5px; border-radius: 5px; background-color: #dddddd; background-image: url("images/settings.png"); /* fallback */ background-image: url("images/settings.png"), -webkit-gradient(linear, left top, left bottom, from(white), to(#bbbbbb)); /* Saf4+, Chrome */ background-image: url("images/settings.png"), -webkit-linear-gradient(top, white, #bbbbbb); /* Chrome 10+, Saf5.1+ */ background-image: url("images/settings.png"), -moz-linear-gradient(top, white, #bbbbbb); /* FF3.6+ */ background-image: url("images/settings.png"), -ms-linear-gradient(top, white, #bbbbbb); /* IE10 */ background-image: url("images/settings.png"), -o-linear-gradient(top, white, #bbbbbb); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +#settings_menu:active, #settings_menu.selected { background-color: #e6e6ff; background-image: url("images/settings.png"); /* fallback */ background-image: url("images/settings.png"), -webkit-gradient(linear, left top, left bottom, from(#cdcdff), to(white)); /* Saf4+, Chrome */ background-image: url("images/settings.png"), -webkit-linear-gradient(top, #cdcdff, white); /* Chrome 10+, Saf5.1+ */ background-image: url("images/settings.png"), -moz-linear-gradient(top, #cdcdff, white); /* FF3.6+ */ background-image: url("images/settings.png"), -ms-linear-gradient(top, #cdcdff, white); /* IE10 */ background-image: url("images/settings.png"), -o-linear-gradient(top, #cdcdff, white); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } + +#prefs-button { -moz-border-radius: 5px; border-radius: 5px; background-color: #dddddd; background-image: url("images/wrench.png"); /* fallback */ background-image: url("images/wrench.png"), -webkit-gradient(linear, left top, left bottom, from(white), to(#bbbbbb)); /* Saf4+, Chrome */ background-image: url("images/wrench.png"), -webkit-linear-gradient(top, white, #bbbbbb); /* Chrome 10+, Saf5.1+ */ background-image: url("images/wrench.png"), -moz-linear-gradient(top, white, #bbbbbb); /* FF3.6+ */ background-image: url("images/wrench.png"), -ms-linear-gradient(top, white, #bbbbbb); /* IE10 */ background-image: url("images/wrench.png"), -o-linear-gradient(top, white, #bbbbbb); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +#prefs-button:active, #prefs-button.selected { background-color: #e6e6ff; background-image: url("images/wrench.png"); /* fallback */ background-image: url("images/wrench.png"), -webkit-gradient(linear, left top, left bottom, from(#cdcdff), to(white)); /* Saf4+, Chrome */ background-image: url("images/wrench.png"), -webkit-linear-gradient(top, #cdcdff, white); /* Chrome 10+, Saf5.1+ */ background-image: url("images/wrench.png"), -moz-linear-gradient(top, #cdcdff, white); /* FF3.6+ */ background-image: url("images/wrench.png"), -ms-linear-gradient(top, #cdcdff, white); /* IE10 */ background-image: url("images/wrench.png"), -o-linear-gradient(top, #cdcdff, white); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } + +#turtle-button { -moz-border-radius: 5px; border-radius: 5px; background-color: #dddddd; background-image: url("images/turtle.png"); /* fallback */ background-image: url("images/turtle.png"), -webkit-gradient(linear, left top, left bottom, from(white), to(#bbbbbb)); /* Saf4+, Chrome */ background-image: url("images/turtle.png"), -webkit-linear-gradient(top, white, #bbbbbb); /* Chrome 10+, Saf5.1+ */ background-image: url("images/turtle.png"), -moz-linear-gradient(top, white, #bbbbbb); /* FF3.6+ */ background-image: url("images/turtle.png"), -ms-linear-gradient(top, white, #bbbbbb); /* IE10 */ background-image: url("images/turtle.png"), -o-linear-gradient(top, white, #bbbbbb); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +#turtle-button:active, #turtle-button.selected { background-color: #e6e6ff; background-image: url("images/turtle.png"); /* fallback */ background-image: url("images/turtle.png"), -webkit-gradient(linear, left top, left bottom, from(#cdcdff), to(white)); /* Saf4+, Chrome */ background-image: url("images/turtle.png"), -webkit-linear-gradient(top, #cdcdff, white); /* Chrome 10+, Saf5.1+ */ background-image: url("images/turtle.png"), -moz-linear-gradient(top, #cdcdff, white); /* FF3.6+ */ background-image: url("images/turtle.png"), -ms-linear-gradient(top, #cdcdff, white); /* IE10 */ background-image: url("images/turtle.png"), -o-linear-gradient(top, #cdcdff, white); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +#turtle-button:active, #turtle-button.selected { background-color: #e6e6ff; background-image: url("images/blue-turtle.png"); /* fallback */ background-image: url("images/blue-turtle.png"), -webkit-gradient(linear, left top, left bottom, from(#cdcdff), to(white)); /* Saf4+, Chrome */ background-image: url("images/blue-turtle.png"), -webkit-linear-gradient(top, #cdcdff, white); /* Chrome 10+, Saf5.1+ */ background-image: url("images/blue-turtle.png"), -moz-linear-gradient(top, #cdcdff, white); /* FF3.6+ */ background-image: url("images/blue-turtle.png"), -ms-linear-gradient(top, #cdcdff, white); /* IE10 */ background-image: url("images/blue-turtle.png"), -o-linear-gradient(top, #cdcdff, white); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } + +#compact-button { -moz-border-radius: 5px; border-radius: 5px; background-color: #dddddd; background-image: url("images/compact.png"); /* fallback */ background-image: url("images/compact.png"), -webkit-gradient(linear, left top, left bottom, from(white), to(#bbbbbb)); /* Saf4+, Chrome */ background-image: url("images/compact.png"), -webkit-linear-gradient(top, white, #bbbbbb); /* Chrome 10+, Saf5.1+ */ background-image: url("images/compact.png"), -moz-linear-gradient(top, white, #bbbbbb); /* FF3.6+ */ background-image: url("images/compact.png"), -ms-linear-gradient(top, white, #bbbbbb); /* IE10 */ background-image: url("images/compact.png"), -o-linear-gradient(top, white, #bbbbbb); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } +#compact-button:active, #compact-button.selected { background-color: #e6e6ff; background-image: url("images/compact.png"); /* fallback */ background-image: url("images/compact.png"), -webkit-gradient(linear, left top, left bottom, from(#cdcdff), to(white)); /* Saf4+, Chrome */ background-image: url("images/compact.png"), -webkit-linear-gradient(top, #cdcdff, white); /* Chrome 10+, Saf5.1+ */ background-image: url("images/compact.png"), -moz-linear-gradient(top, #cdcdff, white); /* FF3.6+ */ background-image: url("images/compact.png"), -ms-linear-gradient(top, #cdcdff, white); /* IE10 */ background-image: url("images/compact.png"), -o-linear-gradient(top, #cdcdff, white); /* Opera 11.10+ */ background-position: center; background-repeat: no-repeat; } + +#freespace-info { float: right; text-align: right; border: 0px; width: 100px; } + +/**** +***** +***** DIALOGS +***** +****/ +div.dialog_container { position: absolute; top: 0; left: 0px; margin: 0px; width: 100%; height: 100%; text-align: center; color: black; font-size: 1.1em; } + +div.dialog_container div.dialog_window { background-color: #eee; margin: 0 auto; opacity: .95; border-top: none; text-align: left; width: 420px; z-index: 10; overflow: hidden; position: relative; -webkit-box-shadow: 0 3px 6px rgba(0, 0, 0, 0.7); top: 80px; } + +@media screen and (-webkit-min-device-pixel-ratio: 0) { div.dialog_container div.dialog_window { top: 0; margin-top: 71px; } } +div.dialog_container .dialog_logo { width: 64px; height: 64px; margin: 20px 20px 0 20px; float: left; background: transparent url("images/logo.png") top left no-repeat; } + +div.dialog_container div.dialog_window h2.dialog_heading { display: block; float: left; width: 305px; font-size: 1.2em; color: black; margin-top: 20px; } + +div.dialog_container div.dialog_window div.dialog_message { float: left; padding-left: 3px; margin-left: -3px; width: 305px; overflow: hidden; } + +div.dialog_container div.dialog_window a { display: block; float: right; margin: 10px 20px 10px -8px; } + +div#upload_container div.dialog_window div.dialog_message label { margin-top: 15px; display: block; } + +div#upload_container div.dialog_window div.dialog_message input { width: 249px; margin: 3px 0 0 0; display: block; } + +div#upload_container div.dialog_window div.dialog_message input[type=text] { width: 245px; padding: 2px; } + +div#upload_container div.dialog_window div.dialog_message input[type=checkbox] { margin: 15px 3px 0 0; display: inline; width: auto; } + +div#upload_container div.dialog_window div.dialog_message #auto_start_label { display: inline; } + +div.dialog_container div.dialog_window form { margin: 0; padding: 0px; } + +div#move_container input#torrent_path { width: 286px; padding: 2px; } + +iframe#torrent_upload_frame { display: block; /* Don't change this : safari forms won't target hidden frames (they open a new window) */ position: absolute; top: -1000px; left: -1000px; width: 0px; height: 0px; border: none; padding: 0; margin: 0; } + +/**** +***** +***** POPUP MENU +***** +****/ +.trans_menu { margin: 0; padding: 0; } + +.trans_menu, .trans_menu ul { list-style: none; } + +.trans_menu ul { /* place it right above the button */ position: relative; bottom: 18px; min-width: 210px; background-color: white; padding: 5px 0; text-align: left; list-style: none; -webkit-border-radius: 5px; -webkit-box-shadow: 0 10px 25px rgba(0, 0, 0, 0.4); } + +.trans_menu ul ul { min-width: 150px; } + +.trans_menu ul ul#footer_sort_menu { min-width: 175px; } + +.trans_menu > * li { margin: 0; padding: 3px 10px 3px 20px !important; color: #000; cursor: default; text-indent: auto !important; width: inherit; } + +.trans_menu li.separator, .trans_menu li.separator.hover { border-top: 1px solid #dddddd; margin: 5px 0; padding: 0px; background: transparent; } + +.trans_menu li span.arrow { float: right; } + +.trans_menu li.hover li.hover span.arrow, .trans_menu li.hover li.hover li.hover span.selected { color: white; } + +.trans_menu span.selected { margin: 0 3px 0 -15px; color: #666; float: left; } + +.trans_menu div.outerbox { display: none; background: transparent; border: 1px solid rgba(0, 0, 0, 0.1); -webkit-border-radius: 5px; } + +.trans_menu div.inner { left: 0; margin: 0; } + +.trans_menu li.main li { z-index: 2; min-width: 78px; } + +.trans_menu a { text-decoration: none; cursor: default; } + +/*-------------------------------------- C O N T E X T M E N U --------------------------------------*/ +div#jqContextMenu { -webkit-border-radius: 5px; border: 1px solid rgba(0, 0, 0, 0.1); -moz-user-select: none; -webkit-user-select: none; } +div#jqContextMenu ul { filter: alpha(opacity=98); -moz-opacity: .98; opacity: .98; -webkit-box-shadow: 0 10px 25px rgba(0, 0, 0, 0.4); -webkit-border-radius: 5px; } +div#jqContextMenu li.separator, div#jqContextMenu div#jqContextMenu li.separator:hover { background: inherit !important; border-top: 1px solid #dddddd !important; margin: 5px 0 !important; padding: 0px; } diff --git a/src/jsp/web/style/transmission/common.scss b/src/jsp/web/style/transmission/common.scss new file mode 100644 index 0000000..4eedd85 --- /dev/null +++ b/src/jsp/web/style/transmission/common.scss @@ -0,0 +1,1057 @@ +@mixin verticalGradient($topColor, $bottomColor) { + background-color: mix($topColor, $bottomColor); + background-image: -webkit-gradient(linear, left top, left bottom, from($topColor), to($bottomColor)); + background-image: -webkit-linear-gradient(top, $topColor, $bottomColor); + background-image: -moz-linear-gradient(top, $topColor, $bottomColor); + background-image: -ms-linear-gradient(top, $topColor, $bottomColor); + background-image: -o-linear-gradient(top, $topColor, $bottomColor); + background-image: linear-gradient(top, $topColor, $bottomColor); +} + +@mixin imageOnVerticalGradient($src, $topColor, $bottomColor) { + background-color: mix($topColor, $bottomColor); + background-image: url($src); /* fallback */ + background-image: url($src), -webkit-gradient(linear, left top, left bottom, from($topColor), to($bottomColor)); /* Saf4+, Chrome */ + background-image: url($src), -webkit-linear-gradient(top, $topColor, $bottomColor); /* Chrome 10+, Saf5.1+ */ + background-image: url($src), -moz-linear-gradient(top, $topColor, $bottomColor); /* FF3.6+ */ + background-image: url($src), -ms-linear-gradient(top, $topColor, $bottomColor); /* IE10 */ + background-image: url($src), -o-linear-gradient(top, $topColor, $bottomColor); /* Opera 11.10+ */ + background-position: center; + background-repeat: no-repeat; +} + +@mixin buttonImage($image-url, $idle-color-top, $idle-color-bottom, $active-color-top, $active-color-bottom) { + @include imageOnVerticalGradient($image-url, $idle-color-top, $idle-color-bottom); + &:active, &.selected { + @include imageOnVerticalGradient($image-url, $active-color-top, $active-color-bottom); + } +} + +$nonselected-gradient-top: white; +$nonselected-gradient-bottom: #BBB; +$selection-color: #cdcdff; +$selected-gradient-top: $selection-color; +$selected-gradient-bottom: white; + +@mixin button { + cursor: pointer; + -moz-user-select: none; + -webkit-user-select: none; + display: inline-block; + border-style: solid; + border-color: #aaa; + border-width: 1px; + padding: 3px; +} + +@mixin roundedBox($radius) { + -moz-border-radius: $radius; + border-radius: $radius; +} + +@mixin leftRoundedBox($radius) { + -moz-border-radius-topleft: $radius; + -moz-border-radius-bottomleft: $radius; + border-top-left-radius: $radius; + border-bottom-left-radius: $radius; +} +@mixin rightRoundedBox($radius) { + -moz-border-radius-topright: $radius; + -moz-border-radius-bottomright: $radius; + border-top-right-radius: $radius; + border-bottom-right-radius: $radius; +} + +@mixin roundedButton($radius) { + @include button; + @include roundedBox($radius); +} + +/*-------------------------------------- + * + * G L O B A L + * + *--------------------------------------*/ + +html { + margin: 0; + padding: 0; + height: 100%; +} + +body { + font: 62.5% "lucida grande", Tahoma, Verdana, Arial, Helvetica, sans-serif; /* Resets 1em to 10px */ + color: #222;/* !important; */ + background: #FFF; + text-align: center; + margin: 0 0 30px; + overflow: hidden; + img { border: none; } + a { outline: 0; } +} + +/*** +**** +**** ABOUT DIALOG +**** +***/ + +#about-dialog +{ + > * { + text-align: center; + } + > #about-logo { + background: transparent url('images/logo.png') top left no-repeat; + width: 64px; + height: 64px; + margin-left: 100px; + } + > #about-title { + font-size: 1.3em; + font-weight: bold; + } +} + +/*** +**** +**** TOOLBAR +**** +***/ +$toolbar-gradient-top: #ddd; +$toolbar-gradient-bottom: #bbb; +$toolbar-height: 36px; + +div#toolbar-area +{ + @include verticalGradient($toolbar-gradient-top, $toolbar-gradient-bottom); + border-bottom: 1px solid #AAA; +} + +div#toolbar +{ + $separator-spacing: 10px; + + width: 100%; + height: $toolbar-height; + margin: 0px; + padding: 2px; + border-bottom: 1px solid #AAA; + @include verticalGradient($toolbar-gradient-top, $toolbar-gradient-bottom); + + $idle-color-top: $nonselected-gradient-top; + $idle-color-bottom: $nonselected-gradient-bottom; + $active-color-top: $selected-gradient-top; + $active-color-bottom: $selected-gradient-bottom; + + > * { + width: 34px; + height: 34px; + float: left; + border: none; + padding: 0px 3px; + } + + >div#toolbar-separator { + height: 25px; + margin-top: 8px; + margin-bottom: 8px; + border-left: 1px solid #aaa; + width: 3px; + } + + div#toolbar-open { + @include leftRoundedBox(10px); + @include buttonImage('images/toolbar-add.png', $idle-color-top, $idle-color-bottom, $active-color-top, $active-color-bottom); + margin-left: 4px; + } + > div#toolbar-remove { + @include rightRoundedBox(10px); + @include buttonImage('images/toolbar-remove.png', $idle-color-top, $idle-color-bottom, $active-color-top, $active-color-bottom); + margin-right: 4px; + } + > div#toolbar-start { + @include buttonImage('images/toolbar-start.png', $idle-color-top, $idle-color-bottom, $active-color-top, $active-color-bottom); + } + > div#toolbar-pause { + @include buttonImage('images/toolbar-stop.png', $idle-color-top, $idle-color-bottom, $active-color-top, $active-color-bottom); + } + > div#toolbar-start-all { + @include leftRoundedBox(10px); + @include buttonImage('images/toolbar-start-all.png', $idle-color-top, $idle-color-bottom, $active-color-top, $active-color-bottom); + } + > div#toolbar-pause-all { + @include rightRoundedBox(10px); + @include buttonImage('images/toolbar-stop-all.png', $idle-color-top, $idle-color-bottom, $active-color-top, $active-color-bottom); + } + > div#toolbar-search { + @include roundedBox(10px); + @include buttonImage('images/toolbar-search.png', $idle-color-top, $idle-color-bottom, $active-color-top, $active-color-bottom); + margin-left: $separator-spacing; + } + > div#toolbar-remote-search { + @include roundedBox(10px); + @include buttonImage('images/toolbar-remote-search.png', $idle-color-top, $idle-color-bottom, $active-color-top, $active-color-bottom); + margin-left: $separator-spacing; + margin-right: $separator-spacing; + } + > div#toolbar-remote { + @include roundedBox(10px); + @include buttonImage('images/toolbar-remote.png', $idle-color-top, $idle-color-bottom, $active-color-top, $active-color-bottom); + } + > div#toolbar-select { + $image-url: 'images/toolbar-pointer.png'; + @include buttonImage('images/toolbar-pointer.png', $idle-color-top, $idle-color-bottom, $active-color-top, $active-color-bottom); + } + > div#toolbar-inspector { + @include roundedBox(10px); + @include buttonImage('images/toolbar-info.png', $idle-color-top, $idle-color-bottom, $active-color-top, $active-color-bottom); + float: right; + margin-right: 4px; + } + + > *.disabled { + opacity: 0.25; + } +} + +/*** +**** +**** STATUSBAR +**** +***/ + +$statusbar-gradient-top: #ddd; +$statusbar-gradient-bottom: #bbb; +$statusbar-height: 26px; + +#statusbar +{ + height: $statusbar-height; + width: 100%; + border-bottom: 1px solid #AAA; + overflow: hidden; + position: relative; + @include verticalGradient($statusbar-gradient-top, $statusbar-gradient-bottom); + + #filter + { + float: left; + margin-left: 5px; + + input#torrent_search { + height: 18px; + width: 100px; + border-radius: 6px; + &.blur { color: #999; } + } + + #filter-count { margin-left: 8px; } + } +} + + #speed-info + { + float: right; + margin-top: 5px; + margin-right: 10px; + + * { + display: inline-block; + } + + #speed-up-icon { + margin-left: 8px; + width: 8px; + height: 8px; + background: url('images/arrow-up.png') bottom no-repeat; + } + + #speed-dn-icon { + width: 8px; + height: 8px; + background: url('images/arrow-down.png') bottom no-repeat; + } + + #speed-up-container, #speed-dn-container { + display: inline; + } + } + +/*** +**** +**** TORRENT CONTAINER +**** +***/ + +$torrent-container-top: $toolbar-height + $statusbar-height + 6px; + +div#torrent_container { + position: fixed; + top: $torrent-container-top; + bottom: 22px; + right: 0px; + left: 0px; + padding: 0px; + margin: 0px; + overflow: auto; + -webkit-overflow-scrolling: touch; +} + +ul.torrent_list +{ + width: 100%; + margin: 0; + padding: 0; + text-align: left; + cursor: pointer; + + li.torrent + { + border-bottom: 1px solid #ccc; + padding: 4px 30px 5px 14px; + color: #666; + background-color: white; + + &.compact { padding: 4px; } + &.even { background-color: #F7F7F7; } + &.selected { background-color: $selection-color; } + &.compact { div.torrent_name { color: black; } } + + // start-stop button + a { + float: right; + position: relative; + right: -22px; + top: 1px; + + img { + position: relative; + right: -10px; + } + + div { + background: url('images/buttons/torrent_buttons.png'); + height: 14px; + width: 14px; + } + + div.torrent_pause { background-position: left top; } + div.torrent_resume { background-position: center top; } + + &:active { + div.torrent_pause { background-position: left bottom; } + div.torrent_resume { background-position: center bottom; } + } + &:hover { + div.torrent_pause { background-position: left center; } + div.torrent_resume { background-position: center center; } + } + } + + div.torrent_name + { + font-size: 1.3em; + font-weight: bold; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: #222; + margin-top: 2px; + margin-bottom: 2px; + + &.compact { font-size: 1.0em; font-weight: normal; } + &.paused { font-weight: normal; color: #777; } + } + + div.torrent_progress_details, + div.torrent_peer_details { + clear: left; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + div.torrent_progress_details.error, + div.torrent_peer_details.error { + color: #F00; + } + + &.selected div.torrent_progress_details.error, + &.selected div.torrent_peer_details.error { + color: #FFF; + } + } + + /** + * Progressbar + * + * Each progressbar has three elemens: a parent container and two children, + * complete and incomplete. + * + * The only thing needed to set the progressbar percentage is to set + * the complete child's width as a percentage. This is because incomplete + * is pinned to the full width and height of the parent, and complete + * is pinned to the left side of the parent and has a higher z-index. + * + * The progressbar has different colors depending on its state, so there + * are five 'decorator' classNames: paused, queued, magnet, leeching, seeding. + */ + div.torrent_progress_bar_container + { + height: 10px; + position: relative; + + &.compact { + width: 50px; + position: absolute; + right: 10px; + margin-top: 2px; + /*float: right;*/ + } + &.full { + margin-top: 2px; + margin-bottom: 5px; + } + } + div.torrent_peer_details.compact + { + margin-top: 2px; + margin-right: 65px; /* leave room on the right for the progressbar */ + float: right; /* pins it next to progressbar & forces torrent_name to ellipsize when it bumps up against this div */ + } + div.torrent_progress_bar + { + height: 100%; + position: absolute; + top: 0px; + left: 0px; + background-image: url('images/progress.png'); + background-repeat: repeat-x; + border: 1px solid #888; + + &.complete { z-index: 2; } + &.complete.paused { background-position: left -30px; border-color: #989898; } + &.complete.magnet { background-position: left -20px; border-color: #CFCFCF; } + &.complete.leeching { background-position: left 0px; border-color: #3D9DEA; } + &.complete.leeching.queued { background-position: left -70px; border-color: #889CA5; } + &.complete.seeding { background-position: left -40px; border-color: #269E30; } + &.complete.seeding.queued { background-position: left -60px; border-color: #8A998D; } + &.incomplete { z-index: 1; width: 100%; } + &.incomplete.paused { background-position: left -20px; border-color: #CFCFCF; } + &.incomplete.magnet { background-position: left -50px; border-color: #D47778; } + &.incomplete.leeching { background-position: left -20px; border-color: #CFCFCF; } + &.incomplete.leeching.queued { background-position: left -80px; border-color: #C4C4C4; } + &.incomplete.seeding { background-position: left -10px; border-color: #29AD35; } + } +} + +/*** +**** +**** PREFERENCES +**** +***/ + +#prefs-dialog.ui-tabs .ui-tabs-panel { + padding: 0px; + -moz-user-select: none; + -webkit-user-select: none; +} + +.prefs-section +{ + margin: 10px; + text-align: left; + + > * { + padding-top: 8px; + padding-left: 8px; + } + + .title { + font-weight: bold; + font-size: larger; + padding-left: 0px; + } + + .row { + .key { + float: left; + padding-top: 3px; + > * { margin-left: 0px; } + } + .value { + margin-left: 150px; + > * { width: 100%; } + } + } + + .checkbox-row { + > input { margin: 0px; } + > label { margin-left: 5px; } + } + + #alternative-speed-limits-title { + padding-left: 18px; + background: transparent url('images/blue-turtle.png') no-repeat; + } +} + +/*** +**** +**** TORRENT INSPECTOR +**** +***/ + +$inspector-width: 570px; + +div#torrent_inspector +{ + overflow: auto; + -webkit-overflow-scrolling: touch; + text-align: left; + padding: 15px; + top: $torrent-container-top; + position: fixed; + width: $inspector-width; + z-index: 5; + border-left: 1px solid #888; + bottom: 22px; + right: 0px; + + $idle-color-top: $nonselected-gradient-top; + $idle-color-bottom: $nonselected-gradient-bottom; + $active-color-top: $selected-gradient-top; + $active-color-bottom: $selected-gradient-bottom; + + #inspector-close + { + display: none; + } + + #inspector-tabs-wrapper + { + width: 100%; + overflow: hidden; + text-align: center; + + #inspector-tabs + { + $border-radius: 5px; + + display: inline-block; + + > * { + @include button; + width: 30px; + height: 20px; + } + + > #inspector-tab-info { + @include leftRoundedBox($border-radius); + @include buttonImage('images/inspector-info.png', $idle-color-top, $idle-color-bottom, $active-color-top, $active-color-bottom); + border-left-width: 1px; + } + + > #inspector-tab-peers { + @include buttonImage('images/inspector-peers.png', $idle-color-top, $idle-color-bottom, $active-color-top, $active-color-bottom); + } + + > #inspector-tab-trackers { + @include buttonImage('images/inspector-trackers.png', $idle-color-top, $idle-color-bottom, $active-color-top, $active-color-bottom); + } + + > #inspector-tab-files { + @include rightRoundedBox($border-radius); + @include buttonImage('images/inspector-files.png', $idle-color-top, $idle-color-bottom, $active-color-top, $active-color-bottom); + } + } + } + + #inspector_header + { + margin-top: 8px; + + #torrent_inspector_name + { + font-weight: bold; + font-size: large; + } + } + + ul.tier_list + { + margin: 2px 0 8px 0; + width: 100%; + padding-left: 0px; + text-align: left; + display: block; + cursor: default; + list-style-type: none; + list-style: none; + list-style-image: none; + clear: both; + + li { + overflow: hidden; + } + .tracker_activity { + float: left; + color: #666; + width: 330px; + display: table; + margin-top: 1px; + } + .tracker_activity div { + padding: 2px; + } + table { + float: right; + color: #666; + } + th { + text-align: right; + } + } + + li.inspector_tracker_entry { + padding: 3px 0 3px 2px; + display: block; + + &.odd { + background-color: #EEEEEE; + } + } + + div.tracker_host { + font-size: 1.2em; + font-weight: bold; + color: #222; + } + + /* Files Inspector Tab */ + + #inspector_file_list { + padding: 0 0 0 0; + margin: 0 0 0 0; + text-align: left; + cursor: default; + overflow: hidden; + } + + #inspector_file_list { + width: 100%; + margin: 6px 0 0 0; + padding-top: 6px; + padding-bottom: 10px; + text-align: left; + display: block; + cursor: default; + list-style-type: none; + list-style: none; + list-style-image: none; + clear: both; + } + li.inspector_torrent_file_list_entry { + padding: 3px 0 3px 2px; + display: block; + &.skip { color: #666; } + &.even { background-color: #F7F7F7; } + } + + div.inspector_torrent_file_list_entry_name { + font-size: 1.2em; + color: black; + display: inline; + margin-left: 0px; + } + li.inspector_torrent_file_list_entry.skip>.inspector_torrent_file_list_entry_name { + color: #999; + } + div.inspector_torrent_file_list_entry_progress { + color: #999; + margin-left: 20px; + } + + ul.single_file li.inspector_torrent_file_list_entry>.file_wanted_control, + li.inspector_torrent_file_list_entry.complete>.file_wanted_control { + cursor: default; + } +} + +/* Peers Inspector Tab */ +#inspector_peers_list { + padding: 0 0 0 0; + margin: 0 0 0 0; + text-align: left; + cursor: default; + overflow: hidden; + + > div.inspector_group { + padding-bottom: 0; + margin-bottom: 0; + } +} + +table.peer_list { + width: 100%; + border-collapse: collapse; + text-align: left; + cursor: default; + clear: both; + table-layout: fixed; + + .encryptedCol { width: 16px; } + .upCol { width: 70px; } + .downCol { width: 70px; } + .percentCol { width: 30px; padding-right: 5px; text-align: right; } + .statusCol { width: 40px; padding-right: 5px; } + .addressCol { width: 180px; } + .clientCol { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +} + +tr.inspector_peer_entry +{ + div.encrypted-peer-cell + { + width: 16px; + height: 16px; + background: transparent url('images/lock_icon.png') no-repeat; + } + + &.odd + { + background-color: #EEEEEE; + } +} + +/*** +**** File Priority Buttons +***/ + +div.file-priority-radiobox +{ + $border-radius: 5px; + + display: inline; + float: right; + margin: 4px; + margin-top: 2px; + + > * { + @include button; + width: 20px; + height: 12px; + } + + // We have row after row of these buttons, so the flashy colors used in the inspector tabs look harsh here. + // Keep the same basic color theme, but look less harsh, by cutting the gradient's color range. + $idle-color-top: mix( $nonselected-gradient-top, $nonselected-gradient-bottom, 80% ); + $idle-color-bottom: mix( $nonselected-gradient-top, $nonselected-gradient-bottom, 20% ); + $active-color-top: mix( $selected-gradient-top, $selected-gradient-bottom, 80% ); + $active-color-bottom: mix( $selected-gradient-top, $selected-gradient-bottom, 20% ); + + > div.low { + @include leftRoundedBox($border-radius); + @include buttonImage('images/file-priority-low.png', $idle-color-top, $idle-color-bottom, $active-color-top, $active-color-bottom); + border-right-width: 0px; + } + + > div.normal { + @include buttonImage('images/file-priority-normal.png', $idle-color-top, $idle-color-bottom, $active-color-top, $active-color-bottom); + } + + > div.high { + @include rightRoundedBox($border-radius); + @include buttonImage('images/file-priority-high.png', $idle-color-top, $idle-color-bottom, $active-color-top, $active-color-bottom); + border-left-width: 0px; + } +} + + +/**** +***** +***** MAIN WINDOW FOOTER +***** +****/ + +div.torrent_footer +{ + height: 22px; + border-top: 1px solid #555; + bottom: 0; + position: fixed; + width: 100%; + z-index: 3; + + @include verticalGradient($statusbar-gradient-top, $statusbar-gradient-bottom); + + > * { + float: left; + margin: 2px 4px; + width: 18px; + height: 12px; + padding: 2px 8px; + float: left; + border: 1px solid #888; + -moz-user-select: none; + -webkit-user-select: none; + } +} /* Vuze: don't require things to be in footer */ + + $idle-color-top: $nonselected-gradient-top; + $idle-color-bottom: $nonselected-gradient-bottom; + $active-color-top: $selected-gradient-top; + $active-color-bottom: $selected-gradient-bottom; + + #settings_menu { + @include roundedBox(5px); + @include buttonImage('images/settings.png', $idle-color-top, $idle-color-bottom, $active-color-top, $active-color-bottom); + } + + #freespace-info { + float: right; + text-align: right; + border: 0px; + width: 100px; + } + + +/**** +***** +***** DIALOGS +***** +****/ + +div.dialog_container { + position: absolute; + top: 0; + left: 0px; + margin: 0px; + width: 100%; + height: 100%; + text-align: center; + color: black; + font-size: 1.1em; +} + +div.dialog_container div.dialog_window { + background-color: #eee; + margin: 0 auto; + opacity: .95; + border-top: none; + text-align: left; + width: 420px; + z-index: 10; + overflow: hidden; + position: relative; + -webkit-box-shadow: 0 3px 6px rgba(0,0,0,0.7); + top: 80px; +} +@media screen and (-webkit-min-device-pixel-ratio:0) { + div.dialog_container div.dialog_window { + top: 0; + margin-top: 71px; + } +} + +div.dialog_container .dialog_logo { + width: 64px; + height: 64px; + margin: 20px 20px 0 20px; + float: left; + background: transparent url('images/logo.png') top left no-repeat; +} + +div.dialog_container div.dialog_window h2.dialog_heading { + display: block; + float: left; + width: 305px; + font-size: 1.2em; + color: black; + margin-top: 20px; +} + +div.dialog_container div.dialog_window div.dialog_message { + float: left; + padding-left: 3px; + margin-left: -3px; + width: 305px; + overflow: hidden; +} + +div.dialog_container div.dialog_window a { + display: block; + float: right; + margin: 10px 20px 10px -8px; +} + + +div#upload_container div.dialog_window div.dialog_message label { + margin-top: 15px; + display: block; +} + +div#upload_container div.dialog_window div.dialog_message input { + width: 249px; + margin: 3px 0 0 0; + display: block; +} + +div#upload_container div.dialog_window div.dialog_message input[type=text] { + width: 245px; + padding: 2px; +} + +div#upload_container div.dialog_window div.dialog_message input[type=checkbox] { + margin: 15px 3px 0 0; + display: inline; + width: auto; +} + +div#upload_container div.dialog_window div.dialog_message #auto_start_label { + display: inline; +} + +div.dialog_container div.dialog_window form { + margin: 0; + padding: 0px; +} + +div#move_container input#torrent_path { + width: 286px; + padding: 2px; +} + + +iframe#torrent_upload_frame { + display: block; /* Don't change this : safari forms won't target hidden frames (they open a new window) */ + position: absolute; + top: -1000px; + left: -1000px; + width: 0px; + height: 0px; + border: none; + padding: 0; + margin: 0; +} + +/**** +***** +***** POPUP MENU +***** +****/ + +.trans_menu { + margin: 0; + padding: 0; +} + +.trans_menu, +.trans_menu ul { + list-style: none; +} + +.trans_menu ul { + /* place it right above the button */ + position: relative; + bottom: 18px; + + min-width: 210px; + background-color: white; + padding: 5px 0; + text-align: left; + list-style: none; + -webkit-border-radius: 5px; + -webkit-box-shadow: 0 10px 25px rgba(0,0,0,0.4); +} + +.trans_menu ul ul { + min-width: 150px; +} + +.trans_menu ul ul#footer_sort_menu { + min-width: 175px; +} + +.trans_menu > * li { + margin: 0; + padding: 3px 10px 3px 20px !important; + color: #000; + cursor: default; + text-indent: auto !important; + width: inherit; +} + +.trans_menu li.separator, +.trans_menu li.separator.hover { + border-top: 1px solid #ddd; + margin: 5px 0; + padding: 0px; + background: transparent; +} + +.trans_menu li span.arrow { + float: right; +} + +.trans_menu li.hover li.hover span.arrow, .trans_menu li.hover li.hover li.hover span.selected { + color: white; +} + +.trans_menu span.selected { + margin: 0 3px 0 -15px; + color: #666; + float: left; +} + +.trans_menu div.outerbox { + display: none; + background: transparent; + border: 1px solid rgba(0,0,0,0.1); + -webkit-border-radius: 5px; +} + +.trans_menu div.inner { + left: 0; + margin: 0; +} + +.trans_menu li.main li { + z-index: 2; + min-width: 78px; +} + +.trans_menu a { + text-decoration: none; + cursor: default; +} + +/*-------------------------------------- + * + * C O N T E X T M E N U + * + *--------------------------------------*/ + +div#jqContextMenu +{ + -webkit-border-radius: 5px; + border: 1px solid rgba(0,0,0,0.1); + -moz-user-select: none; + -webkit-user-select: none; + + ul { + filter: alpha(opacity=98); + -moz-opacity: .98; + opacity: .98; + -webkit-box-shadow: 0 10px 25px rgba(0,0,0,0.4); + -webkit-border-radius: 5px; + } + + li.separator, div#jqContextMenu li.separator:hover { + background: inherit !important; + border-top: 1px solid #ddd !important; + margin: 5px 0 !important; + padding: 0px; + } +} diff --git a/src/jsp/web/style/transmission/images/arrow-down.png b/src/jsp/web/style/transmission/images/arrow-down.png new file mode 100644 index 0000000000000000000000000000000000000000..14bbe97bed26c9c23d2e212e3c76f0ba506208b0 GIT binary patch literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^93afX3?$7I7w-U4x&b~Ru0Yz<)KphjR~rnJz(7Gk zL0nv1R8*9gmzR-|5y;@?<^~E%IW~Ya@RS7k1vBt5+}O&Hyv^g3zW%p4?}~upnw~C> zArg|w49twIYHn=8*BH{<_Aq@*=aEr=Zen1tX7AAh2M+9QWaMF}?P2@H&=Bw*sENVT L)z4*}Q$iB}AQve7 literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/transmission/images/arrow-up.png b/src/jsp/web/style/transmission/images/arrow-up.png new file mode 100644 index 0000000000000000000000000000000000000000..8dfcbeae6407f6550db81adbb7ba8551567b45fe GIT binary patch literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^93afX3?$7I7w-U4x&b~Ru0Yz*&`?!XRa#nFR#sL( zKtNbnn4O)SgM&jzNC+qkWNT<>0HuIj@zu5dKp~!zAirP+eSMEp$=hyhW#D62_~O74 zptz=|i(?3fY;pnvvm2wr(&CQB#-Cm58Ixr$Z~E;Mq`h0rQ0K;hv#enZ3^nK3Cp?PJ R%>-&<@O1TaS?83{1ON+tDZT&z literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/transmission/images/blank.gif b/src/jsp/web/style/transmission/images/blank.gif new file mode 100644 index 0000000000000000000000000000000000000000..75b945d2553848b8b6f41fe5e24599c0687b8472 GIT binary patch literal 49 zcmZ?wbhEHbWMp7unE0RJ|Ns9C3=9Vj8~~DvKUo+V7?>DzfNY>Fh|Ltj$Y2csQN9XW literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/transmission/images/buttons/cancel.png b/src/jsp/web/style/transmission/images/buttons/cancel.png new file mode 100644 index 0000000000000000000000000000000000000000..07d803f0c4b3b334019310f59b8599b63fbc2a91 GIT binary patch literal 426 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 literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/transmission/images/buttons/torrent_buttons.png b/src/jsp/web/style/transmission/images/buttons/torrent_buttons.png new file mode 100644 index 0000000000000000000000000000000000000000..483ae6db082ebfb4905ecdaa2d7de4bfda00cb36 GIT binary patch literal 1390 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 a|5H}&O~#utEX_c{#o+1c=d#Wzp$PyU5QlsK literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/transmission/images/file-priority-normal.png b/src/jsp/web/style/transmission/images/file-priority-normal.png new file mode 100644 index 0000000000000000000000000000000000000000..cb01551c1f1fcefbb3d3261b2d203f1fbd500d91 GIT binary patch literal 185 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&k#^NA%Cx&(BWL^R}Y)RhkE)4%c zaKYZ?lYt_f1s;*b3=G`DAk4@xYmNj^kiEpy*OmP?mnai2_l`wfuYp3Ao-U3d8t0P} zBsv}3+}vEl!oFz=*ef*LXWS^#ykyA|F@gW;LZ^*8y1G6w6$%Sn7e4UT;f%eElPJUL XZA#w@9AAh44Po$f^>bP0l+XkKZj3b5 literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/transmission/images/filter_icon.png b/src/jsp/web/style/transmission/images/filter_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..230209d8a79fb4e6b55f9623559c170495b77873 GIT binary patch literal 446 zcmV;v0YUzWP)c`sIp+BU-9F_{|(u-3?PW4s4zM4 zf6n4iPDTa>24{x+%Gm#fv-2515J^#1O8oz%?o5UVhH!>dhP3FI|2cEB7(ft7QFCy{ zzswu;hA9k*3^ig!yAuD_HI*`eAd;e7`Sg9M|BKEiMmNXY11C^5s00j*WUH+)KJ^%m!07*qoM6N<$f^2xb0{{R3 literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/transmission/images/graphics/reset_link_btn.png b/src/jsp/web/style/transmission/images/graphics/reset_link_btn.png new file mode 100644 index 0000000000000000000000000000000000000000..242d9ba702cb21a66f316afe8eb1cf99da09ad81 GIT binary patch literal 709 zcmV;$0y_PPP)sJbXHwxxNRpFPGQ1qscN^ z3n4wbD-5XrzV+d_OCbLA@vPrz*TTSZ=qxYpN6(Lw8T)$mc-(D!?$p)o0~^f~&#B6b zlC;)tLBrXklw;5+_mno!N-GDIR!%DIq^BLUN7X$l=(hwdm7-0WMN!Qm=wHhfjc4;- zyH$@O-)FXKEY*6GE>{Vi^xEU;&vlwgX?qAD;;rVp*Z2tM;9O(9kfHcd044ad&1X;uuM5|8g z-TB~@uJ49fDH?Gc1OeYtd$w_Fjg1GH%d%Xo681EvLdv6NqaM}5FhriQQ)%>Nq`^GT zDWI24O2CcdD6Si9&bgJ$(Aq17fTkWpyp;rnqp()5p+e%^e8oO{V84M4##K@lNG<{K zFd(yVnR9D5B6guxsw56z@=dYrGG%A8gOt8P_h3d>-zWB3{_f4%CJj*yyEzy^_QPdu z?eKODnf52I%U-1qAYl)el-{jBo@Tat`k;(Gb_0jM-Hi4sy(a`B;U1|yZB7lawzf}i z)z;g`TADCDNg>T-EhXFk64oJW^8f%d6JBvpQ3&&w9u@e^3`|xw5O|?1MV=Q}MJWk# ruQw^K3t{z?2bHrdOVS)h_$|Nyb(~AQ4+F*300000NkvXXu0mjfPkT&5 literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/transmission/images/graphics/vuze_remote_logo.png b/src/jsp/web/style/transmission/images/graphics/vuze_remote_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f9740f5d62af54a52e36e236881e15c9f18a7d69 GIT binary patch literal 2831 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 literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/transmission/images/inspector-activity.png b/src/jsp/web/style/transmission/images/inspector-activity.png new file mode 100644 index 0000000000000000000000000000000000000000..7e3f0604fb7c3cb1664639f7948f406808988aa3 GIT binary patch literal 1137 zcmV-%1djWOP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipn` z4>uxEd5<#y00Z$!L_t(I%YD*cY*Pgk$MJ9Pzuxw)?b@yzom-Io9}I9QNCpXk7^0Yf z0g2&>ps0z42j6@k#>7NlO^kmYjAlL%q7arKVG;w$B1SS4#IaTP2RF8suIvByw!OE1 zt}jApK+p3zpPZcYvssqKf7R>z-Z@mrmJT=hv~I~Rt9q_fu9?PyFW7Sa({mq=J!!`F zByeBX(P!6p27lbNeWS8zN3Ytkp#w=!$t2UnB2nT?tK}<~Ce2K`uy1^Jbn<_}SBBp_ z(zl^^G_+?#8`#lJlh=)8QxQcG8y1;#j_~zmTt1!gGvoU0TjArEm%lytU|xC@d$H?i zXlqaC?B2Zx+4w>~7I1$tN2w5}TDKus5{e=-w5x}%Ar*3}mX8=`4|E^j_Wi=>Zveuh zV2j>75pZs%>}X?pD$dUr?(ugxO)^@+)S;L+iLVrCc}n8KnHZ|KpPjp&N6k7X9vBb7 zw};<7?DY=H@vMvGy9HL~%KUUX!(S6R*UqNMCo6P4(?lj;<)dTsT>mabcq)Zab`x~> z%0~v?c)4-InrFY()35Xl2dHOFBuVCzn_C#%>!GbjrJNV|{c4`28J(7(L@8Tk{+Bed zB^!#bUD2fG=Ndu9wcV;Vb8ial4uK6r4u1G1!RkVpQVtfUbCmQ7!@Ip?V0c6OI-eJ4LxCDGepdi!1`_{`%knH=ui*@7F5`kzinVML=>lmt4%^r)j0oglJ;JO_I?+) zuNCokNbK0}VB(_iYXhi?T+)2CV|HgniTzn77Xz2yO))95c|@Rn^S^z911(TR&)Hb- zwbQ*tW_B)RniuPr8g6*z=83VC9;u1WD(l+)IAss5Z8Cye;LdeON5IrUF9^hD1=aPMy*6TZDN%y zR_@G^jwU{cNA>a7#=Z=>TJ39q{ty&xa>Q~~v}(219+}NtNv;%bb`I%>k}-Kpct#3n z3UZ4qRPutg7P*`GBQl*BGj%I1+l63Ls}i*#MgaZ+Znm+ZqON{g00000NkvXXu0mjf DKt3FH literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/transmission/images/inspector-files.png b/src/jsp/web/style/transmission/images/inspector-files.png new file mode 100644 index 0000000000000000000000000000000000000000..68739f16d8bbbe3de0ab7a205be83e2c3e4d478e GIT binary patch literal 358 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQajKx9jP7LeL$-D$|asqrpT!Az) z*t~f&kh^8e7Kq@?nKS?Y|Nr&t*VnIKKYRA<;lqd5u3fu*`}VJ2zs{UF^WnpXD_5>u zxNza>)vI^z+&OjX)QuZAjvhU_bLY+j2M(M+f4;M`^Tdf0+qZA;>gt*`YnHG4@}EF+ z8B2ovf*Bm1-ADs*lDyqr7&=&GJ%Aj}0*}aI1_o|n5N2eUHAey{$X?><>&kwci;v%2 zu3qdj$Yp7sE{-7*m)D*%=WB2faK4x;Fv*;~sO|TCLBmA}?OdJV|Bi`0GM1S>>8OB{ z@^z(tua!|=PYjoeB%gU47rOSER8GpA>f{6N+s;MpabtV`QEKL#Y8T%P4E-4#zcV*U)WZTlRr7^Tno@f22WQ%mvv4FO#pWjkwgFh literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/transmission/images/inspector-info.png b/src/jsp/web/style/transmission/images/inspector-info.png new file mode 100644 index 0000000000000000000000000000000000000000..8b733c9d8d3c94cbfeebdb49097dc330fc36f8a2 GIT binary patch literal 1370 zcmeAS@N?(olHy`uVBq!ia0vp^B0wz2!3HGny7cS=Qj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS=07?_nZLn2Bde0{8v^K*7iAWdWaj57fJ{tG z$}cUkRRX#c;)UD-xUqS~&|m@vn0`fKfxe-h0mw@*g}%P{mFDKcRTq~8r6Sym)!^cg z%7Rq=pw#00(xPNw#HA^NtSYc_E=o--$uA1Y&(DE{Vn9ZINq%ugeu08>u&JJ*LU?9g zN`84USiP^Wm1kaYNn&1ds;7&s63_&_%*+%kM<+K!BU1}gVgeO}d2cGa#^MI+n2$-yG9k&AW7vnik7sn8b z-lbDwHJlSAj!z9bmLpi^k}dSq%P4%ZblwD!E^eWtvX9*M2}_!~1s)2xh(==VglGVI#r z4$W&jBR1JR(M%6)yE9kdiF}J#}z- z$1a|=@vXa>$+n-1vRd~2>}&Cxcfrjmw>SFei)Ck8wjQp_*xD6XY9)NrYT_ZMti~s& zqCM9ANJ;tH$SK>dc-4-1^QpVfk7niUUU!h$Q*1hyce{C3{{_>kB=fQ(tIMYZy=U~D z$t~oaze8o}?gpb7cU>RV8_QH>Fl4O0^JaR6*E+}KeTmt*TYo28l>Do@r4cmQXjMjv kXNSC{quPI)eg6(y-*wBIsS%zV2rBJ7UHx3vIVCg!0JOaEJpcdz literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/transmission/images/inspector-peers.png b/src/jsp/web/style/transmission/images/inspector-peers.png new file mode 100644 index 0000000000000000000000000000000000000000..3e5903f98918bcb6d2d1bb5c6be153ab97514b27 GIT binary patch literal 425 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQas{?#OT>t<74`f1smzS5HpC6C} zay2wGfTXIbDhCILh=_=xp&>*KA0Hotp`f6ksHg}erNKZ-N(#sZl0X0y07?OYsHmv9 zxw(~m!X5*(%oT zJiU33%yy5a8Os(i_)hCurl}&S5dW-n?sOXmk$oA8O*3pWZ}Oa+?v|D@NkKu)#rx%| zRlzz9+g7P|F1y9B{q@pc?Oosn(G=KhlAbaZ6 zsSuHk8#e+Ot5>fEl7WGNKoYKH`}XY+H9*qV))vApFE58MBqStiYirZe(tzCP=;(@y zin6jY6BCp8_;{d5a&j_|6c!fF$jI>Y^t^iYs)vV1Oiav#2@@dZM?^$qWo3nihNh>d z0~L35b-B8_78VxXxpQat?%l1etsx;HK*ibF+1uVsDgt`PxFpCgn8CrACtvb|-F*{@ z9KO#A4r~Gz{LeYu^$kMfmrURm`24*n>F^bUrw&UZ1wMWENxG6}!m};5#R{l>kEe@c zh=%CYgX!Xp0SpZfzu!CM6&99qKIfFy>;M0?iY%Tke*UojXNZ=+3p1~f;{*m?6DJ`R ziRs;pLMjVWrj@Ymjk$Jm$w@U%Ulq&c0XkE=Sk>F-2pC=Ex*GMjWYzUmAt95^S2LQt z|E_jn!;cMD!**YOTXswL>ND?`nzi``AAWiHE2dTKj>~th>)EqWAUWUV&Xw99{|?U; zGM=qJ%}Gc6v*g67yX|HA+xPBP+GLl1hYC*Z=?j1DOyI6BA=%Vge+A zTp-!n+S=sGyJgE3m^=!7B1`n~=G2>0RWG+Voy-!yJ;&t6WTg}%u6}R+HPJ%*_U*el zRS9VLg9i^THCmmk)IYzf^}))3Cuf#DIlKJP+R*#U-R~~6x;;z(<-yuFC;Cnne80fWh_RvfeAAzCn(6@238ry{IKX;*;C|J14{caWXWNa+J>D=e*Hz*~F}X zu_{L~yeMSeGRK;NokQM?xK|S)<|>>~v}H50U&R`jm4m W3-8>IQ=dtLg4xs6&t;ucLK6TdUc@{A literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/transmission/images/logo.png b/src/jsp/web/style/transmission/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..bc0ee0254748be46566a955f2f8a785a5bf2ee85 GIT binary patch literal 1875 zcmV-Z2dwysP)$!L0ME*MJcqo-E+@(&Ufw@`uh3||7SDY0N@4y zL2tzNM{-xSrn_risCL(N?seCn>hWyqe&1c++3c?EsKVckB5$S_03=t{@mzA!)7bO9 zXY-l9khZD&lH;!JWB)4v7-^-+sjyB586x8XK5L0*03_F%BU9WBr#~2upWet+=iu zUI1`{_I}g=03{v(kmN-hXNhz^-T*M8aM>mr0LRj8TjOO5$nK5Lza<&~`|X?8;M$|& z0RRsbE}{Xj&0an~9%#_$tUSC$wDV-R_9U1|?*Pc?<3ADYJi5qlaP9GwVD$`u(9(B6 z8GQHS$8i18dhP{GSf%|CIQ|g6`gJFNwWMYh;2w580?;pV45jR-$^ON)J8_?ziC*;t zfaZ>qxz--JhLg2xcNb(03-m_bI}F>5C0MFO}szXdEcS8um25O zE<_%X>?QR%0YF_br%W#lC!0kAfaECM>W%v9`$By?>HahcPNE7ha66#O+%-r&U;!I- z_}Bt~;dKGG%oa5OSX}FypNjeT~<7964sSG-yUb zIU9@Bmlv#W2%@$p0otF=y?B4e>E8jfP`2%?_2$O zm0Oj2o~-hn4T1x{vqH5U`>=Aa4EuT%A=Pc4D;0ZsAfuf{4bjeOk`fL8O=RrI1dpbV zaeuF}Vpk3Ed7wpJEq4BloN(TjTTX|n<`Io@ULZ=H#`(b#4uCN@#qnA!3Lh3c#=%lx zGhn%}nXuWg1+dalb1EJ~sJAG~cbvn-={+*~o!+y0OSa&D7W3z(Fb&?sb%TQkDdwS) zpv#)IuzavGR7>Y=Gs|i?^IQ2r-Td%mW0=+^uwz*aOg#=z|mS zK($c;ft=5|qmCULYSd-q=aYFKyVw-|tYtW}f=~cRe1i;AQd+tFwuWQMvK{BKEj~k0 z^rg@}zrOhk)7Z(&*m=9!L5Ik8SYkxSbK;lSDn&LoLD zu(gF~veTgQdjp~XFmm0VGeEr5$~JZXg&AQN*Zo#K0N}3=sA0*a(pi(OP}BaceMw`J zY0M-#CGKU#NLPoX%-p$otuvN1LWsCJ@-IZRU8TMQ64(W5b1@btrKgAzF4M0fA~9-m zM21^YYHkYU`Rn#mzaB|fNlwc}Yp6FUg)L$+BahqMNgQFa-p1ph3MCsWFQn~}a$7=T zg-^)9st(pwk`~B=j0ybNL4eP%5I_y?CB0uIPhCSd>o N002ovPDHLkV1h=`cJlxL literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/transmission/images/progress.png b/src/jsp/web/style/transmission/images/progress.png new file mode 100644 index 0000000000000000000000000000000000000000..10773b40e65ba9a87113e22bbaabc8ff2f8c8492 GIT binary patch literal 938 zcmV;b16BNqP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipi4 z1r`9aA7Z2c00SyXL_t(o!_`yXCD*WobD zz7EH$=guj!bISAE58gQRX3AMN9wEFP-hKPy44Y~2W{S7NJ#RZgy1l*iJyX%)8J@09J|zI5xiulw(J-j{aT`gLySOud1fBmDIF)k2w2N>v3>u4_b9mSa_aFwam$ zA+&~BzOK&?X8{2~T-UHG2%unO`Pnp?9kpYH zM!u%cJPvu2p#*DzJFRX)7xekdo0lsm4x#>EN0J{x)qXp%`$DKT?8usm-s*C<>6fnT zw%wO99LcWPGsRNM(yghZb~ZEitR^juMIu1d&{d-IFj=Gox3%Zfj}tU*YXpSAPuR7L z6f)Ds+Vc=#LzEpf&4w{D?{+=fr@KwYQaLc88j4R@&+r=6pyl~$u9^@GV7h2fVmqra z1PJ!a{ZKm{{ggKh;9<3l9?Gp|K04W*{Xa)!*0?*W04JAnfQ({3y zbH}7;jzFuJbC7?Yg7vDt;4%Na#Lamo)YtJGIB4F82i*q6dC}MCLTDq)d zy=c#;pT}~vYYTFlR$8wHMYQK}Q3LM!esS}zTBA%Yg&l(PRS<1UfP$Q>-3}SFIw@OlXhO#*8l(j M07*qoM6N<$f*A$C2LJ#7 literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/transmission/images/row-info-active.png b/src/jsp/web/style/transmission/images/row-info-active.png new file mode 100644 index 0000000000000000000000000000000000000000..02f2479b14f15099b2cef06f7011b3ffddbedd8f GIT binary patch literal 2379 zcmaJ@c|25mAD+YwMV81iatuW;X0y&@WEsQIOhWM~y_x06#4KhGGnQ^ zDN88HE{U6Ni)4!wMM+xJO}(R=?)%5vdp@6Ye#_^1p6~YjaS}bLu1bnJiZB>ViR?!7 zlCHqgEB}l1D~ODID_yi97eC0G8wd$$d=TcyBJ?I`2ZOCduzdU= zKgu>dgUd$JmM};Wn9l{adMFhk*UA%O^bd5p)-yl#h0r7WIeiTo@ znac+ODt;OqR=+x7&P9>29L%7-yeiD8lM@6_ac(M z$C921h#&~!;ZZ1|P>2*-Ai4bgC=3pVL!q%KEY@6#Fc(B{AezXWBQRW6Ac6u0pT&b% zTn@0LNTYMZAOb>~>E9)=c|T-1g70mTHVh@A@lY5fda0ykAcgY(p=|aKv;gt~|H=1% z3JZK9cp%CP6mY}%4C&wk4VOak@XmaY266d5TyDs67d?Zx5LXbycGeJ{KNzT7x%MOf`s~4x$;FS?1UzGEY>08QS)G=+C&O^>;G0^^RK@ z)`>d@5*rA%!s2VkH(@@g+^BPErnaLxn4iyUE(Gu69DqCAl9-qR12bo=>tbGvbatkf zS6zu-us7R&mawL34hkFNi8b$p3MuU z1=(*}?IsHIIo|Ebxf*@33#DlFkf%uz(72=VRL|2DMK_g_s

    <#|B!LTy;~a5JttI zGyr{t`m|~Mcu~BD1FLEGNu^sdtInux79>%1avfN_n4v!T^hQ;tdPvHLhyI0k*G&XU zjKuLymPWv2zx-&Me7)E1Y_@6yf5bn`Y*26N+=^-T${hyh9&`B3scVn4_TIfH9=zZF zjOlM!sM0ZJX>mUVA#2i6Na>}TdH?Ro>W5*{y z{;Oj2_c)$iuLI+%aX?K?)_{q1mep_FlE=w%Z{{{QT~g~SoD|q=rhA3rJa$>;Q)w``YyXzA${1N0wK+FG z-H(by@-O}Gt$)$Em}A^x1fTWj+GuDTy85Zw#~0N-urM?8vo&{Hs?x(X7jwNeSEFAYEH`u!7C-2QzU1h>_OCDMXgHZ&U#pn0 z{*=YOT?DkJ*$MFzTc6(h?DOF#^x)P{EGei-S_rl54;;IyNa@C0$_WSi*Pn*nn^U7p zd*05$eF>fq3NBafcFv|U^IM)XKa3KF6qJjoPp}5?Cx2)R;COBB&F1TP`|esk7}%EJ zvZj;?CHk7nNE%atkkAXlyHZPkQvuT~2fT`pW&52d>}Ne{27`(?8lE zlen_6K-cKm0QF4U#2Zgp>cL3$Psr$1qoW=UgB2xZ^E>>JmuCB1 zUpINd^|OkioWYgsm}|9bHukI$cS0z5o+K?zwcf(+WOix?HG};!fBw+*lyic{=g>2^ zpaU<)_cnb!Iv5+9C>E~(`m$fiNm@Kgp--THgVG%BW%%HEZkxX9tT#(#!+gMG4`w*( ihS}bTvL+?{!FQ+-rq)*11Q=!M_lE33B~~~ENd5!vMdwZc literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/transmission/images/row-info.png b/src/jsp/web/style/transmission/images/row-info.png new file mode 100644 index 0000000000000000000000000000000000000000..84a82b492a112f6aef34e779dfb936cdd7086848 GIT binary patch literal 1838 zcmaJ?X;c$u6b%>zQAAl(T!#@U1(KNwM97pyOjrU2F{BVRw!~z>5t50M!9?^}L2zjm zH>xOFMXXZXrHW_;f`TIAir|78RB!<`qS&K?cA}vDQ99?$e9O7_zWd(yzB4OhqQmWM zN7^zN3_D?jKunK-`L*d!f3<08uj#>s3YAi^L?We=lNcjJNzBFop-P^Fi7~ly!MqwQ zfWhbo;u0w(6-_}Ego-UUW7v9?hGsJu0l|8WT#<}Xz-%lDR|kSGPL_iJt_%d@xguDk z;bU{~h%^$5O^cQ&(vlTCB^Vq81n5y(K!s6qK(9(sYf*h5_(>O~=jLq)1U^Bi_R0Z&ALiN0*a7v}l$ya5i3z!1WLU_TZDqg+1}Mu46Nq@$6_L{uyY z>xo6L0>L>Hr9mM`r_-@@K5T+af)E~$2f-YO!(q_~mUe-flIvM&?f6~=0j5=uxQ4zAPQzp7#kY28V5H@U1sTU{`{XbNt`hwO{V(hEm{}k3r z7HBX?jA@BfQb8YF;&^i?4az4mIYp2Xf=KDDV$2+ZBD8Y|4Zx4(vJoI2S1Spf_7h$t zLWOEAC08pjp&$^X71%hgME$rh-`AJV_u=?+5G2%x7lKR{@O?rN9v|@u5%7As0z#3h z!qikRSNWAI=#y)11(k-5EWk*7KBf#K2^H`uWfbq*OGux3JzQnqUMBa+g=jO7`C$Kb z(7ju9_n4<&+Lm5?X?#pgcRNY9bqDFfq;Jbkp&&#eJ$+}>$|y<5hzcuLn|F?tg#nI; zqoD4Pw{ZQc3u5mH8yudQ8tRX^~SgfmYf_brp z`;5tD*gdfH{oPmKQ-gg_;v>akA~ET%A3CaAoch5X{kUa`i|a-Jq#RlY?D)AV(?6qS zhRyLIyIQX-($vjewj<3Yle>D-q8EeQBFjA<7?_63&E8de|YgdEWJde|1}aRrqClNA0Ufcz4@E zk7AbzX#?6GGi52n#&qj)>!u?oM>OlQ`*kKvX>8o~cmL6`Ph-H$=!A2;Q!X;zwtXh( z`O=j)Uqr%*Pk+k2mTjqE&JtNpY`wBTS%fw?)p}XxXd2vx5DWL(4i3U+#HX59?XT`X&fkF#3KPa!jUH#?^70}c`| z!o1%lUa~8P(o8w;f^L;L-3H(KA7AxEyRX1=q?7y50Qj5Q{M9~9n~YmuuR3O*g)7G% z?QFlXQ7H}T<_xa2Dt=t)EO#(0XAS>t=aNS8FXEisYxb=fxWsS-9#pgYZr4MymXu}R zt$VaR!*#QrBj?u?*!I!7l8d_f4F%}28J29*~C-V}>VN3FMcVYMs zf(!O8p9~b?EbxddW?vN_jGX#k+|$T zL072NfT!iLCFiC|>Jz7WX{ zX=)Q^TjGXu4h4z}PH)TY;#6p3pV5*ex>?@c_Pp}(!WG5mTwd?W+RdfdD$pc(Lb7(= z@@vU0N-3<%-p|$y`p<1R(KP0M^tq^~JHFZS3W&V?@${g)_r_OyTs^;?0(ynP)78&q Iol`;+0AJ5`UjP6A literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/transmission/images/toolbar-add.png b/src/jsp/web/style/transmission/images/toolbar-add.png new file mode 100644 index 0000000000000000000000000000000000000000..45be109170610efe8f463c8acee15e91a974aaeb GIT binary patch literal 231 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 T{?;s*00000NkvXXu0mjfR>fE` literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/transmission/images/toolbar-info.png b/src/jsp/web/style/transmission/images/toolbar-info.png new file mode 100644 index 0000000000000000000000000000000000000000..7b8f0a9c6c0346536927ca0182f22fbabacb6cc2 GIT binary patch literal 1525 zcmV|kp0)n`(u<}Mg zU|~TK7J077OIT1*5Mcp%D?&h2YNr3rcZD4;cXxf&RCne#+`adF=l}oCckaC(!vGBX zHyQT7GH@g;X(F+gP!QDw`l;@}#_s9=?FKwTWM+gy6eEXJaS0g}O~|ZjM{ZpY(ysrE zIAt@!^HmU?DS=m%928qfSU$G_x1hxFgxEhm6N<`al(qKZTzxN2RdpeF zHL&wotSBhG3^~+bl9G0ACalbw#2LBuJs5o4=)TKcXC_ulA1x32y)i5wEVoVr+kIjd zI7pHqr_K&9yT#31mpDUK*~v4&8g<~_fQd)HtFgr|8KhyR)xc)2XqhCZ3h@=U;Ye$~ zTB=)|D{o>gN?ZCkaaGqH>^M`;yU$Dv^o@0-5~QKfXkh)}6O*=y61j#uR@kOqKV9rh zYv3L|5ye~UFgv($*ZRmnS~CR(tlT2(_r~OLkJ{dh8#+tRW?9pDhgbTPs!o_kU*)hk zS;aB93hHx7E=cQ;z`)9bVc9Ncu5h#YF{MuXtf?=0@7_mq58uG~x=xsemIyElFUQKL z3Nmz!8*dSz1Zfot3>Z6wwj4-T;gF&k8)UTtVe^dtzujgJWgV&P@8;6v@FtC7!s-sUW{@~$5?u9cnMib^S zRUj=03@mgwesAlkTJF&sSTmEbax4+&VTu1a9?rB>gO9x04nd4I%&9j>OK4!OU0{o4 z^cC)%&cqFm7sJ3eM^)_H+}Sv&mg7kZ|0v!Cykfw_m}8p*0`Q;Xld|0j(7 zaU|cZC43C0Z4*tLwJVr2Fz-kPK6N{-k>20`P#dUh?N(p6iKXN|a;@VS zrd0mf_TeC{=^9_fQ#Sj`mj+O`hA8o|bCQ5Xo@u=DvkD}YHQ+{PA6mPA(>P{;dEQxI zxtWI6Z^65pe3nhK4S_{$H9n`3&2*IV`0~&xXggezm4bLR2NGZsaTzQ()6n@jetW&Q zXs(AGX3>?Hu|EOR_Qdk2ZtLa=KcLM(VD?o(++Y@o^1yPk+>hN4yt&3}(pzhfz(Aaj z<>8m`;huQBZx^Lzg>M?pSGS_H?iNB7m00MW%)397FFZ52Tr4Nc{X|=`*R4H`#;rSw z#s2wR>1H@4LfwUu>~WS+N~d&hZsY=M7*HMz*u4ep-)U9CJ$x!CuS>0EZrTf zXX1QW&(yU`&&&-R?0mV?#S!1^^dCOU&`{IZe%>M8AYRsF;6-9|zss@z5Uj$Z2)+Nz bf5(3TO8KpNh>nETQvX!@x8-$K7(a3*%H1#fCy$t6^PQ&hpPZLjKe*KnNoMfBv1=5#_UD zAOV66HCRMo6d6TOs&XnTf^4)n!s;p(m`ySbnXrKY1a3GH0~klZV6%XgHhi5s!qVpv zhXvbUPT1%v$OvYU97QAO4^mlyOhkm$mK9P`KxUg%wx9Jip!tHzIXci8Otl}k^o*ba zJM+eHU)az9!MSlF;y&(YY*Is>>$vAwF@YtPIU{UnAl1c1Irj3yioQ|?QY%R1vw;Eh zSstCRH3?|rLkq6(i`h8i(~U_ zIeYymg|%641j9et`|EQnug0O~;IH;(zCHg8(I^5sR6|sURHrQacxCN4Y)VYqGE_%p z10A*XWRidxsw9?(l2ulKDz_$xojN9K3NE99|NoqxxPd=8d$QI20hj&&0000>ES@%?n{rGEVQ5~nC`FU^r`zZV*!}6j8VvcTfn&#c=-J2Q7Gd9=JD-YTYDsy zX9opXI~wooT+h-1V-5}`DvnJOc6<9;k$v_0?fCw)$^M<)8{dlp8t07@n4jJ-0*nZF z0f8)Q%nt{mTF8Wz)si%=&v*KY}`W;Agg@`1m1-5lLt}# z2vQbYufGfCEjNOG4vv5s-6R0D1dXU ze-i2`DVi=e^$u_&Ev$SQ3ABE{ne`%hkMnL^;#*TYb*hWw;@BDj1O9=}@+z(zcPu>ckhtcP+Tp2n7X#B4&I#;UOray3>vz)Q&A>KuR*3OK{~|L61% XuchquH`ZHo00000NkvXXu0mjf%}F$z literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/transmission/images/toolbar-pointer.png b/src/jsp/web/style/transmission/images/toolbar-pointer.png new file mode 100644 index 0000000000000000000000000000000000000000..190fbe1494f969794327470b0daf35935420725b GIT binary patch literal 364 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbMf`*c8Q3H0Cnl;=m6~kIsj;wk)x0bP^V%^ zkRQ;Qb(4YM>EwTt^N*jH{O*|2znpE$|1tEli}xmRbFL~%t<6;jDo*!waSYKA4L!)o z>}0^uaM54fO7*3m^sdAI|CjpCJX;-6RdP1Jq{p6h+dcuN(+ffj_Eh&?`|zvTtm@yz z8~%5<$7@PV4(Cl4(`zzuEuI#%u0v&Q>7RtdV!6{(*+trq8J_pQaqcARTkgv*IM+Wd Rnv?*FP)!ixMb;}`F*S;Z_ za_l1%5cIj4raO!D5VX0P#vEz6ZRc|HkQWzKwrrA8PMwr$o|V=1fUIE{a_yraaXbLA zV}kz7vwJ{h8vps+b@!Sp~DY(*IgjdIA(Jy8iXY4l@-aeEB|^|Ox^TIE&XuC?uXM> zA0(L{!ZFhWIJyMbf1K1MU-v;88J9=KODgOgBy;1k9QrU^*Uk3&2`e)|!) zCKq6C0XW!4<{*Vg;umW}NidAViP{Men1?`IZW|_!*Q3c_%~U)4(~JXf#4rYjtG$q9 z9EV$@|3s9AQ_waAKanxvV9v3cF%TI0K&rIZh-W=Vo~^QFWElD&QRjuY%27B(81OGF zOB^J>H9(IG2lI}S3lizczd>1-7%tx^EN`AYuJ=HU)&mDA4^TaiS;77OdB}43=sx6> zag0-goVQFcHKAZGPw&R7_u^e<X8|3FAJIeD>u0C2|gWZxFEZW@c6dz`Frc!f5Ui%~A*$?IWPmX$@U+yVY4|8h2wwvj2Cd*d?^ zcR3t$MA5>@3*~H4J<5|&9?LJX!j9j}91=^MBy43{?DGjM&|@QIB3DbSDBpqdH$|lu z_)K7o$QRibFm~RLDsoTGfuN=f?v71!CV=hXdAo&16bJB-5!~BbzY!VW({x=__KHl9 zO0`0vy^kSY-oWbr+sobSGYlQ9oVXlXt_ppy#HQq%VEff;t74DP0ETuqX95y)H&b(| z&`jU*P&qzhI}c=5!`92xs?pyb;YwcaY7ATA(LMGFTb_AceUqyym;%pzmnkd zf9T-2xRQY{(?hzP-aPPZo)NmD*Fql3JB00o_zSPqDVkg#&+ zf{l}bLGc+`-_(^m+|UiakL=2a&tC@v#cj46I;#i`p83#l2hjmA%`?~T_Cz+kuy^4_ zpg1?!U}V>V6rH+qCl2Jw?Djd%K(Y7O!Zp2R)@4|v0Uc6~EVp*=iD(RiLE#4!bj89=@*++Zjc2SpOlup%_uouLs1S6erIWid?6 zf=vf9pyHsgRRdyaTp0u$H1#u9tpoDiftVL52NuB8Wwy;ZhGaQE!JG%hLQwB(KyxBI zyrD@CrcN8Gj+EE0iYZiMs@WiSW>Q-t!0okxqH>D!iS1_NE|kl8l7 z1(@6HfqWLK7!1k?u<%W7p7{|JnN&9zl&*ndPE-pDP)-Epy!o58@^6DUnk ZJpf>;cl0W+k?jBg002ovPDHLkV1f)Z_&ops literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/transmission/images/toolbar-remove.png b/src/jsp/web/style/transmission/images/toolbar-remove.png new file mode 100644 index 0000000000000000000000000000000000000000..619c63a88cf048dd43b3cf60493d98ac33873bf2 GIT binary patch literal 428 zcmV;d0aN~oP)+`lF=X1b@ zffP+!wNo;uWA1fyg}I&cjuB~)B@pw#ZEc^oUJRRo`Q7tRVlybKeaqI@WEve3|CHG)=99`fC_>P(jZg}AsYxc3@RxDH3WoJi7g<}^#K3{ WSde+)E&Xo*00009 literal 0 HcmV?d00001 diff --git a/src/jsp/web/style/transmission/images/toolbar-search.png b/src/jsp/web/style/transmission/images/toolbar-search.png new file mode 100644 index 0000000000000000000000000000000000000000..4dd5cd5fdb5d19c78385052de9a86e9eace8878e GIT binary patch literal 658 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 * { + width: 34px; /*34px*/ + height: 30px; /*34px*/ + border: 1px solid #888; +} + +.toolbar-vuze { + display: none; +} + +/*** +**** +**** STATUSBAR +**** +***/ + +#filter-tracker { + display: none; +} + +#statusbar { + /* + overflow: hidden; + */ + overflow: visible; + height: 28px; + /* might need position: none + position: relative; + */ +} + +div#speed-info { + margin: 0px; + float: none; + padding-top: 0px; + line-height: 18px; + height: 18px; +} + +div.main_container { + float: left; +} + +#ul_torrent_context_menu { + display:none; + z-index: 101; + position: absolute; + width: 290px; + text-align: left; + overflow-y: auto; + overflow-x: none; +} + +#ul_torrent_context_menu li ul { + width: 150px; + /* Force to overlap existing menu just below menu header. + Needed when overflow-y is scroll */ + top: auto !important; + left: 137px !important; +} + + +#footer_super_menu { + display:none; + z-index: 99; + position: fixed; + top: 77px; + left: 12px; + width: 200px; + text-align: left; +} + +#footer_super_menu li ul { + width: 180px; +} + +.ui-menu .ui-menu-item a { + line-height: 2.5em; +} + +.ui-menu .ui-icon { + margin-top: 0.8em; +} + +.ui-menu .menu-item-selected a { + display: list-item; + list-style: disc outside; +} +.ui-menu .ui-menu-item a { + margin-left: 2em; +} + +.ui-menu .ui-menu-item a.ui-state-hover, +.ui-menu .ui-menu-item a.ui-state-focus, +.ui-menu .ui-menu-item a.ui-state-active { + margin-left: 2em; +} + + +#settings_menu { + float: left; + margin: 2px 4px; + border: 1px solid #888; + width: 32px; + height: 22px; +} +#settings_menu #button { + width: 32px; + height: 22px; +} + +/*** +**** +**** TORRENT CONTAINER +**** +***/ +div#torrent_container { + -webkit-overflow-scrolling: auto; + bottom: auto; + top: auto; + left: auto; + right: auto; + position: relative; + padding: 80px 0 0 0; + + user-select: none; + -o-user-select:none; + -moz-user-select: none; + -khtml-user-select: none; + -webkit-user-select: none; + +} + +ul.torrent_list li.torrent { + padding: 4px 5px 4px 10px; /* Make space for buttons on the right */ +} + +ul.torrent_list li.torrent div.torrent_progress_details { + color: #3e5365 +} + +ul.torrent_list li.torrent div.torrent_peer_details { + color: #4f5b65 +} + +ul.torrent_list div.torrent_progress_bar_container.full { + margin-right: 53px; +} + +/**** +***** START / STOP BUTTON +****/ + +/* >> Vuze: buttons changed to add info button */ +ul.torrent_list li.torrent a { + float: right; + position: relative; + right: auto; + top: -13px; + margin-right: 0px; +} + +ul.torrent_list li.torrent a div { + background: none; +} + +ul.torrent_list li.torrent a div.torrent_info { + background: url('images/row-info.png') center no-repeat; + padding: 0px 8px 8px 12px; + width: 32px; + height: 32px; +} + +ul.torrent_list li.torrent a:hover div.torrent_info { +} + +ul.torrent_list li.torrent a:active div.torrent_info { +} + +ul.torrent_list li.torrent a div.torrent_info.selected { + background: url('images/row-info-active.png') center no-repeat; +} +/* << Vuze: buttons changed to add info button */ + +/*-------------------------------------- + * + * T O R R E N T I N S P E C T O R + * + *--------------------------------------*/ + + +div#torrent_inspector { + -webkit-overflow-scrolling: auto; + bottom: auto; + top: auto; + left: auto; + right: auto; + position: relative; + + padding: 10px 0 0 0; + width: 100%; + border-top: 1px dotted gray; + border-left: none; + color: #000; +} + +div#torrent_inspector_name { + display: none; +} + +div#torrent_inspector.selected { + background-color: #ddddff; +} + +div#torrent_inspector li.inspector_tracker_entry.odd { + background-color: #e4e4ff; +} + + +/**** +***** +***** MAIN WINDOW FOOTER +***** +****/ + +div.torrent_footer { + /* Vuze: Hide torrent footer */ + display:none; +} + +#prefs-button, +#turtle-button { + display:none; + background: none; +} +.prefs-section #alternative-speed-limits-title { + background: none; +} + +#compact-button { + /* Vuze: hide compact buttonf or now (we still have a menu) */ + display:none; + background: none; +} + + +/* >> Vuze: Misc stuff, no idea if needed */ + +div#torrent_pref { + float:left; + position:relative; + z-index:5 +} + +div#torrent_pref ul#settings_menu { + float:right; + margin:1px 7px 0; +} + +/* << Vuze: Misc stuff, no idea if needed */ + + +/**** +***** +***** POPUP MENU +***** +****/ + +.trans_menu ul { + /* Vuze: Place it below the button! */ + bottom: 0px; +} + +.trans_menu div.inner { + /* Vuze: pad it down below the button */ + margin: 5px 0 0; +} + +.trans_menu li.main li { + font-size: 8pt; +} + +.trans_menu li.separator, +.trans_menu li.separator.hover { + margin: 4px 0; +} + +/************/ + +#prefs-section-seeding, +#prefs-section-blocklist, +#prefs-section-alternative-speed-limits, +#port-label, +#prefs-section-peer-port-random-on-start, +#prefs-section-port-forwarding-enabled +{ + display: none; +} + + +.prefs-section .row .value { + margin-left: 130px; + padding-top: 3px; +} + +.prefs-section .row .value, +#torrent_inspector_name { + word-wrap: break-word; /* otherwise long value strings will create horizontal scroll */ +} + + +div#torrent_inspector #inspector-tabs-wrapper #inspector-tabs > * { + width: 0.5in; + height: 0.4in; +} + +#inspector_file_list div li input[type="checkbox"] { + float: left; +} + +div#torrent_inspector div.inspector_torrent_file_list_entry_name { + overflow: auto; + word-break: break-all; + display: block; +} + +#inspector_file_list li { + padding-top: 1em; +} diff --git a/src/jsp/web/style/transmission/vuzeandroid.css b/src/jsp/web/style/transmission/vuzeandroid.css new file mode 100644 index 0000000..32e7973 --- /dev/null +++ b/src/jsp/web/style/transmission/vuzeandroid.css @@ -0,0 +1,20 @@ +div#toolbar-area { + display: none; +} + +div#torrent_logo { + display: none; +} + +#statusbar { + display: none; +} + +#remotesearch_container { + padding: 0 0 0 0; +} + +div#torrent_container { + padding: 0 0 0 0; +} +