Initial checkin, v0.1
2
CHANGES.txt
Normal file
@ -0,0 +1,2 @@
|
||||
* 2017-03-20 0.1
|
||||
- Initial version
|
23
LICENSE.txt
Normal file
@ -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
|
13
README.txt
Normal file
@ -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.
|
69
build.xml
Normal file
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<project basedir="." default="all" name="i2psnark-rpc">
|
||||
<property file="override.properties"/>
|
||||
|
||||
<target name="all" depends="clean,plugin" />
|
||||
|
||||
<target name="war" >
|
||||
<ant dir="src" target="build" />
|
||||
</target>
|
||||
|
||||
<target name="plugin" depends="war">
|
||||
<!-- get version number -->
|
||||
<buildnumber file="scripts/build.number" />
|
||||
<property name="release.number" value="0.1" />
|
||||
|
||||
<!-- make the update xpi2p -->
|
||||
<copy file="LICENSE.txt" todir="plugin/" overwrite="true" />
|
||||
<copy file="README.txt" todir="plugin/" overwrite="true" />
|
||||
<copy file="scripts/plugin.config" todir="plugin/" overwrite="true" />
|
||||
<exec executable="echo" osfamily="unix" failonerror="true" output="plugin/plugin.config" append="true">
|
||||
<arg value="update-only=true" />
|
||||
</exec>
|
||||
<exec executable="echo" osfamily="unix" failonerror="true" output="plugin/plugin.config" append="true">
|
||||
<arg value="version=${release.number}-b${build.number}" />
|
||||
</exec>
|
||||
<exec executable="pack200" failonerror="true">
|
||||
<arg value="-g" />
|
||||
<arg value="plugin/console/webapps/transmission.war.pack" />
|
||||
<arg value="src/build/transmission.war.jar" />
|
||||
</exec>
|
||||
<input message="Enter su3 signing key password:" addproperty="release.password.su3" />
|
||||
<fail message="You must enter a password." >
|
||||
<condition>
|
||||
<equals arg1="${release.password.su3}" arg2=""/>
|
||||
</condition>
|
||||
</fail>
|
||||
<!-- this will fail if no su3 keys exist, as it needs the password twice -->
|
||||
<exec executable="scripts/makeplugin.sh" inputstring="${release.password.su3}" failonerror="true" >
|
||||
<arg value="plugin" />
|
||||
</exec>
|
||||
<move file="i2psnark-rpc.xpi2p" tofile="i2psnark-rpc-update.xpi2p" overwrite="true" />
|
||||
<move file="i2psnark-rpc.su3" tofile="i2psnark-rpc-update.su3" overwrite="true" />
|
||||
|
||||
<!-- make the install xpi2p -->
|
||||
<copy file="scripts/plugin.config" todir="plugin/" overwrite="true" />
|
||||
<exec executable="echo" osfamily="unix" failonerror="true" output="plugin/plugin.config" append="true">
|
||||
<arg value="version=${release.number}-b${build.number}" />
|
||||
</exec>
|
||||
<exec executable="scripts/makeplugin.sh" inputstring="${release.password.su3}" failonerror="true" >
|
||||
<arg value="plugin" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="distclean" depends="clean" />
|
||||
|
||||
<target name="clean" >
|
||||
<ant dir="src" target="clean" />
|
||||
<delete file="plugin/plugin.config" />
|
||||
<delete file="plugin/console/webapps/transmission.war.pack" />
|
||||
<delete file="plugin/LICENSE.txt" />
|
||||
<delete file="plugin/README.txt" />
|
||||
<delete file="plugin.zip" />
|
||||
<delete file="i2psnark-rpc.xpi2p" />
|
||||
<delete file="i2psnark-rpc-update.xpi2p" />
|
||||
<delete file="i2psnark-rpc.su3" />
|
||||
<delete file="i2psnark-rpc-update.su3" />
|
||||
</target>
|
||||
|
||||
</project>
|
157
plugin/licenses/JSON.txt
Normal file
@ -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<fangyidong@yahoo.com.cn>
|
||||
Fang Yidong<fangyidng@gmail.com>
|
340
plugin/licenses/LICENSE-GPLv2.txt
Normal file
@ -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.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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.
|
||||
|
||||
<signature of Ty Coon>, 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.
|
19
plugin/licenses/Transmission.txt
Normal file
@ -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.
|
138
scripts/makeplugin.sh
Executable file
@ -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
|
12
scripts/plugin.config
Normal file
@ -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
|
51
src/build.xml
Normal file
@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project basedir="." default="all" name="i2psnark-rpc">
|
||||
<property name="i2pbase" value="../../i2p.i2p"/>
|
||||
<property name="i2plib" value="${i2pbase}/build"/>
|
||||
<property name="jettylib" value="${i2pbase}/apps/jetty/jettylib"/>
|
||||
<path id="cp">
|
||||
<pathelement path="${java.class.path}" />
|
||||
<pathelement location="${i2plib}/i2p.jar" />
|
||||
<pathelement location="${i2plib}/i2psnark.jar" />
|
||||
<pathelement location="${jettylib}/javax.servlet.jar" />
|
||||
<pathelement location="${jettylib}/jetty-i2p.jar" />
|
||||
</path>
|
||||
|
||||
<target name="all" depends="clean, build" />
|
||||
<target name="build" depends="war" />
|
||||
|
||||
<property name="javac.compilerargs" value="" />
|
||||
<property name="javac.version" value="1.7" />
|
||||
|
||||
<target name="compile">
|
||||
<mkdir dir="./build/obj" />
|
||||
<javac
|
||||
srcdir="./java"
|
||||
debug="true" deprecation="on" source="${javac.version}" target="${javac.version}"
|
||||
destdir="./build/obj"
|
||||
includeAntRuntime="false"
|
||||
classpathref="cp" >
|
||||
<compilerarg line="${javac.compilerargs}" />
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<target name="war" depends="compile">
|
||||
<copy todir="jsp" >
|
||||
<fileset dir="../plugin" includes="licenses/*" />
|
||||
</copy>
|
||||
<war destfile="build/transmission.war.jar" webxml="jsp/WEB-INF/web.xml">
|
||||
<classes dir="./build/obj" />
|
||||
<fileset dir="jsp/" excludes="WEB-INF/" />
|
||||
</war>
|
||||
</target>
|
||||
|
||||
<target name="clean">
|
||||
<delete dir="./build" />
|
||||
<delete dir="jsp/licenses" />
|
||||
</target>
|
||||
|
||||
<target name="cleandep" depends="clean">
|
||||
</target>
|
||||
<target name="distclean" depends="clean">
|
||||
</target>
|
||||
</project>
|
@ -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";
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
205
src/java/org/json/simple/ItemList.java
Normal file
@ -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;
|
||||
|
||||
/**
|
||||
* <20><><EFBFBD><EFBFBD><EFBFBD>÷ָ<C3B7><D6B8><EFBFBD><EFBFBD>ֿ<EFBFBD><D6BF><EFBFBD>һ<EFBFBD><D2BB>item.<2E>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD>һ<EFBFBD><D2BB>item.ÿ<><C3BF>item<65><6D><EFBFBD>߲<EFBFBD><DFB2><EFBFBD><EFBFBD>ǿհ<D5B0>.
|
||||
* <20><><EFBFBD>磺
|
||||
* |a:b:c| => |a|,|b|,|c|
|
||||
* |:| => ||,||
|
||||
* |a:| => |a|,||
|
||||
* @author FangYidong<fangyidong@yahoo.com.cn>
|
||||
*/
|
||||
public class ItemList {
|
||||
private final static String sp=",";
|
||||
List<String> items=new ArrayList<String>();
|
||||
|
||||
|
||||
public ItemList(){}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param s <20>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD>ַ<EFBFBD><D6B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
*/
|
||||
public ItemList(String s){
|
||||
this.split(s,sp,items);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param s <20>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD>ַ<EFBFBD><D6B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @param sp <20>ָ<EFBFBD><D6B8><EFBFBD>
|
||||
*/
|
||||
//public ItemList(String s,String sp){
|
||||
// this.sp=s;
|
||||
// this.split(s,sp,items);
|
||||
//}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param s
|
||||
* @param sp
|
||||
* @param isMultiToken sp<73>Ƿ<EFBFBD>Ϊ<EFBFBD><CEAA>ָ<EFBFBD><D6B8><EFBFBD>
|
||||
*/
|
||||
public ItemList(String s,String sp,boolean isMultiToken){
|
||||
split(s,sp,items,isMultiToken);
|
||||
}
|
||||
|
||||
public List<String> getItems(){
|
||||
return this.items;
|
||||
}
|
||||
|
||||
public String[] getArray(){
|
||||
return (String[])this.items.toArray(new String[items.size()]);
|
||||
}
|
||||
|
||||
public void split(String s,String sp,List<String> 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<String> 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* <20><><EFBFBD>÷ָ<C3B7><D6B8><EFBFBD>.
|
||||
* @param sp <20>ָ<EFBFBD><D6B8><EFBFBD>
|
||||
*/
|
||||
//public void setSP(String sp){
|
||||
// this.sp=sp;
|
||||
//}
|
||||
|
||||
/**
|
||||
* <20><><EFBFBD>뵥<EFBFBD><EBB5A5>item.
|
||||
* @param i <20><><EFBFBD><EFBFBD><EFBFBD>λ<EFBFBD><CEBB>(֮ǰ)
|
||||
* @param item
|
||||
*/
|
||||
public void add(int i,String item){
|
||||
if(item==null)
|
||||
return;
|
||||
items.add(i,item.trim());
|
||||
}
|
||||
/**
|
||||
* <20><><EFBFBD>뵥<EFBFBD><EBB5A5>item.
|
||||
* @param item
|
||||
*/
|
||||
public void add(String item){
|
||||
if(item==null)
|
||||
return;
|
||||
items.add(item.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* <20><>һ<EFBFBD><D2BB>item.
|
||||
* @param list <20><><EFBFBD><EFBFBD><EFBFBD>list
|
||||
*/
|
||||
public void addAll(ItemList list){
|
||||
items.addAll(list.items);
|
||||
}
|
||||
|
||||
/**
|
||||
* <20><>һ<EFBFBD><D2BB>item.
|
||||
* @param s <20>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD>ַ<EFBFBD><D6B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
*/
|
||||
public void addAll(String s){
|
||||
this.split(s,sp,items);
|
||||
}
|
||||
|
||||
/**
|
||||
* <20><>һ<EFBFBD><D2BB>item.
|
||||
* @param s <20>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD>ַ<EFBFBD><D6B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
* @param sp <20>ָ<EFBFBD><D6B8><EFBFBD>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* <20><>õ<EFBFBD>i<EFBFBD><69>item. 0-based.
|
||||
* @param i
|
||||
* @return
|
||||
*/
|
||||
public String get(int i){
|
||||
return (String)items.get(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* <20><><EFBFBD>item<65><6D>.
|
||||
* @return
|
||||
*/
|
||||
public int size(){
|
||||
return items.size();
|
||||
}
|
||||
/**
|
||||
* <20>÷ָ<C3B7><D6B8><EFBFBD><EFBFBD>ָ<EFBFBD><D6B8>ı<EFBFBD>ʾ.
|
||||
*/
|
||||
public String toString(){
|
||||
return toString(sp);
|
||||
}
|
||||
|
||||
/**
|
||||
* <20>÷ָ<C3B7><D6B8><EFBFBD><EFBFBD>ָ<EFBFBD><D6B8>ı<EFBFBD>ʾ.
|
||||
* @param sp <20><><EFBFBD><EFBFBD>ø÷ָ<C3B7><D6B8><EFBFBD><EFBFBD>ָ<EFBFBD>.
|
||||
* @return
|
||||
*/
|
||||
public String toString(String sp){
|
||||
StringBuilder sb=new StringBuilder();
|
||||
|
||||
for(int i=0;i<items.size();i++){
|
||||
if(i==0)
|
||||
sb.append(items.get(i));
|
||||
else{
|
||||
sb.append(sp);
|
||||
sb.append(items.get(i));
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>item.
|
||||
*/
|
||||
public void clear(){
|
||||
items.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* <20><>λ.<2E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݣ<EFBFBD><DDA3><EFBFBD><EFBFBD>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD>Ĭ<EFBFBD><C4AC>ֵ.
|
||||
*/
|
||||
public void reset(){
|
||||
//sp=",";
|
||||
items.clear();
|
||||
}
|
||||
}
|
72
src/java/org/json/simple/JSONArray.java
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* $Id: JSONArray.java,v 1.1 2007-06-05 00:43:56 tuxpaper Exp $
|
||||
* Created on 2006-4-10
|
||||
*/
|
||||
package org.json.simple;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
|
||||
/**
|
||||
* @author FangYidong<fangyidong@yahoo.com.cn>
|
||||
*/
|
||||
public class JSONArray extends ArrayList<Object> {
|
||||
public JSONArray() {
|
||||
super();
|
||||
}
|
||||
|
||||
public JSONArray(Collection<Object> arg0) {
|
||||
super(arg0);
|
||||
}
|
||||
|
||||
public JSONArray(int initialCapacity) {
|
||||
super(initialCapacity);
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
ItemList list=new ItemList();
|
||||
|
||||
Iterator<Object> 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<Object> 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( "]" );
|
||||
}
|
||||
}
|
207
src/java/org/json/simple/JSONObject.java
Normal file
@ -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<fangyidong@yahoo.com.cn>
|
||||
*/
|
||||
public class JSONObject extends HashMap<String,Object>{
|
||||
|
||||
public JSONObject() {
|
||||
super();
|
||||
}
|
||||
|
||||
public JSONObject(int initialCapacity, float loadFactor) {
|
||||
super(initialCapacity, loadFactor);
|
||||
}
|
||||
|
||||
public JSONObject(int initialCapacity) {
|
||||
super(initialCapacity);
|
||||
}
|
||||
|
||||
public JSONObject(Map<String,Object> arg0) {
|
||||
super(arg0);
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
ItemList list=new ItemList();
|
||||
Iterator<Map.Entry<String, Object>> iter=entrySet().iterator();
|
||||
|
||||
while(iter.hasNext()){
|
||||
Map.Entry<String, Object> 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<s.length();i++){
|
||||
char ch=s.charAt(i);
|
||||
switch(ch){
|
||||
case '"':
|
||||
sb.append("\\\"");
|
||||
break;
|
||||
case '\\':
|
||||
sb.append("\\\\");
|
||||
break;
|
||||
case '\b':
|
||||
sb.append("\\b");
|
||||
break;
|
||||
case '\f':
|
||||
sb.append("\\f");
|
||||
break;
|
||||
case '\n':
|
||||
sb.append("\\n");
|
||||
break;
|
||||
case '\r':
|
||||
sb.append("\\r");
|
||||
break;
|
||||
case '\t':
|
||||
sb.append("\\t");
|
||||
break;
|
||||
case '/':
|
||||
sb.append("\\/");
|
||||
break;
|
||||
default:
|
||||
if(ch>='\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<s.length();i++){
|
||||
char ch=s.charAt(i);
|
||||
switch(ch){
|
||||
case '"':
|
||||
sb.append("\\\"");
|
||||
break;
|
||||
case '\\':
|
||||
sb.append("\\\\");
|
||||
break;
|
||||
case '\b':
|
||||
sb.append("\\b");
|
||||
break;
|
||||
case '\f':
|
||||
sb.append("\\f");
|
||||
break;
|
||||
case '\n':
|
||||
sb.append("\\n");
|
||||
break;
|
||||
case '\r':
|
||||
sb.append("\\r");
|
||||
break;
|
||||
case '\t':
|
||||
sb.append("\\t");
|
||||
break;
|
||||
case '/':
|
||||
sb.append("\\/");
|
||||
break;
|
||||
default:
|
||||
if(ch>='\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
|
||||
}
|
||||
}
|
||||
}
|
46
src/java/org/json/simple/JSONValue.java
Normal file
@ -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<fangyidong@yahoo.com.cn>
|
||||
*/
|
||||
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 "";
|
||||
}
|
||||
}
|
190
src/java/org/json/simple/parser/JSONParser.java
Normal file
@ -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<fangyidong@yahoo.com.cn>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
428
src/java/org/json/simple/parser/Yylex.java
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
31
src/java/org/json/simple/parser/Yytoken.java
Normal file
@ -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<fangyidong@yahoo.com.cn>
|
||||
*/
|
||||
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+"|");
|
||||
}
|
||||
}
|
@ -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 );
|
||||
}
|
201
src/java/org/klomp/snark/rpc/JSONUtils.java
Normal file
@ -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
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* 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.
|
||||
* <P>
|
||||
* 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<String, Object> entry: ((Map<String,Object>)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.
|
||||
* <p>
|
||||
* 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<Object> list = new ArrayList<Object>();
|
||||
for (boolean b : array) {
|
||||
list.add(b);
|
||||
}
|
||||
value = encodeToJSONArray(list);
|
||||
} else if (value instanceof long[]) {
|
||||
long[] array = (long[]) value;
|
||||
ArrayList<Object> list = new ArrayList<Object>();
|
||||
for (long b : array) {
|
||||
list.add(b);
|
||||
}
|
||||
value = encodeToJSONArray(list);
|
||||
} else if (value instanceof int[]) {
|
||||
int[] array = (int[]) value;
|
||||
ArrayList<Object> list = new ArrayList<Object>();
|
||||
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));
|
||||
}
|
||||
}
|
238
src/java/org/klomp/snark/rpc/MapUtils.java
Normal file
@ -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<result.length;i++){
|
||||
result[i] = getString( list.get(i));
|
||||
}
|
||||
return( result );
|
||||
}
|
||||
|
||||
public static String
|
||||
getString(
|
||||
Object obj )
|
||||
{
|
||||
if ( obj instanceof String ){
|
||||
return((String)obj);
|
||||
}else if ( obj instanceof byte[]){
|
||||
try{
|
||||
return new String((byte[])obj, "UTF-8");
|
||||
}catch( Throwable e ){
|
||||
|
||||
}
|
||||
}
|
||||
return( null );
|
||||
}
|
||||
|
||||
public static void setMapString(Map map, String key, String val ){
|
||||
if ( map == null ){
|
||||
//Debug.out( "Map is null!" );
|
||||
return;
|
||||
}
|
||||
try{
|
||||
if ( val == null ){
|
||||
map.remove( key );
|
||||
}else{
|
||||
map.put( key, val.getBytes( "utf-8" ));
|
||||
}
|
||||
}catch( Throwable e ){
|
||||
//Debug.out(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] getMapByteArray(Map map, String key, byte[] def) {
|
||||
if (map == null) {
|
||||
return def;
|
||||
}
|
||||
try {
|
||||
Object o = map.get(key);
|
||||
if (o instanceof byte[]) {
|
||||
return (byte[]) o;
|
||||
}
|
||||
String b64Key = key + ".B64";
|
||||
if (map.containsKey(b64Key)) {
|
||||
o = map.get(b64Key);
|
||||
if (o instanceof String) {
|
||||
return Base64.decode((String) o);
|
||||
}
|
||||
}
|
||||
String b32Key = key + ".B32";
|
||||
if (map.containsKey(b32Key)) {
|
||||
o = map.get(b32Key);
|
||||
if (o instanceof String) {
|
||||
return Base32.decode((String) o);
|
||||
}
|
||||
}
|
||||
return def;
|
||||
} catch (Throwable t) {
|
||||
//Debug.out(t);
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
public static Object getMapObject(Map map, String key, Object def, Class cla) {
|
||||
if (map == null) {
|
||||
return def;
|
||||
}
|
||||
try {
|
||||
Object o = map.get(key);
|
||||
if (cla.isInstance(o)) {
|
||||
return o;
|
||||
} else {
|
||||
return def;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
//Debug.out(t);
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean getMapBoolean(Map map, String key, boolean def) {
|
||||
if (map == null) {
|
||||
return def;
|
||||
}
|
||||
try {
|
||||
Object o = map.get(key);
|
||||
if (o instanceof Boolean) {
|
||||
return ((Boolean) o).booleanValue();
|
||||
}
|
||||
if (o instanceof Long) {
|
||||
return ((Long) o).longValue() == 1;
|
||||
}
|
||||
return def;
|
||||
} catch (Throwable e) {
|
||||
//Debug.out(e);
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
public static List getMapList(Map map, String key, List def) {
|
||||
if (map == null) {
|
||||
return def;
|
||||
}
|
||||
try {
|
||||
List list = (List) map.get(key);
|
||||
if (list == null && !map.containsKey(key)) {
|
||||
return def;
|
||||
}
|
||||
return list;
|
||||
} catch (Throwable t) {
|
||||
//Debug.out(t);
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
public static Map getMapMap(Map map, String key, Map def) {
|
||||
if (map == null) {
|
||||
return def;
|
||||
}
|
||||
try {
|
||||
Map valMap = (Map) map.get(key);
|
||||
if (valMap == null && !map.containsKey(key)) {
|
||||
return def;
|
||||
}
|
||||
return valMap;
|
||||
} catch (Throwable t) {
|
||||
//Debug.out(t);
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
66
src/java/org/klomp/snark/rpc/RPCServlet.java
Normal file
@ -0,0 +1,66 @@
|
||||
package org.klomp.snark.rpc;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.ClientAppManager;
|
||||
|
||||
import org.klomp.snark.SnarkManager;
|
||||
|
||||
/**
|
||||
* The servlet.
|
||||
*/
|
||||
public class RPCServlet extends HttpServlet {
|
||||
|
||||
private static final long serialVersionUID = 99999999L;
|
||||
|
||||
private final I2PAppContext _context = I2PAppContext.getGlobalContext();
|
||||
|
||||
private SnarkManager _manager;
|
||||
private XMWebUIPlugin _plugin;
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
XMWebUIPlugin plugin;
|
||||
synchronized(this) {
|
||||
ClientAppManager cmgr = _context.clientAppManager();
|
||||
if (cmgr == null) {
|
||||
resp.setContentType( "application/json; charset=UTF-8" );
|
||||
resp.sendError(403);
|
||||
_plugin = null;
|
||||
return;
|
||||
}
|
||||
SnarkManager smgr = (SnarkManager) cmgr.getRegisteredApp("i2psnark");
|
||||
if (smgr == null) {
|
||||
resp.setContentType( "application/json; charset=UTF-8" );
|
||||
resp.sendError(403);
|
||||
_plugin = null;
|
||||
return;
|
||||
}
|
||||
if (!smgr.equals(_manager)) {
|
||||
_manager = smgr;
|
||||
_plugin = new XMWebUIPlugin(_context, _manager);
|
||||
}
|
||||
plugin = _plugin;
|
||||
}
|
||||
boolean ok = plugin.generateSupport(req, resp);
|
||||
if (!ok) {
|
||||
resp.setContentType( "application/json; charset=UTF-8" );
|
||||
resp.sendError(403);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
synchronized(this) {
|
||||
_manager = null;
|
||||
_plugin = null;
|
||||
}
|
||||
super.destroy();
|
||||
}
|
||||
}
|
30
src/java/org/klomp/snark/rpc/TextualException.java
Normal file
@ -0,0 +1,30 @@
|
||||
package org.klomp.snark.rpc;
|
||||
|
||||
public class TextualException
|
||||
extends RuntimeException
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = -3302508436945014676L;
|
||||
|
||||
public TextualException() {
|
||||
}
|
||||
|
||||
public TextualException(String arg0) {
|
||||
super(arg0);
|
||||
|
||||
}
|
||||
|
||||
public TextualException(Throwable arg0) {
|
||||
super(arg0);
|
||||
|
||||
}
|
||||
|
||||
public TextualException(String arg0, Throwable arg1) {
|
||||
super(arg0, arg1);
|
||||
|
||||
}
|
||||
|
||||
}
|
87
src/java/org/klomp/snark/rpc/UIUtil.java
Normal file
@ -0,0 +1,87 @@
|
||||
package org.klomp.snark.rpc;
|
||||
|
||||
import org.klomp.snark.PeerID;
|
||||
|
||||
/**
|
||||
* From I2PSnarkServlet
|
||||
* TODO put in common dir
|
||||
*/
|
||||
class UIUtil {
|
||||
|
||||
private UIUtil() {}
|
||||
|
||||
/**
|
||||
* @param pid may be null
|
||||
* @return "Name w.x.y.z" or "Name"
|
||||
* @since 0.9.30
|
||||
*/
|
||||
public static String getClientName(PeerID pid) {
|
||||
String ch = pid != null ? pid.toString().substring(0, 4) : "????";
|
||||
String client;
|
||||
if ("AwMD".equals(ch))
|
||||
client = "I2PSnark";
|
||||
else if ("BFJT".equals(ch))
|
||||
client = "I2PRufus";
|
||||
else if ("TTMt".equals(ch))
|
||||
client = "I2P-BT";
|
||||
else if ("LUFa".equals(ch))
|
||||
client = "Vuze" + getAzVersion(pid.getID());
|
||||
else if ("CwsL".equals(ch))
|
||||
client = "I2PSnarkXL";
|
||||
else if ("LVhE".equals(ch))
|
||||
client = "XD" + getAzVersion(pid.getID());
|
||||
else if ("ZV".equals(ch.substring(2,4)) || "VUZP".equals(ch))
|
||||
client = "Robert" + getRobtVersion(pid.getID());
|
||||
else if (ch.startsWith("LV")) // LVCS 1.0.2?; LVRS 1.0.4
|
||||
client = "Transmission" + getAzVersion(pid.getID());
|
||||
else if ("LUtU".equals(ch))
|
||||
client = "KTorrent" + getAzVersion(pid.getID());
|
||||
else
|
||||
client = "Unknown (" + ch + ')';
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get version from bytes 3-6
|
||||
* @return " w.x.y.z" or ""
|
||||
* @since 0.9.14
|
||||
*/
|
||||
private static String getAzVersion(byte[] id) {
|
||||
if (id[7] != '-')
|
||||
return "";
|
||||
StringBuilder buf = new StringBuilder(16);
|
||||
buf.append(' ');
|
||||
for (int i = 3; i <= 6; i++) {
|
||||
int val = id[i] - '0';
|
||||
if (val < 0)
|
||||
return "";
|
||||
if (val > 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();
|
||||
}
|
||||
}
|
111
src/java/org/klomp/snark/rpc/WebPlugin.java
Normal file
@ -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" };
|
||||
}
|
5654
src/java/org/klomp/snark/rpc/XMWebUIPlugin.java
Normal file
30
src/jsp/WEB-INF/web.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE web-app
|
||||
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
|
||||
"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
|
||||
<web-app>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>org.klomp.snark.rpc.RPCServlet</servlet-name>
|
||||
<servlet-class>org.klomp.snark.rpc.RPCServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>org.klomp.snark.rpc.RPCServlet</servlet-name>
|
||||
<!-- from external transmission-remote -->
|
||||
<url-pattern>/rpc</url-pattern>
|
||||
<url-pattern>/rpc/*</url-pattern>
|
||||
<url-pattern>/upload</url-pattern>
|
||||
<url-pattern>/upload/*</url-pattern>
|
||||
<!-- from internal web UI -->
|
||||
<url-pattern>/web/transmission/rpc</url-pattern>
|
||||
<url-pattern>/web/transmission/rpc/*</url-pattern>
|
||||
<url-pattern>/web/transmission/upload</url-pattern>
|
||||
<url-pattern>/web/transmission/upload/*</url-pattern>
|
||||
<url-pattern>/web/vuze/rpc</url-pattern>
|
||||
<url-pattern>/web/vuze/rpc/*</url-pattern>
|
||||
<url-pattern>/web/vuze/upload</url-pattern>
|
||||
<url-pattern>/web/vuze/upload/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
</web-app>
|
29
src/jsp/index.html
Normal file
@ -0,0 +1,29 @@
|
||||
<html>
|
||||
<body>
|
||||
<h2>i2psnark-rpc Plugin Version 0.1</h2>
|
||||
<p>
|
||||
This is a Transmission- and Vuze- compatible RPC server for i2psnark,
|
||||
with the Vuze-modified Transmission web UI.
|
||||
|
||||
<p>
|
||||
To access the web UI, go to <a href="web/">/transmission/web/</a> in your router console.
|
||||
|
||||
<p>
|
||||
To use any compatible RPC client software, such as transmission-remote,
|
||||
specify port 7657. For example, to list the torrents:
|
||||
|
||||
<pre>
|
||||
transmission-remote 7657 -l
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Most basic features are supported. Several advanced features are not supported.
|
||||
Some may be added in a future release.
|
||||
Please report bugs on <a href="http://zzz.i2p/forums/16">zzz.i2p</a>.
|
||||
|
||||
<p>
|
||||
<a href="web/">Web UI</a>
|
||||
<br>
|
||||
<a href="licenses.html">Licenses</a>
|
||||
</body>
|
||||
</html>
|
45
src/jsp/licenses.html
Normal file
@ -0,0 +1,45 @@
|
||||
<html>
|
||||
<body>
|
||||
<h2>i2psnark-rpc licenses</h2>
|
||||
<p>
|
||||
New code: GPLv2
|
||||
<br>
|
||||
See <a href="licenses/LICENSE-GPLv2.txt">LICENSE-GPLv2.txt</a>
|
||||
|
||||
<p>
|
||||
Contains code from Vuze trunk and from xmwebui plugin,
|
||||
which includes code from Transmission and JSON-simple.
|
||||
|
||||
<p>
|
||||
Vuze Web Remote Plugin (xmwebui): GPLv2
|
||||
<br>
|
||||
Modified from v0.6.5 from SVN
|
||||
<br>
|
||||
Copyright 2009 Vuze, Inc. All rights reserved.
|
||||
<br>
|
||||
See <a href="licenses/LICENSE-GPLv2.txt">LICENSE-GPLv2.txt</a>
|
||||
|
||||
<p>
|
||||
Vuze: GPLv2
|
||||
<br>
|
||||
Modified from v5.7.5.1 from SVN
|
||||
<br>
|
||||
Copyright 2009 Vuze, Inc. All rights reserved.
|
||||
<br>
|
||||
See <a href="licenses/LICENSE-GPLv2.txt">LICENSE-GPLv2.txt</a>
|
||||
|
||||
<p>
|
||||
Transmission:
|
||||
<br>
|
||||
Copyright (c) Transmission authors and contributors
|
||||
<br>
|
||||
See <a href="licenses/Transmission.txt">Transmission.txt</a>
|
||||
|
||||
<p>
|
||||
JSON:
|
||||
<br>
|
||||
LGPLv2.1
|
||||
<br>
|
||||
See <a href="licenses/JSON.txt">JSON.txt</a>
|
||||
</body>
|
||||
</html>
|
25
src/jsp/web/easyXDM/hash.html
Normal file
@ -0,0 +1,25 @@
|
||||
<!--
|
||||
easyXDM
|
||||
http://easydm.net/
|
||||
Copyright(c) 2009, <20>yvind Sean Kinsey, oyvind@kinsey.no.
|
||||
|
||||
MIT Licensed - http://easyxdm.net/license/mit.txt
|
||||
-->
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<meta http-equiv="CACHE-CONTROL" content="PUBLIC"/>
|
||||
<meta http-equiv="EXPIRES" content="Sat, 01 Jan 2050 00:00:00 GMT"/>
|
||||
<script type="text/javascript">
|
||||
if (location.hash) {
|
||||
/**
|
||||
* Notify the library that the transport is ready
|
||||
*/
|
||||
window.parent.parent.easyXDM.transport.HashTransport.channelReady(location.hash.substring(1), window);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
BIN
src/jsp/web/images/favicon.ico
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/jsp/web/images/favicon.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
src/jsp/web/images/webclip-icon.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
461
src/jsp/web/index.html
Normal file
@ -0,0 +1,461 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=8,IE=9,IE=10"><!-- ticket #4555 -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<link href="./images/favicon.ico" rel="icon" />
|
||||
<link href="./images/favicon.png" rel="shortcut icon" />
|
||||
<link rel="apple-touch-icon" href="./images/webclip-icon.png"/>
|
||||
<script type="text/javascript" src="./javascript/jquery/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="./javascript/jquery/jquery.mobile.custom.js"></script>
|
||||
<script type="text/javascript" src="./javascript/jquery/purl.js"></script>
|
||||
|
||||
<script type="text/javascript" src="./javascript/jquery/jquery-ui-1.10.3.custom.min.js"></script>
|
||||
<link rel="stylesheet" href="./style/jqueryui/jquery-ui-1.10.3.custom.min.css" type="text/css" media="all" />
|
||||
<!--
|
||||
<script type="text/javascript" src="./javascript/jquery/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="./javascript/jquery/jqueryui-1.8.16.min.js"></script>
|
||||
<link rel="stylesheet" href="./style/jqueryui/jqueryui-1.8.16.css" type="text/css" media="all" />
|
||||
<link media="screen" href="./style/transmission/mobile.css" type= "text/css" rel="stylesheet" />
|
||||
-->
|
||||
|
||||
<link href="./style/transmission/common.css" type="text/css" rel="stylesheet" />
|
||||
<link href="./style/transmission/vuze.css" type="text/css" rel="stylesheet" />
|
||||
<link media="screen and (max-device-width: 900px)" href="./style/transmission/vuze-mw-900.css" type="text/css" rel="stylesheet" />
|
||||
<link media="screen and (max-device-width: 420px)" href="./style/transmission/vuze-mw-420.css" type="text/css" rel="stylesheet" />
|
||||
<link media="screen and (-webkit-device-pixel-ratio: 0.75)" href="./style/transmission/vuze-mw-420.css" type="text/css" rel="stylesheet" />
|
||||
|
||||
<!--
|
||||
<ink media="only screen and (max-device-width: 480px)" href="./style/transmission/mobile.css" type= "text/css" rel="stylesheet" />
|
||||
<ink media="screen and (min-device-width: 481px)" href="./style/transmission/common.css" type="text/css" rel="stylesheet" />
|
||||
<ink media="only screen and (max-device-width: 480px)" href="./style/transmission/vuze-mobile.css" type= "text/css" rel="stylesheet" />
|
||||
<script type="text/javascript" src="./javascript/jquery/jquery.transmenu.js"></script>
|
||||
<script type="text/javascript" src="./javascript/jquery/jquery.contextmenu.min.js"></script>
|
||||
<script type="text/javascript" src="./javascript/jquery/jquery.ctxtmnu.js"></script>
|
||||
-->
|
||||
<script type="text/javascript" src="./javascript/jquery/json2.min.js"></script>
|
||||
<script type="text/javascript" src="./javascript/common.js"></script>
|
||||
<script type="text/javascript" src="./javascript/inspector.js"></script>
|
||||
<script type="text/javascript" src="./javascript/prefs-dialog.js"></script>
|
||||
<script type="text/javascript" src="./javascript/remote.js"></script>
|
||||
<script type="text/javascript" src="./javascript/transmission.js"></script>
|
||||
<script type="text/javascript" src="./javascript/torrent.js"></script>
|
||||
<script type="text/javascript" src="./javascript/torrent-row.js"></script>
|
||||
<script type="text/javascript" src="./javascript/file-row.js"></script>
|
||||
<script type="text/javascript" src="./javascript/dialog.js"></script>
|
||||
<script type="text/javascript" src="./javascript/formatter.js"></script>
|
||||
<script type="text/javascript" src="./javascript/notifications.js"></script>
|
||||
<script type="text/javascript" src="./javascript/easyXDM-2.4.18.4.min.js"></script>
|
||||
<script type="text/javascript" src="./javascript/vuze.js"></script>
|
||||
<title>Vuze Remote</title>
|
||||
</head>
|
||||
<body id="transmission_body">
|
||||
|
||||
<div id="header">
|
||||
<div id="toolbar-area">
|
||||
<div id="toolbar" class="toolbar-main">
|
||||
<div id="toolbar-open" title="Add Torrent"></div>
|
||||
<div id="toolbar-start" title="Start Selected Torrents"></div>
|
||||
<div id="toolbar-pause" title="Stop Selected Torrents"></div>
|
||||
<div id="toolbar-remove" title="Remove Selected Torrents"></div>
|
||||
<div id="toolbar-start-all" title="Start All Torrents"></div>
|
||||
<div id="toolbar-pause-all" title="Pause All Torrents"></div>
|
||||
<div id="toolbar-search" title="Search"></div>
|
||||
|
||||
<div id="toolbar-inspector" title="Toggle Inspector"></div>
|
||||
</div>
|
||||
<div id="toolbar" class="toolbar-vuze">
|
||||
<div id="search_li">
|
||||
<div class="search_term">
|
||||
<form name="search_form" onsubmit="vz.executeSearch();return false;">
|
||||
<input id="search_input" name="search_input" placeholder="Find..." />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div id="toolbar-remote-search" title="Search"></div>
|
||||
<div id="toolbar-remote" title="Back to Remote"></div>
|
||||
</div>
|
||||
<div id="log_out" style="display:none">log out</div>
|
||||
|
||||
<div id='speed-info'>
|
||||
<div id='speed-dn-container'>
|
||||
<div id='speed-dn-icon'></div>
|
||||
<div id='speed-dn-label'></div>
|
||||
</div>
|
||||
<div id='speed-up-container'>
|
||||
<div id='speed-up-icon'></div>
|
||||
<div id='speed-up-label'></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="torrent_logo">
|
||||
<img src="style/transmission/images/graphics/vuze_remote_logo.png" alt="Vuze Remote">
|
||||
</div>
|
||||
|
||||
<div id="statusbar">
|
||||
<div id="settings_menu" title="Settings Menu">
|
||||
</div>
|
||||
<div id="turtle-button" title="Alternative Speed Limits"> </div>
|
||||
<div id="compact-button" title="Compact View"> </div>
|
||||
<div id="prefs-button" title="Edit Preferences..."> </div>
|
||||
|
||||
<div id='filter'>
|
||||
Show
|
||||
<select id="filter-mode">
|
||||
<option value="all">All</option>
|
||||
<option value="active">Active</option>
|
||||
<option value="complete">Complete</option>
|
||||
<option value="incomplete">Incomplete </option>
|
||||
<option value="paused">Stopped</option>
|
||||
</select>
|
||||
<select id="filter-tracker"></select>
|
||||
<input type="search" id="torrent_search" placeholder="Filter" />
|
||||
<span id="filter-count"> </span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="prefs-dialog" style="display:none;">
|
||||
<ul>
|
||||
<li id="prefs-tab-general"><a href="#prefs-page-torrents">Torrents</a></li>
|
||||
<li id="prefs-tab-speed"><a href="#prefs-page-speed">Speed</a></li>
|
||||
<li id="prefs-tab-peers"><a href="#prefs-page-peers">Peers</a></li>
|
||||
<li id="prefs-tab-network"><a href="#prefs-page-network">Network</a></li>
|
||||
<li class="ui-tab-dialog-close"></li>
|
||||
</ul>
|
||||
<div>
|
||||
<div id="prefs-page-torrents">
|
||||
<div class="prefs-section">
|
||||
<div class="title">Downloading</div>
|
||||
<div class="row"><div class="key">Download to:</div><div class="value"><input type="text" id="download-dir"/></div></div>
|
||||
<div class="checkbox-row"><input type="checkbox" id="start-added-torrents"/><label for="start-added-torrents">Start when added</label></div>
|
||||
<div class="checkbox-row"><input type="checkbox" id="rename-partial-files"/><label for="rename-partial-files">Append ".az!" to incomplete files' names</label></div>
|
||||
</div>
|
||||
<div class="prefs-section" id="prefs-section-seeding">
|
||||
<div class="title">Seeding</div>
|
||||
<div class="row"><div class="key"><input type="checkbox" id="seedRatioLimited"/><label for="seedRatioLimited">Stop seeding at ratio:</label></div>
|
||||
<div class="value"><input type="text" type="number" id="seedRatioLimit"/></div></div>
|
||||
<div class="row"><div class="key"><input type="checkbox" id="idle-seeding-limit-enabled"/><label for="idle-seeding-limit-enabled">Stop seeding if idle for (min):</label></div>
|
||||
<div class="value"><input type="text" type="number" id="idle-seeding-limit"/></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="prefs-page-speed">
|
||||
<div class="prefs-section">
|
||||
<div class="title">Speed Limits</div>
|
||||
<div class="row"><div class="key"><input type="checkbox" id="speed-limit-up-enabled"/><label for="speed-limit-up-enabled">Upload (kB/s):</label></div>
|
||||
<div class="value"><input type="text" type="number" id="speed-limit-up"/></div></div>
|
||||
<div class="row"><div class="key"><input type="checkbox" id="speed-limit-down-enabled"/><label for="speed-limit-down-enabled">Download (kB/s):</label></div>
|
||||
<div class="value"><input type="text" type="number" id="speed-limit-down"/></div></div>
|
||||
</div>
|
||||
<div class="prefs-section" id="prefs-section-alternative-speed-limits">
|
||||
<div class="title"><div id="alternative-speed-limits-title">Alternative Speed Limits</div></div>
|
||||
<div class="row" style="font-size: smaller; padding-bottom: 4px;">Override normal speed limits manually or at scheduled times</div>
|
||||
<div class="row"><div class="key">Upload (kB/s):</div>
|
||||
<div class="value"><input type="text" type="number" id="alt-speed-up"/></div></div>
|
||||
<div class="row"><div class="key">Download (kB/s):</div>
|
||||
<div class="value"><input type="text" type="number" id="alt-speed-down"/></div></div>
|
||||
<div class="checkbox-row"><input type="checkbox" id="alt-speed-time-enabled"/><label for="alt-speed-time-enabled">Scheduled Times</label></div>
|
||||
<div class="row"><div class="key">From:</div>
|
||||
<div class="value"><select id="alt-speed-time-begin"></select></div></div>
|
||||
<div class="row"><div class="key">To:</div>
|
||||
<div class="value"><select id="alt-speed-time-end"></select></div></div>
|
||||
<div class="row"><div class="key"><label for="alt-speed-time-day">On days:</label></div>
|
||||
<div class="value"><select id="alt-speed-time-day">
|
||||
<option value="127">Everyday</option>
|
||||
<option value="62">Weekdays</option>
|
||||
<option value="65">Weekends</option>
|
||||
<option value="1">Sunday</option>
|
||||
<option value="2">Monday</option>
|
||||
<option value="4">Tuesday</option>
|
||||
<option value="8">Wednesday</option>
|
||||
<option value="16">Thursday</option>
|
||||
<option value="32">Friday</option>
|
||||
<option value="64">Saturday</option></select></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="prefs-page-peers">
|
||||
<div class="prefs-section">
|
||||
<div class="title">Connections</div>
|
||||
<div class="row"><div class="key"><label for="peer-limit-per-torrent">Max peers per torrent:</label></div>
|
||||
<div class="value"><input type="text" type="number" id="peer-limit-per-torrent"/></div></div>
|
||||
<div class="row"><div class="key"><label for="peer-limit-global">Max peers overall:</label></div>
|
||||
<div class="value"><input type="text" type="number" id="peer-limit-global"/></div></div>
|
||||
</div>
|
||||
<div class="prefs-section">
|
||||
<div class="title">Options</div>
|
||||
<div class="row"><div class="key">Encryption mode:</div>
|
||||
<div class="value"><select id="encryption">
|
||||
<option value="tolerated">Allow encryption</option>
|
||||
<option value="preferred">Prefer encryption</option>
|
||||
<option value="required">Require encryption</option></select></div></div>
|
||||
<div class="checkbox-row"><input type="checkbox" id="pex-enabled" title="PEX is a tool for exchanging peer lists with the peers you're connected to."/>
|
||||
<label for="pex-enabled" title="PEX is a tool for exchanging peer lists with the peers you're connected to.">Use PEX to find more peers</label></div>
|
||||
<div class="checkbox-row"><input type="checkbox" id="dht-enabled" title="DHT is a tool for finding peers without a tracker."/>
|
||||
<label for="dht-enabled" title="DHT is a tool for finding peers without a tracker.">Use DHT to find more peers</label></div>
|
||||
<div class="checkbox-row"><input type="checkbox" id="lpd-enabled" title="LPD is a tool for finding peers on your local network."/>
|
||||
<label for="lpd-enabled" title="LPD is a tool for finding peers on your local network.">Use LPD to find more peers</label></div>
|
||||
</div>
|
||||
<div class="prefs-section" id="prefs-section-blocklist">
|
||||
<div class="title">Blocklist</div>
|
||||
<div class="row"><div class="key"><input type="checkbox" id="blocklist-enabled"/><label for="blocklist-enabled">Enable blocklist:</label></div>
|
||||
<div class="value"><input type="url" id="blocklist-url"/></div></div>
|
||||
<div class="row"><div class="key" style="margin-top: 3px; font-size: smaller;">Blocklist has <span id="blocklist-size">?</span> rules</div>
|
||||
<div class="value"><input type="button" id="blocklist-update-button" value="Update"/></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="prefs-page-network">
|
||||
<div class="prefs-section">
|
||||
<div class="title">Listening Port</div>
|
||||
<div class="row"><div class="key"><label for="peer-port">Peer listening port:</label></div>
|
||||
<div class="value"><input type="text" type="number" id="peer-port"/></div></div>
|
||||
<div class="row"><div class="key"> </div>
|
||||
<div class="value"><span id="port-label">Status: Unknown</span></div></div>
|
||||
<div id="prefs-section-peer-port-random-on-start" class="checkbox-row"><input type="checkbox" id="peer-port-random-on-start"/><label for="peer-port-random-on-start">Randomize port on launch</label></div>
|
||||
<div id="prefs-section-port-forwarding-enabled" class="checkbox-row"><input type="checkbox" id="port-forwarding-enabled"/><label for="port-forwarding-enabled">Use port forwarding from my router</label></div>
|
||||
</div>
|
||||
<div class="prefs-section">
|
||||
<div class="title">Options</div>
|
||||
<div class="checkbox-row"><input type="checkbox" id="utp-enabled" title="uTP is a tool for reducing network congestion."/>
|
||||
<label for="utp-enabled" title="uTP is a tool for reducing network congestion.">Enable uTP for peer communication</label></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul id="footer_super_menu">
|
||||
<li id="preferences"><a href="#">Preferences</a></li>
|
||||
<li id="menu_sep_rates" class="separator"></li>
|
||||
<li id="menu_total_download_rate"><a href="#">Total Download Rate</a>
|
||||
<ul id="footer_download_rate_menu">
|
||||
<li id="unlimited_download_rate"><a href="#">Unlimited</a></li>
|
||||
<li id="limited_download_rate"><a href="#">Limit (10 KB/s)</a></li>
|
||||
<li class="separator"></li>
|
||||
<li class='download-speed'><a href="#">5 KB/s</a></li>
|
||||
<li class='download-speed'><a href="#">10 KB/s</a></li>
|
||||
<li class='download-speed'><a href="#">20 KB/s</a></li>
|
||||
<li class='download-speed'><a href="#">30 KB/s</a></li>
|
||||
<li class='download-speed'><a href="#">40 KB/s</a></li>
|
||||
<li class='download-speed'><a href="#">50 KB/s</a></li>
|
||||
<li class='download-speed'><a href="#">75 KB/s</a></li>
|
||||
<li class='download-speed'><a href="#">100 KB/s</a></li>
|
||||
<li class='download-speed'><a href="#">150 KB/s</a></li>
|
||||
<li class='download-speed'><a href="#">200 KB/s</a></li>
|
||||
<li class='download-speed'><a href="#">250 KB/s</a></li>
|
||||
<li class='download-speed'><a href="#">500 KB/s</a></li>
|
||||
<li class='download-speed'><a href="#">750 KB/s</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li id="menu_total_upload_rate"><a href="#">Total Upload Rate</a>
|
||||
<ul id="footer_upload_rate_menu">
|
||||
<li id="unlimited_upload_rate"><a href="#">Unlimited</a></li>
|
||||
<li id="limited_upload_rate"><a href="#">Limit (10 KB/s)</a></li>
|
||||
<li class="separator"></li>
|
||||
<li class='upload-speed'><a href="#">5 KB/s</a></li>
|
||||
<li class='upload-speed'><a href="#">10 KB/s</a></li>
|
||||
<li class='upload-speed'><a href="#">20 KB/s</a></li>
|
||||
<li class='upload-speed'><a href="#">30 KB/s</a></li>
|
||||
<li class='upload-speed'><a href="#">40 KB/s</a></li>
|
||||
<li class='upload-speed'><a href="#">50 KB/s</a></li>
|
||||
<li class='upload-speed'><a href="#">75 KB/s</a></li>
|
||||
<li class='upload-speed'><a href="#">100 KB/s</a></li>
|
||||
<li class='upload-speed'><a href="#">150 KB/s</a></li>
|
||||
<li class='upload-speed'><a href="#">200 KB/s</a></li>
|
||||
<li class='upload-speed'><a href="#">250 KB/s</a></li>
|
||||
<li class='upload-speed'><a href="#">500 KB/s</a></li>
|
||||
<li class='upload-speed'><a href="#">750 KB/s</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="separator"></li>
|
||||
<li><a href="#">Sort Transfers By</a>
|
||||
<ul id="footer_sort_menu">
|
||||
<li class='sort-mode' id="sort_by_queue_order"><a href="#">Queue Order</a></li>
|
||||
<li class='sort-mode' id="sort_by_activity"><a href="#">Activity</a></li>
|
||||
<li class='sort-mode' id="sort_by_age"><a href="#">Age</a></li>
|
||||
<li class='sort-mode' id="sort_by_name"><a href="#">Name</a></li>
|
||||
<li class='sort-mode' id="sort_by_percent_completed"><a href="#">Progress</a></li>
|
||||
<li class='sort-mode' id="sort_by_ratio"><a href="#">Ratio</a></li>
|
||||
<li class='sort-mode' id="sort_by_size"><a href="#">Size</a></li>
|
||||
<li class='sort-mode' id="sort_by_state"><a href="#">State</a></li>
|
||||
<li class="separator"></li>
|
||||
<li id="reverse_sort_order"><a href="#">Reverse Sort Order</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="separator"></li>
|
||||
<li id="compact_view"><a href="#">Compact View</a></li>
|
||||
<li id="tipjar"><a href="#">Transmission Tip Jar</a></li>
|
||||
</ul>
|
||||
|
||||
<div id="remotesearch_container" style="display:none" class="scrollable">
|
||||
<!-- iframe id="remotesearch" src="about:blank"></iframe-->
|
||||
</div>
|
||||
|
||||
<div id="torrent_inspector" style="display:none;" class="scrollable">
|
||||
|
||||
<div id="inspector-close" class="inspector_close"></div>
|
||||
|
||||
<div id="inspector-tabs-wrapper">
|
||||
<div id="inspector-tabs">
|
||||
<div class="inspector-tab selected" id="inspector-tab-info" title="Info"><a href="#info"></a></div><div class="inspector-tab" id="inspector-tab-peers" title="Peers"><a href="#peers"></a></div><div class="inspector-tab" id="inspector-tab-trackers" title="Trackers"><a href="#trackers"></a></div><div class="inspector-tab" id="inspector-tab-files" title="Files"><a href="#files"></a></div>
|
||||
</div><!-- inspector-tabs -->
|
||||
</div><!-- inspector-tabs-wrapper -->
|
||||
|
||||
<div id="inspector_header">
|
||||
<div id="torrent_inspector_name"></div>
|
||||
<span id="torrent_inspector_size"></span>
|
||||
</div>
|
||||
|
||||
<div class="inspector-page" id="inspector-page-info">
|
||||
<div class="prefs-section">
|
||||
<div class="title">Activity</div>
|
||||
<div class="row"><div class="key">Have:</div><div class="value" id="inspector-info-have"> </div></div>
|
||||
<div class="row"><div class="key">Availability:</div><div class="value" id="inspector-info-availability"> </div></div>
|
||||
<div class="row"><div class="key">Downloaded:</div><div class="value" id="inspector-info-downloaded"> </div></div>
|
||||
<div class="row"><div class="key">Uploaded:</div><div class="value" id="inspector-info-uploaded"> </div></div>
|
||||
<div class="row"><div class="key">State:</div><div class="value" id="inspector-info-state"> </div></div>
|
||||
<div class="row"><div class="key">Running Time:</div><div class="value" id="inspector-info-running-time"> </div></div>
|
||||
<div class="row"><div class="key">Remaining Time:</div><div class="value" id="inspector-info-remaining-time"> </div></div>
|
||||
<div class="row"><div class="key">Last Activity:</div><div class="value" id="inspector-info-last-activity"> </div></div>
|
||||
<div class="row"><div class="key">Error:</div><div class="value" id="inspector-info-error"> </div></div>
|
||||
</div>
|
||||
<div class="prefs-section">
|
||||
<div class="title">Details</div>
|
||||
<div class="row"><div class="key">Size:</div><div class="value" id="inspector-info-size"> </div></div>
|
||||
<div class="row"><div class="key">Location:</div><div class="value" id="inspector-info-location"> </div></div>
|
||||
<div class="row"><div class="key">Hash:</div><div class="value" id="inspector-info-hash"> </div></div>
|
||||
<div class="row"><div class="key">Privacy:</div><div class="value" id="inspector-info-privacy"> </div></div>
|
||||
<div class="row"><div class="key">Origin:</div><div class="value" id="inspector-info-origin"> </div></div>
|
||||
<div class="row"><div class="key">Comment:</div><div class="value" id="inspector-info-comment"> </div></div>
|
||||
<div class="row"><div class="key">Tags:</div><div class="value" id="inspector-info-tags"> </div></div>
|
||||
</div>
|
||||
</div><!-- id="inspector_tab_info_container" -->
|
||||
|
||||
<div style="display:none;" class="inspector-page" id="inspector-page-peers">
|
||||
<div id="inspector_peers_list">
|
||||
</div>
|
||||
</div><!-- id="inspector_tab_peers_container" -->
|
||||
|
||||
<div style="display:none;" class="inspector-page" id="inspector-page-trackers">
|
||||
<div id="inspector_trackers_list">
|
||||
</div>
|
||||
</div><!-- id="inspector_tab_trackers_container" -->
|
||||
|
||||
<div style="display:none;" class="inspector-page" id="inspector-page-files">
|
||||
<ul id="inspector_file_list">
|
||||
</ul>
|
||||
</div><!-- id="inspector_tab_files_container" -->
|
||||
|
||||
</div>
|
||||
|
||||
<div id="torrent_container" class="scrollable">
|
||||
<ul class="torrent_list" id="torrent_list"></ul>
|
||||
</div>
|
||||
|
||||
<div class="dialog_container" id="dialog_container" style="display:none;">
|
||||
<div class="dialog_top_bar"></div>
|
||||
<div class="dialog_window">
|
||||
<div class="dialog_logo" id="upload_dialog_logo"></div>
|
||||
<h2 class="dialog_heading" id="dialog_heading"></h2>
|
||||
<div class="dialog_message" id="dialog_message"></div>
|
||||
<a href="#confirm" id="dialog_confirm_button">Confirm</a>
|
||||
<a href="#cancel" id="dialog_cancel_button">Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="about-dialog" style="display:none;">
|
||||
<p id="about-logo"></p>
|
||||
<p id="about-title">Transmission X with Vuze Modifications</p>
|
||||
<p id="about-blurb">A fast and easy BitTorrent client</p>
|
||||
<p id="about-copyright">Copyright (c) The Transmission Project. GPLv2</p>
|
||||
</div>
|
||||
|
||||
<div id="stats-dialog" style="display:none;">
|
||||
<div class="prefs-section">
|
||||
<div class="title">Current Session</div>
|
||||
<div class="row"><div class="key">Uploaded:</div><div class="value" id='stats-session-uploaded'> </div></div>
|
||||
<div class="row"><div class="key">Downloaded:</div><div class="value" id='stats-session-downloaded'> </div></div>
|
||||
<div class="row"><div class="key">Ratio:</div><div class="value" id='stats-session-ratio'> </div></div>
|
||||
<div class="row"><div class="key">Running Time:</div><div class="value" id='stats-session-duration'> </div></div>
|
||||
</div>
|
||||
<div class="prefs-section">
|
||||
<div class="title">Total</div>
|
||||
<div class="row"><div class="key">Started:</div><div class="value" id='stats-total-count'> </div></div>
|
||||
<div class="row"><div class="key">Uploaded:</div><div class="value" id='stats-total-uploaded'> </div></div>
|
||||
<div class="row"><div class="key">Downloaded:</div><div class="value" id='stats-total-downloaded'> </div></div>
|
||||
<div class="row"><div class="key">Ratio:</div><div class="value" id='stats-total-ratio'> </div></div>
|
||||
<div class="row"><div class="key">Running Time:</div><div class="value" id='stats-total-duration'> </div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dialog_container" id="upload_container" style="display:none;">
|
||||
<div class="dialog_top_bar"></div>
|
||||
<div class="dialog_window">
|
||||
<div class="dialog_logo" id="dialog_logo"></div>
|
||||
<h2 class="dialog_heading">Add Torrent</h2>
|
||||
<form action="#" method="post" id="torrent_upload_form"
|
||||
enctype="multipart/form-data" target="torrent_upload_frame">
|
||||
<div class="dialog_message">
|
||||
<label for="torrent_upload_file">Please select a torrent to add:</label>
|
||||
<input type="file" name="torrent_files[]" id="torrent_upload_file" multiple="multiple" />
|
||||
<label for="torrent_upload_url">Or enter a URL:</label>
|
||||
<input type="url" id="torrent_upload_url"/>
|
||||
<label id='add-dialog-folder-label' for="add-dialog-folder-input">Destination folder:</label>
|
||||
<input type="text" id="add-dialog-folder-input"/>
|
||||
<input type="checkbox" id="torrent_auto_start" />
|
||||
<label for="torrent_auto_start" id="auto_start_label">Start when added</label>
|
||||
</div>
|
||||
<a href="#upload" id="upload_confirm_button">Add</a>
|
||||
<a href="#cancel" id="upload_cancel_button">Cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dialog_container" id="move_container" style="display:none;">
|
||||
<div class="dialog_top_bar"></div>
|
||||
<div class="dialog_window">
|
||||
<div class="dialog_logo" id="move_dialog_logo"></div>
|
||||
<h2 class="dialog_heading">Set Location</h2>
|
||||
<form action="#" method="post" id="torrent_move_form"
|
||||
enctype="multipart/form-data" target="torrent_move_frame">
|
||||
<div class="dialog_message">
|
||||
<label for="torrent_path">Location:</label>
|
||||
<input type="text" id="torrent_path"/>
|
||||
</div>
|
||||
<a href="#move" id="move_confirm_button">Apply</a>
|
||||
<a href="#cancel" id="move_cancel_button">Cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="torrent_footer">
|
||||
</div>
|
||||
|
||||
<ul id='ul_torrent_context_menu'>
|
||||
<li id="context_pause_selected" class="disabled context_pause_selected"><a href="#">Stop Selected</a></li>
|
||||
<li id="context_resume_selected" class="disabled context_resume_selected"><a href="#">Start Selected</a></li>
|
||||
<li id="context_resume_now_selected" class="disabled context_resume_selected"><a href="#">Start Now</a></li>
|
||||
<li class="separator"></li>
|
||||
<li id="context_move"><a href="#">Move</a>
|
||||
<ul id='context_move_ul'>
|
||||
<li id="context_move_top"><a href="#">Move to Top</a></li>
|
||||
<li id="context_move_up"><a href="#">Move Up</a></li>
|
||||
<li id="context_move_down"><a href="#">Move Down</a></li>
|
||||
<li id="context_move_bottom"><a href="#">Move to Bottom</a></li>
|
||||
</ul></li>
|
||||
<li class="separator"></li>
|
||||
<li id="context_remove"><a href="#">Remove From List...</a></li>
|
||||
<li id="context_removedata"><a href="#">Delete Data & Remove From List...</a></li>
|
||||
<li class="separator"></li>
|
||||
<li id="context_verify"><a href="#">Verify Local Data</a></li>
|
||||
<li id="context_move"><a href="#">Set Location...</a></li>
|
||||
<li class="separator"></li>
|
||||
<li id="context_reannounce"><a href="#">Ask tracker for more peers</a></li>
|
||||
<li class="separator"></li>
|
||||
<li id="context_select_all"><a href="#">Select All</a></li>
|
||||
<li id="context_deselect_all"><a href="#">Deselect All</a></li>
|
||||
</ul>
|
||||
|
||||
<iframe name="torrent_upload_frame" id="torrent_upload_frame" src="about:blank" ></iframe>
|
||||
</body>
|
||||
</html>
|
358
src/jsp/web/javascript/common.js
Normal file
@ -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<len; i++)
|
||||
if (this[i]==obj)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// hasParent implementation from http://stackoverflow.com/a/2389549
|
||||
jQuery.extend( jQuery.fn, {
|
||||
// Name of our method & one argument (the parent selector)
|
||||
hasParent: function( p ) {
|
||||
// Returns a subset of items using jQuery.filter
|
||||
return this.filter(function () {
|
||||
// Return truthy/falsey based on presence in parent
|
||||
return $(p).find(this).length;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
//http://forum.jquery.com/topic/combining-ui-dialog-and-tabs
|
||||
$.fn.tabbedDialog = function (dialog_opts) {
|
||||
this.tabs({selected: 0});
|
||||
this.dialog(dialog_opts);
|
||||
this.find('.ui-tab-dialog-close').append(this.parent().find('.ui-dialog-titlebar-close'));
|
||||
this.find('.ui-tab-dialog-close').css({'position':'absolute','right':'0', 'top':'16px'});
|
||||
this.find('.ui-tab-dialog-close > 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, "<").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<len; ++i) {
|
||||
var line = lines[i].trim();
|
||||
var delim = line.indexOf('=');
|
||||
if ((delim === key.length) && line.indexOf(key) === 0) {
|
||||
val = line.substring(delim + 1);
|
||||
if (val.lastIndexOf("[", 0) === 0 && val.endsWith("]")) {
|
||||
val = JSON.parse(val)[0];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: we support strings and booleans... add number support too?
|
||||
if (typeof val === "undefined") val = fallback;
|
||||
else if (val === 'true') val = true;
|
||||
else if (val === 'false') val = false;
|
||||
return val;
|
||||
};
|
||||
|
||||
if (typeof String.prototype.endsWith !== 'function') {
|
||||
String.prototype.endsWith = function(suffix) {
|
||||
return this.indexOf(suffix, this.length - suffix.length) !== -1;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object with all the Clutch preferences set
|
||||
*
|
||||
* @pararm o object to be populated (optional)
|
||||
*/
|
||||
Prefs.getClutchPrefs = function(o)
|
||||
{
|
||||
if (!o)
|
||||
o = { };
|
||||
for (var key in Prefs._Defaults)
|
||||
o[key] = Prefs.getValue(key, Prefs._Defaults[key]);
|
||||
return o;
|
||||
};
|
||||
|
||||
|
||||
// forceNumeric() plug-in implementation
|
||||
jQuery.fn.forceNumeric = function () {
|
||||
return this.each(function () {
|
||||
$(this).keydown(function (e) {
|
||||
var key = e.which || e.keyCode;
|
||||
return !e.shiftKey && !e.altKey && !e.ctrlKey &&
|
||||
// numbers
|
||||
key >= 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 <stevenlevithan.com>
|
||||
* 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*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
|
||||
}
|
||||
};
|
126
src/jsp/web/javascript/dialog.js
Normal file
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
24
src/jsp/web/javascript/easyXDM-2.4.18.4.min.js
vendored
Normal file
BIN
src/jsp/web/javascript/easyxdm-2.4.18.4.swf
Normal file
213
src/jsp/web/javascript/file-row.js
Normal file
@ -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.indices.length; ++i) {
|
||||
file = fields.torrent.getFile (fields.indices[i]);
|
||||
have += file.bytesCompleted;
|
||||
size += file.length;
|
||||
wanted |= file.wanted;
|
||||
switch (file.priority) {
|
||||
case -1: low = true; break;
|
||||
case 0: normal = true; break;
|
||||
case 1: high = true; break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((fields.have != have) || (fields.size != size)) {
|
||||
fields.have = have;
|
||||
fields.size = size;
|
||||
refreshProgressHTML();
|
||||
}
|
||||
|
||||
if (fields.isWanted !== wanted) {
|
||||
fields.isWanted = wanted;
|
||||
refreshWantedHTML();
|
||||
}
|
||||
|
||||
if (fields.priorityLow !== low) {
|
||||
fields.priorityLow = low;
|
||||
$(elements.priority_low_button).toggleClass('selected', low);
|
||||
}
|
||||
|
||||
if (fields.priorityNormal !== normal) {
|
||||
fields.priorityNormal = normal;
|
||||
$(elements.priority_normal_button).toggleClass('selected', normal);
|
||||
}
|
||||
|
||||
if (fields.priorityHigh !== high) {
|
||||
fields.priorityHigh = high;
|
||||
$(elements.priority_high_button).toggleClass('selected', high);
|
||||
}
|
||||
},
|
||||
|
||||
isDone = function () {
|
||||
return fields.have >= 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);
|
||||
};
|
428
src/jsp/web/javascript/formatter.js
Normal file
@ -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("<span title=\"" + flag + ': ' + explanation + "\">" + flag + "</span>");
|
||||
}
|
||||
}
|
||||
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 );
|
||||
};
|
||||
|
||||
|
883
src/jsp/web/javascript/inspector.js
Normal file
@ -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<n; ++i) {
|
||||
name = tor.getFile(i).name;
|
||||
tokens = name.split('/');
|
||||
walk = tree;
|
||||
for (j=0; j<tokens.length; ++j) {
|
||||
token = tokens[j];
|
||||
sub = walk.children[token];
|
||||
if (!sub) {
|
||||
walk.children[token] = sub = {
|
||||
name: token,
|
||||
parent: walk,
|
||||
children: { },
|
||||
file_indices: [ ],
|
||||
depth: j
|
||||
};
|
||||
}
|
||||
walk = sub;
|
||||
}
|
||||
walk.file_index = i;
|
||||
delete walk.children;
|
||||
leaves.push (walk);
|
||||
}
|
||||
|
||||
for (i=0; i<leaves.length; ++i) {
|
||||
walk = leaves[i];
|
||||
j = walk.file_index;
|
||||
do {
|
||||
walk.file_indices.push (j);
|
||||
walk = walk.parent;
|
||||
} while (walk);
|
||||
}
|
||||
|
||||
return tree;
|
||||
},
|
||||
|
||||
addNodeToView = function (tor, parent, sub, i) {
|
||||
var row;
|
||||
row = new FileRow(tor, sub.depth, sub.name, sub.file_indices, i%2);
|
||||
data.file_rows.push(row);
|
||||
parent.appendChild(row.getElement());
|
||||
$(row).bind('wantedToggled',onFileWantedToggled);
|
||||
$(row).bind('priorityToggled',onFilePriorityToggled);
|
||||
$(row).bind('nameClicked',onNameClicked);
|
||||
},
|
||||
|
||||
addSubtreeToView = function (tor, parent, sub, i) {
|
||||
var key, div;
|
||||
div = document.createElement('div');
|
||||
if (sub.parent)
|
||||
addNodeToView (tor, div, sub, i++);
|
||||
if (sub.children)
|
||||
for (key in sub.children)
|
||||
i = addSubtreeToView (tor, div, sub.children[key]);
|
||||
parent.appendChild(div);
|
||||
return i;
|
||||
},
|
||||
|
||||
updateFilesPage = function() {
|
||||
var i, n, tor, fragment, tree,
|
||||
file_list = data.elements.file_list,
|
||||
torrents = data.torrents;
|
||||
|
||||
// only show one torrent at a time
|
||||
if (torrents.length !== 1) {
|
||||
clearFileList();
|
||||
return;
|
||||
}
|
||||
|
||||
tor = torrents[0];
|
||||
n = tor ? tor.getFileCount() : 0;
|
||||
if (tor!=data.file_torrent || n!=data.file_torrent_n) {
|
||||
// rebuild the file list...
|
||||
clearFileList();
|
||||
data.file_torrent = tor;
|
||||
data.file_torrent_n = n;
|
||||
data.file_rows = [ ];
|
||||
fragment = document.createDocumentFragment();
|
||||
tree = createFileTreeModel (tor);
|
||||
addSubtreeToView (tor, fragment, tree, 0);
|
||||
file_list.appendChild (fragment);
|
||||
} else {
|
||||
// ...refresh the already-existing file list
|
||||
for (i=0, n=data.file_rows.length; i<n; ++i)
|
||||
data.file_rows[i].refresh();
|
||||
}
|
||||
},
|
||||
|
||||
/****
|
||||
***** PEERS PAGE
|
||||
****/
|
||||
|
||||
updatePeersPage = function() {
|
||||
var i, k, tor, peers, peer, parity,
|
||||
html = [],
|
||||
fmt = Transmission.fmt,
|
||||
peers_list = data.elements.peers_list,
|
||||
torrents = data.torrents;
|
||||
|
||||
for (k=0; tor=torrents[k]; ++k)
|
||||
{
|
||||
peers = tor.getPeers();
|
||||
html.push('<div class="inspector_group">');
|
||||
if (torrents.length > 1) {
|
||||
html.push('<div class="inspector_torrent_label">', sanitizeText(tor.getName()), '</div>');
|
||||
}
|
||||
if (!peers || !peers.length) {
|
||||
html.push('<br></div>'); // firefox won't paint the top border if the div is empty
|
||||
continue;
|
||||
}
|
||||
html.push('<table class="peer_list">',
|
||||
'<tr class="inspector_peer_entry even">',
|
||||
'<th class="encryptedCol"></th>',
|
||||
'<th class="upCol">Up</th>',
|
||||
'<th class="downCol">Down</th>',
|
||||
'<th class="percentCol">%</th>',
|
||||
'<th class="statusCol">Status</th>',
|
||||
'<th class="addressCol">Address</th>',
|
||||
'<th class="clientCol">Client</th>',
|
||||
'</tr>');
|
||||
for (i=0; peer=peers[i]; ++i) {
|
||||
parity = (i%2) ? 'odd' : 'even';
|
||||
html.push('<tr class="inspector_peer_entry ', parity, '">',
|
||||
'<td>', (peer.isEncrypted ? '<div class="encrypted-peer-cell" title="Encrypted Connection">'
|
||||
: '<div class="unencrypted-peer-cell">'), '</div>', '</td>',
|
||||
'<td>', (peer.rateToPeer ? fmt.speedBps(peer.rateToPeer) : ''), '</td>',
|
||||
'<td>', (peer.rateToClient ? fmt.speedBps(peer.rateToClient) : ''), '</td>',
|
||||
'<td class="percentCol">', Math.floor(peer.progress*100), '%', '</td>',
|
||||
'<td>', fmt.peerStatus(peer.flagStr), '</td>',
|
||||
'<td>', sanitizeText(peer.address), '</td>',
|
||||
'<td class="clientCol">', sanitizeText(peer.clientName), '</td>',
|
||||
'</tr>');
|
||||
}
|
||||
html.push('</table></div>');
|
||||
}
|
||||
|
||||
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 ('<div class="inspector_group">');
|
||||
|
||||
if (torrents.length > 1)
|
||||
html.push('<div class="inspector_torrent_label">', tor.getName(), '</div>');
|
||||
|
||||
tier = -1;
|
||||
trackers = tor.getTrackers();
|
||||
for (j=0; tracker=trackers[j]; ++j)
|
||||
{
|
||||
if (tier != tracker.tier)
|
||||
{
|
||||
if (tier !== -1) // close previous tier
|
||||
html.push('</ul></div>');
|
||||
|
||||
tier = tracker.tier;
|
||||
|
||||
html.push('<div class="inspector_group_label">',
|
||||
'Tier ', tier+1, '</div>',
|
||||
'<ul class="tier_list">');
|
||||
}
|
||||
|
||||
// Display construction
|
||||
lastAnnounceStatusHash = lastAnnounceStatus(tracker);
|
||||
announceState = getAnnounceState(tracker);
|
||||
lastScrapeStatusHash = lastScrapeStatus(tracker);
|
||||
parity = (j%2) ? 'odd' : 'even';
|
||||
html.push('<li class="inspector_tracker_entry ', parity, '"><div class="tracker_host" title="', sanitizeText(tracker.announce), '">',
|
||||
sanitizeText(tracker.host || tracker.announce), '</div>',
|
||||
'<div class="tracker_activity">',
|
||||
'<div>', lastAnnounceStatusHash['label'], ': ', lastAnnounceStatusHash['value'], '</div>',
|
||||
'<div>', announceState, '</div>',
|
||||
'<div>', lastScrapeStatusHash['label'], ': ', lastScrapeStatusHash['value'], '</div>',
|
||||
'</div><table class="tracker_stats">',
|
||||
'<tr><th>Seeders:</th><td>', (tracker.seederCount > -1 ? tracker.seederCount : na), '</td></tr>',
|
||||
'<tr><th>Leechers:</th><td>', (tracker.leecherCount > -1 ? tracker.leecherCount : na), '</td></tr>',
|
||||
'<tr><th>Downloads:</th><td>', (tracker.downloadCount > -1 ? tracker.downloadCount : na), '</td></tr>',
|
||||
'</table></li>');
|
||||
}
|
||||
if (tier !== -1) // close last tier
|
||||
html.push('</ul></div>');
|
||||
|
||||
html.push('</div>'); // 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);
|
||||
};
|
6
src/jsp/web/javascript/jquery/jquery-1.10.2.min.js
vendored
Normal file
6
src/jsp/web/javascript/jquery/jquery-ui-1.10.3.custom.min.js
vendored
Normal file
944
src/jsp/web/javascript/jquery/jquery.mobile.custom.js
Normal file
@ -0,0 +1,944 @@
|
||||
/*
|
||||
* jQuery Mobile v1.3.2
|
||||
* http://jquerymobile.com
|
||||
*
|
||||
* Copyright 2010, 2013 jQuery Foundation, Inc. and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* Vuze Custom via http://jquerymobile.com/download-builder/
|
||||
* : Virtual Mouse (vmouse) Bindings, Orientation Change, Touch
|
||||
* : Added originalEvent to taphold event
|
||||
* : Added tapFired to prevent tap on taphold from http://stackoverflow.com/a/11941654
|
||||
*/
|
||||
|
||||
(function ( root, doc, factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define( [ "jquery" ], function ( $ ) {
|
||||
factory( $, root, doc );
|
||||
return $.mobile;
|
||||
});
|
||||
} else {
|
||||
// Browser globals
|
||||
factory( root.jQuery, root, doc );
|
||||
}
|
||||
}( this, document, function ( jQuery, window, document, undefined ) {
|
||||
(function( $, undefined ) {
|
||||
$.extend( $.support, {
|
||||
orientation: "orientation" in window && "onorientationchange" in window
|
||||
});
|
||||
}( jQuery ));
|
||||
|
||||
|
||||
// throttled resize event
|
||||
(function( $ ) {
|
||||
$.event.special.throttledresize = {
|
||||
setup: function() {
|
||||
$( this ).bind( "resize", handler );
|
||||
},
|
||||
teardown: function() {
|
||||
$( this ).unbind( "resize", handler );
|
||||
}
|
||||
};
|
||||
|
||||
var throttle = 250,
|
||||
handler = function() {
|
||||
curr = ( new Date() ).getTime();
|
||||
diff = curr - lastCall;
|
||||
|
||||
if ( diff >= throttle ) {
|
||||
|
||||
lastCall = curr;
|
||||
$( this ).trigger( "throttledresize" );
|
||||
|
||||
} else {
|
||||
|
||||
if ( heldCall ) {
|
||||
clearTimeout( heldCall );
|
||||
}
|
||||
|
||||
// Promise a held call will still execute
|
||||
heldCall = setTimeout( handler, throttle - diff );
|
||||
}
|
||||
},
|
||||
lastCall = 0,
|
||||
heldCall,
|
||||
curr,
|
||||
diff;
|
||||
})( jQuery );
|
||||
|
||||
(function( $, window ) {
|
||||
var win = $( window ),
|
||||
event_name = "orientationchange",
|
||||
special_event,
|
||||
get_orientation,
|
||||
last_orientation,
|
||||
initial_orientation_is_landscape,
|
||||
initial_orientation_is_default,
|
||||
portrait_map = { "0": true, "180": true };
|
||||
|
||||
// It seems that some device/browser vendors use window.orientation values 0 and 180 to
|
||||
// denote the "default" orientation. For iOS devices, and most other smart-phones tested,
|
||||
// the default orientation is always "portrait", but in some Android and RIM based tablets,
|
||||
// the default orientation is "landscape". The following code attempts to use the window
|
||||
// dimensions to figure out what the current orientation is, and then makes adjustments
|
||||
// to the to the portrait_map if necessary, so that we can properly decode the
|
||||
// window.orientation value whenever get_orientation() is called.
|
||||
//
|
||||
// Note that we used to use a media query to figure out what the orientation the browser
|
||||
// thinks it is in:
|
||||
//
|
||||
// initial_orientation_is_landscape = $.mobile.media("all and (orientation: landscape)");
|
||||
//
|
||||
// but there was an iPhone/iPod Touch bug beginning with iOS 4.2, up through iOS 5.1,
|
||||
// where the browser *ALWAYS* applied the landscape media query. This bug does not
|
||||
// happen on iPad.
|
||||
|
||||
if ( $.support.orientation ) {
|
||||
|
||||
// Check the window width and height to figure out what the current orientation
|
||||
// of the device is at this moment. Note that we've initialized the portrait map
|
||||
// values to 0 and 180, *AND* we purposely check for landscape so that if we guess
|
||||
// wrong, , we default to the assumption that portrait is the default orientation.
|
||||
// We use a threshold check below because on some platforms like iOS, the iPhone
|
||||
// form-factor can report a larger width than height if the user turns on the
|
||||
// developer console. The actual threshold value is somewhat arbitrary, we just
|
||||
// need to make sure it is large enough to exclude the developer console case.
|
||||
|
||||
var ww = window.innerWidth || win.width(),
|
||||
wh = window.innerHeight || win.height(),
|
||||
landscape_threshold = 50;
|
||||
|
||||
initial_orientation_is_landscape = ww > wh && ( ww - wh ) > landscape_threshold;
|
||||
|
||||
|
||||
// Now check to see if the current window.orientation is 0 or 180.
|
||||
initial_orientation_is_default = portrait_map[ window.orientation ];
|
||||
|
||||
// If the initial orientation is landscape, but window.orientation reports 0 or 180, *OR*
|
||||
// if the initial orientation is portrait, but window.orientation reports 90 or -90, we
|
||||
// need to flip our portrait_map values because landscape is the default orientation for
|
||||
// this device/browser.
|
||||
if ( ( initial_orientation_is_landscape && initial_orientation_is_default ) || ( !initial_orientation_is_landscape && !initial_orientation_is_default ) ) {
|
||||
portrait_map = { "-90": true, "90": true };
|
||||
}
|
||||
}
|
||||
|
||||
$.event.special.orientationchange = $.extend( {}, $.event.special.orientationchange, {
|
||||
setup: function() {
|
||||
// If the event is supported natively, return false so that jQuery
|
||||
// will bind to the event using DOM methods.
|
||||
if ( $.support.orientation && !$.event.special.orientationchange.disabled ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the current orientation to avoid initial double-triggering.
|
||||
last_orientation = get_orientation();
|
||||
|
||||
// Because the orientationchange event doesn't exist, simulate the
|
||||
// event by testing window dimensions on resize.
|
||||
win.bind( "throttledresize", handler );
|
||||
},
|
||||
teardown: function() {
|
||||
// If the event is not supported natively, return false so that
|
||||
// jQuery will unbind the event using DOM methods.
|
||||
if ( $.support.orientation && !$.event.special.orientationchange.disabled ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Because the orientationchange event doesn't exist, unbind the
|
||||
// resize event handler.
|
||||
win.unbind( "throttledresize", handler );
|
||||
},
|
||||
add: function( handleObj ) {
|
||||
// Save a reference to the bound event handler.
|
||||
var old_handler = handleObj.handler;
|
||||
|
||||
|
||||
handleObj.handler = function( event ) {
|
||||
// Modify event object, adding the .orientation property.
|
||||
event.orientation = get_orientation();
|
||||
|
||||
// Call the originally-bound event handler and return its result.
|
||||
return old_handler.apply( this, arguments );
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// If the event is not supported natively, this handler will be bound to
|
||||
// the window resize event to simulate the orientationchange event.
|
||||
function handler() {
|
||||
// Get the current orientation.
|
||||
var orientation = get_orientation();
|
||||
|
||||
if ( orientation !== last_orientation ) {
|
||||
// The orientation has changed, so trigger the orientationchange event.
|
||||
last_orientation = orientation;
|
||||
win.trigger( event_name );
|
||||
}
|
||||
}
|
||||
|
||||
// Get the current page orientation. This method is exposed publicly, should it
|
||||
// be needed, as jQuery.event.special.orientationchange.orientation()
|
||||
$.event.special.orientationchange.orientation = get_orientation = function() {
|
||||
var isPortrait = true, elem = document.documentElement;
|
||||
|
||||
// prefer window orientation to the calculation based on screensize as
|
||||
// the actual screen resize takes place before or after the orientation change event
|
||||
// has been fired depending on implementation (eg android 2.3 is before, iphone after).
|
||||
// More testing is required to determine if a more reliable method of determining the new screensize
|
||||
// is possible when orientationchange is fired. (eg, use media queries + element + opacity)
|
||||
if ( $.support.orientation ) {
|
||||
// if the window orientation registers as 0 or 180 degrees report
|
||||
// portrait, otherwise landscape
|
||||
isPortrait = portrait_map[ window.orientation ];
|
||||
} else {
|
||||
isPortrait = elem && elem.clientWidth / elem.clientHeight < 1.1;
|
||||
}
|
||||
|
||||
return isPortrait ? "portrait" : "landscape";
|
||||
};
|
||||
|
||||
$.fn[ event_name ] = function( fn ) {
|
||||
return fn ? this.bind( event_name, fn ) : this.trigger( event_name );
|
||||
};
|
||||
|
||||
// jQuery < 1.8
|
||||
if ( $.attrFn ) {
|
||||
$.attrFn[ event_name ] = true;
|
||||
}
|
||||
|
||||
}( jQuery, this ));
|
||||
|
||||
|
||||
// This plugin is an experiment for abstracting away the touch and mouse
|
||||
// events so that developers don't have to worry about which method of input
|
||||
// the device their document is loaded on supports.
|
||||
//
|
||||
// The idea here is to allow the developer to register listeners for the
|
||||
// basic mouse events, such as mousedown, mousemove, mouseup, and click,
|
||||
// and the plugin will take care of registering the correct listeners
|
||||
// behind the scenes to invoke the listener at the fastest possible time
|
||||
// for that device, while still retaining the order of event firing in
|
||||
// the traditional mouse environment, should multiple handlers be registered
|
||||
// on the same element for different events.
|
||||
//
|
||||
// The current version exposes the following virtual events to jQuery bind methods:
|
||||
// "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel"
|
||||
|
||||
(function( $, window, document, undefined ) {
|
||||
|
||||
var dataPropertyName = "virtualMouseBindings",
|
||||
touchTargetPropertyName = "virtualTouchID",
|
||||
virtualEventNames = "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split( " " ),
|
||||
touchEventProps = "clientX clientY pageX pageY screenX screenY".split( " " ),
|
||||
mouseHookProps = $.event.mouseHooks ? $.event.mouseHooks.props : [],
|
||||
mouseEventProps = $.event.props.concat( mouseHookProps ),
|
||||
activeDocHandlers = {},
|
||||
resetTimerID = 0,
|
||||
startX = 0,
|
||||
startY = 0,
|
||||
didScroll = false,
|
||||
clickBlockList = [],
|
||||
blockMouseTriggers = false,
|
||||
blockTouchTriggers = false,
|
||||
eventCaptureSupported = "addEventListener" in document,
|
||||
$document = $( document ),
|
||||
nextTouchID = 1,
|
||||
lastTouchID = 0, threshold;
|
||||
|
||||
$.vmouse = {
|
||||
moveDistanceThreshold: 10,
|
||||
clickDistanceThreshold: 10,
|
||||
resetTimerDuration: 1500
|
||||
};
|
||||
|
||||
function getNativeEvent( event ) {
|
||||
|
||||
while ( event && typeof event.originalEvent !== "undefined" ) {
|
||||
event = event.originalEvent;
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
function createVirtualEvent( event, eventType ) {
|
||||
|
||||
var t = event.type,
|
||||
oe, props, ne, prop, ct, touch, i, j, len;
|
||||
|
||||
event = $.Event( event );
|
||||
event.type = eventType;
|
||||
|
||||
oe = event.originalEvent;
|
||||
props = $.event.props;
|
||||
|
||||
// addresses separation of $.event.props in to $.event.mouseHook.props and Issue 3280
|
||||
// https://github.com/jquery/jquery-mobile/issues/3280
|
||||
if ( t.search( /^(mouse|click)/ ) > -1 ) {
|
||||
props = mouseEventProps;
|
||||
}
|
||||
|
||||
// copy original event properties over to the new event
|
||||
// this would happen if we could call $.event.fix instead of $.Event
|
||||
// but we don't have a way to force an event to be fixed multiple times
|
||||
if ( oe ) {
|
||||
for ( i = props.length, prop; i; ) {
|
||||
prop = props[ --i ];
|
||||
event[ prop ] = oe[ prop ];
|
||||
}
|
||||
}
|
||||
|
||||
// make sure that if the mouse and click virtual events are generated
|
||||
// without a .which one is defined
|
||||
if ( t.search(/mouse(down|up)|click/) > -1 && !event.which ) {
|
||||
event.which = 1;
|
||||
}
|
||||
|
||||
if ( t.search(/^touch/) !== -1 ) {
|
||||
ne = getNativeEvent( oe );
|
||||
t = ne.touches;
|
||||
ct = ne.changedTouches;
|
||||
touch = ( t && t.length ) ? t[0] : ( ( ct && ct.length ) ? ct[ 0 ] : undefined );
|
||||
|
||||
if ( touch ) {
|
||||
for ( j = 0, len = touchEventProps.length; j < len; j++) {
|
||||
prop = touchEventProps[ j ];
|
||||
event[ prop ] = touch[ prop ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
function getVirtualBindingFlags( element ) {
|
||||
|
||||
var flags = {},
|
||||
b, k;
|
||||
|
||||
while ( element ) {
|
||||
|
||||
b = $.data( element, dataPropertyName );
|
||||
|
||||
for ( k in b ) {
|
||||
if ( b[ k ] ) {
|
||||
flags[ k ] = flags.hasVirtualBinding = true;
|
||||
}
|
||||
}
|
||||
element = element.parentNode;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
function getClosestElementWithVirtualBinding( element, eventType ) {
|
||||
var b;
|
||||
while ( element ) {
|
||||
|
||||
b = $.data( element, dataPropertyName );
|
||||
|
||||
if ( b && ( !eventType || b[ eventType ] ) ) {
|
||||
return element;
|
||||
}
|
||||
element = element.parentNode;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function enableTouchBindings() {
|
||||
blockTouchTriggers = false;
|
||||
}
|
||||
|
||||
function disableTouchBindings() {
|
||||
blockTouchTriggers = true;
|
||||
}
|
||||
|
||||
function enableMouseBindings() {
|
||||
lastTouchID = 0;
|
||||
clickBlockList.length = 0;
|
||||
blockMouseTriggers = false;
|
||||
|
||||
// When mouse bindings are enabled, our
|
||||
// touch bindings are disabled.
|
||||
disableTouchBindings();
|
||||
}
|
||||
|
||||
function disableMouseBindings() {
|
||||
// When mouse bindings are disabled, our
|
||||
// touch bindings are enabled.
|
||||
enableTouchBindings();
|
||||
}
|
||||
|
||||
function startResetTimer() {
|
||||
clearResetTimer();
|
||||
resetTimerID = setTimeout( function() {
|
||||
resetTimerID = 0;
|
||||
enableMouseBindings();
|
||||
}, $.vmouse.resetTimerDuration );
|
||||
}
|
||||
|
||||
function clearResetTimer() {
|
||||
if ( resetTimerID ) {
|
||||
clearTimeout( resetTimerID );
|
||||
resetTimerID = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function triggerVirtualEvent( eventType, event, flags ) {
|
||||
var ve;
|
||||
|
||||
if ( ( flags && flags[ eventType ] ) ||
|
||||
( !flags && getClosestElementWithVirtualBinding( event.target, eventType ) ) ) {
|
||||
|
||||
ve = createVirtualEvent( event, eventType );
|
||||
|
||||
$( event.target).trigger( ve );
|
||||
}
|
||||
|
||||
return ve;
|
||||
}
|
||||
|
||||
function mouseEventCallback( event ) {
|
||||
var touchID = $.data( event.target, touchTargetPropertyName );
|
||||
|
||||
if ( !blockMouseTriggers && ( !lastTouchID || lastTouchID !== touchID ) ) {
|
||||
var ve = triggerVirtualEvent( "v" + event.type, event );
|
||||
if ( ve ) {
|
||||
if ( ve.isDefaultPrevented() ) {
|
||||
event.preventDefault();
|
||||
}
|
||||
if ( ve.isPropagationStopped() ) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
if ( ve.isImmediatePropagationStopped() ) {
|
||||
event.stopImmediatePropagation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleTouchStart( event ) {
|
||||
|
||||
var touches = getNativeEvent( event ).touches,
|
||||
target, flags;
|
||||
|
||||
if ( touches && touches.length === 1 ) {
|
||||
|
||||
target = event.target;
|
||||
flags = getVirtualBindingFlags( target );
|
||||
|
||||
if ( flags.hasVirtualBinding ) {
|
||||
|
||||
lastTouchID = nextTouchID++;
|
||||
$.data( target, touchTargetPropertyName, lastTouchID );
|
||||
|
||||
clearResetTimer();
|
||||
|
||||
disableMouseBindings();
|
||||
didScroll = false;
|
||||
|
||||
var t = getNativeEvent( event ).touches[ 0 ];
|
||||
startX = t.pageX;
|
||||
startY = t.pageY;
|
||||
|
||||
triggerVirtualEvent( "vmouseover", event, flags );
|
||||
triggerVirtualEvent( "vmousedown", event, flags );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleScroll( event ) {
|
||||
if ( blockTouchTriggers ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !didScroll ) {
|
||||
triggerVirtualEvent( "vmousecancel", event, getVirtualBindingFlags( event.target ) );
|
||||
}
|
||||
|
||||
didScroll = true;
|
||||
startResetTimer();
|
||||
}
|
||||
|
||||
function handleTouchMove( event ) {
|
||||
if ( blockTouchTriggers ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var t = getNativeEvent( event ).touches[ 0 ],
|
||||
didCancel = didScroll,
|
||||
moveThreshold = $.vmouse.moveDistanceThreshold,
|
||||
flags = getVirtualBindingFlags( event.target );
|
||||
|
||||
didScroll = didScroll ||
|
||||
( Math.abs( t.pageX - startX ) > moveThreshold ||
|
||||
Math.abs( t.pageY - startY ) > moveThreshold );
|
||||
|
||||
|
||||
if ( didScroll && !didCancel ) {
|
||||
triggerVirtualEvent( "vmousecancel", event, flags );
|
||||
}
|
||||
|
||||
triggerVirtualEvent( "vmousemove", event, flags );
|
||||
startResetTimer();
|
||||
}
|
||||
|
||||
function handleTouchEnd( event ) {
|
||||
if ( blockTouchTriggers ) {
|
||||
return;
|
||||
}
|
||||
|
||||
disableTouchBindings();
|
||||
|
||||
var flags = getVirtualBindingFlags( event.target ),
|
||||
t;
|
||||
triggerVirtualEvent( "vmouseup", event, flags );
|
||||
|
||||
if ( !didScroll ) {
|
||||
var ve = triggerVirtualEvent( "vclick", event, flags );
|
||||
if ( ve && ve.isDefaultPrevented() ) {
|
||||
// The target of the mouse events that follow the touchend
|
||||
// event don't necessarily match the target used during the
|
||||
// touch. This means we need to rely on coordinates for blocking
|
||||
// any click that is generated.
|
||||
t = getNativeEvent( event ).changedTouches[ 0 ];
|
||||
clickBlockList.push({
|
||||
touchID: lastTouchID,
|
||||
x: t.clientX,
|
||||
y: t.clientY
|
||||
});
|
||||
|
||||
// Prevent any mouse events that follow from triggering
|
||||
// virtual event notifications.
|
||||
blockMouseTriggers = true;
|
||||
}
|
||||
}
|
||||
triggerVirtualEvent( "vmouseout", event, flags);
|
||||
didScroll = false;
|
||||
|
||||
startResetTimer();
|
||||
}
|
||||
|
||||
function hasVirtualBindings( ele ) {
|
||||
var bindings = $.data( ele, dataPropertyName ),
|
||||
k;
|
||||
|
||||
if ( bindings ) {
|
||||
for ( k in bindings ) {
|
||||
if ( bindings[ k ] ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function dummyMouseHandler() {}
|
||||
|
||||
function getSpecialEventObject( eventType ) {
|
||||
var realType = eventType.substr( 1 );
|
||||
|
||||
return {
|
||||
setup: function( data, namespace ) {
|
||||
// If this is the first virtual mouse binding for this element,
|
||||
// add a bindings object to its data.
|
||||
|
||||
if ( !hasVirtualBindings( this ) ) {
|
||||
$.data( this, dataPropertyName, {} );
|
||||
}
|
||||
|
||||
// If setup is called, we know it is the first binding for this
|
||||
// eventType, so initialize the count for the eventType to zero.
|
||||
var bindings = $.data( this, dataPropertyName );
|
||||
bindings[ eventType ] = true;
|
||||
|
||||
// If this is the first virtual mouse event for this type,
|
||||
// register a global handler on the document.
|
||||
|
||||
activeDocHandlers[ eventType ] = ( activeDocHandlers[ eventType ] || 0 ) + 1;
|
||||
|
||||
if ( activeDocHandlers[ eventType ] === 1 ) {
|
||||
$document.bind( realType, mouseEventCallback );
|
||||
}
|
||||
|
||||
// Some browsers, like Opera Mini, won't dispatch mouse/click events
|
||||
// for elements unless they actually have handlers registered on them.
|
||||
// To get around this, we register dummy handlers on the elements.
|
||||
|
||||
$( this ).bind( realType, dummyMouseHandler );
|
||||
|
||||
// For now, if event capture is not supported, we rely on mouse handlers.
|
||||
if ( eventCaptureSupported ) {
|
||||
// If this is the first virtual mouse binding for the document,
|
||||
// register our touchstart handler on the document.
|
||||
|
||||
activeDocHandlers[ "touchstart" ] = ( activeDocHandlers[ "touchstart" ] || 0) + 1;
|
||||
|
||||
if ( activeDocHandlers[ "touchstart" ] === 1 ) {
|
||||
$document.bind( "touchstart", handleTouchStart )
|
||||
.bind( "touchend", handleTouchEnd )
|
||||
|
||||
// On touch platforms, touching the screen and then dragging your finger
|
||||
// causes the window content to scroll after some distance threshold is
|
||||
// exceeded. On these platforms, a scroll prevents a click event from being
|
||||
// dispatched, and on some platforms, even the touchend is suppressed. To
|
||||
// mimic the suppression of the click event, we need to watch for a scroll
|
||||
// event. Unfortunately, some platforms like iOS don't dispatch scroll
|
||||
// events until *AFTER* the user lifts their finger (touchend). This means
|
||||
// we need to watch both scroll and touchmove events to figure out whether
|
||||
// or not a scroll happenens before the touchend event is fired.
|
||||
|
||||
.bind( "touchmove", handleTouchMove )
|
||||
.bind( "scroll", handleScroll );
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
teardown: function( data, namespace ) {
|
||||
// If this is the last virtual binding for this eventType,
|
||||
// remove its global handler from the document.
|
||||
|
||||
--activeDocHandlers[ eventType ];
|
||||
|
||||
if ( !activeDocHandlers[ eventType ] ) {
|
||||
$document.unbind( realType, mouseEventCallback );
|
||||
}
|
||||
|
||||
if ( eventCaptureSupported ) {
|
||||
// If this is the last virtual mouse binding in existence,
|
||||
// remove our document touchstart listener.
|
||||
|
||||
--activeDocHandlers[ "touchstart" ];
|
||||
|
||||
if ( !activeDocHandlers[ "touchstart" ] ) {
|
||||
$document.unbind( "touchstart", handleTouchStart )
|
||||
.unbind( "touchmove", handleTouchMove )
|
||||
.unbind( "touchend", handleTouchEnd )
|
||||
.unbind( "scroll", handleScroll );
|
||||
}
|
||||
}
|
||||
|
||||
var $this = $( this ),
|
||||
bindings = $.data( this, dataPropertyName );
|
||||
|
||||
// teardown may be called when an element was
|
||||
// removed from the DOM. If this is the case,
|
||||
// jQuery core may have already stripped the element
|
||||
// of any data bindings so we need to check it before
|
||||
// using it.
|
||||
if ( bindings ) {
|
||||
bindings[ eventType ] = false;
|
||||
}
|
||||
|
||||
// Unregister the dummy event handler.
|
||||
|
||||
$this.unbind( realType, dummyMouseHandler );
|
||||
|
||||
// If this is the last virtual mouse binding on the
|
||||
// element, remove the binding data from the element.
|
||||
|
||||
if ( !hasVirtualBindings( this ) ) {
|
||||
$this.removeData( dataPropertyName );
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Expose our custom events to the jQuery bind/unbind mechanism.
|
||||
|
||||
for ( var i = 0; i < virtualEventNames.length; i++ ) {
|
||||
$.event.special[ virtualEventNames[ i ] ] = getSpecialEventObject( virtualEventNames[ i ] );
|
||||
}
|
||||
|
||||
// Add a capture click handler to block clicks.
|
||||
// Note that we require event capture support for this so if the device
|
||||
// doesn't support it, we punt for now and rely solely on mouse events.
|
||||
if ( eventCaptureSupported ) {
|
||||
document.addEventListener( "click", function( e ) {
|
||||
var cnt = clickBlockList.length,
|
||||
target = e.target,
|
||||
x, y, ele, i, o, touchID;
|
||||
|
||||
if ( cnt ) {
|
||||
x = e.clientX;
|
||||
y = e.clientY;
|
||||
threshold = $.vmouse.clickDistanceThreshold;
|
||||
|
||||
// The idea here is to run through the clickBlockList to see if
|
||||
// the current click event is in the proximity of one of our
|
||||
// vclick events that had preventDefault() called on it. If we find
|
||||
// one, then we block the click.
|
||||
//
|
||||
// Why do we have to rely on proximity?
|
||||
//
|
||||
// Because the target of the touch event that triggered the vclick
|
||||
// can be different from the target of the click event synthesized
|
||||
// by the browser. The target of a mouse/click event that is syntehsized
|
||||
// from a touch event seems to be implementation specific. For example,
|
||||
// some browsers will fire mouse/click events for a link that is near
|
||||
// a touch event, even though the target of the touchstart/touchend event
|
||||
// says the user touched outside the link. Also, it seems that with most
|
||||
// browsers, the target of the mouse/click event is not calculated until the
|
||||
// time it is dispatched, so if you replace an element that you touched
|
||||
// with another element, the target of the mouse/click will be the new
|
||||
// element underneath that point.
|
||||
//
|
||||
// Aside from proximity, we also check to see if the target and any
|
||||
// of its ancestors were the ones that blocked a click. This is necessary
|
||||
// because of the strange mouse/click target calculation done in the
|
||||
// Android 2.1 browser, where if you click on an element, and there is a
|
||||
// mouse/click handler on one of its ancestors, the target will be the
|
||||
// innermost child of the touched element, even if that child is no where
|
||||
// near the point of touch.
|
||||
|
||||
ele = target;
|
||||
|
||||
while ( ele ) {
|
||||
for ( i = 0; i < cnt; i++ ) {
|
||||
o = clickBlockList[ i ];
|
||||
touchID = 0;
|
||||
|
||||
if ( ( ele === target && Math.abs( o.x - x ) < threshold && Math.abs( o.y - y ) < threshold ) ||
|
||||
$.data( ele, touchTargetPropertyName ) === o.touchID ) {
|
||||
// XXX: We may want to consider removing matches from the block list
|
||||
// instead of waiting for the reset timer to fire.
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return;
|
||||
}
|
||||
}
|
||||
ele = ele.parentNode;
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
})( jQuery, window, document );
|
||||
|
||||
(function( $ ) {
|
||||
$.mobile = {};
|
||||
}( jQuery ));
|
||||
(function( $, undefined ) {
|
||||
var support = {
|
||||
touch: "ontouchend" in document
|
||||
};
|
||||
|
||||
$.mobile.support = $.mobile.support || {};
|
||||
$.extend( $.support, support );
|
||||
$.extend( $.mobile.support, support );
|
||||
}( jQuery ));
|
||||
|
||||
|
||||
(function( $, window, undefined ) {
|
||||
var $document = $( document );
|
||||
|
||||
// add new event shortcuts
|
||||
$.each( ( "touchstart touchmove touchend " +
|
||||
"tap taphold " +
|
||||
"swipe swipeleft swiperight " +
|
||||
"scrollstart scrollstop" ).split( " " ), function( i, name ) {
|
||||
|
||||
$.fn[ name ] = function( fn ) {
|
||||
return fn ? this.bind( name, fn ) : this.trigger( name );
|
||||
};
|
||||
|
||||
// jQuery < 1.8
|
||||
if ( $.attrFn ) {
|
||||
$.attrFn[ name ] = true;
|
||||
}
|
||||
});
|
||||
|
||||
var supportTouch = $.mobile.support.touch,
|
||||
scrollEvent = "touchmove scroll",
|
||||
touchStartEvent = supportTouch ? "touchstart" : "mousedown",
|
||||
touchStopEvent = supportTouch ? "touchend" : "mouseup",
|
||||
touchMoveEvent = supportTouch ? "touchmove" : "mousemove";
|
||||
|
||||
function triggerCustomEvent( obj, eventType, event ) {
|
||||
var originalType = event.type;
|
||||
event.type = eventType;
|
||||
$.event.dispatch.call( obj, event );
|
||||
event.type = originalType;
|
||||
}
|
||||
|
||||
// also handles scrollstop
|
||||
$.event.special.scrollstart = {
|
||||
|
||||
enabled: true,
|
||||
|
||||
setup: function() {
|
||||
|
||||
var thisObject = this,
|
||||
$this = $( thisObject ),
|
||||
scrolling,
|
||||
timer;
|
||||
|
||||
function trigger( event, state ) {
|
||||
scrolling = state;
|
||||
triggerCustomEvent( thisObject, scrolling ? "scrollstart" : "scrollstop", event );
|
||||
}
|
||||
|
||||
// iPhone triggers scroll after a small delay; use touchmove instead
|
||||
$this.bind( scrollEvent, function( event ) {
|
||||
|
||||
if ( !$.event.special.scrollstart.enabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !scrolling ) {
|
||||
trigger( event, true );
|
||||
}
|
||||
|
||||
clearTimeout( timer );
|
||||
timer = setTimeout( function() {
|
||||
trigger( event, false );
|
||||
}, 50 );
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// also handles taphold
|
||||
$.event.special.tap = {
|
||||
tapholdThreshold: 750,
|
||||
|
||||
setup: function() {
|
||||
var thisObject = this,
|
||||
$this = $( thisObject );
|
||||
|
||||
$this.bind( "vmousedown", function( event ) {
|
||||
|
||||
if ( event.which && event.which !== 1 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var origTarget = event.target,
|
||||
origEvent = event.originalEvent,
|
||||
tapFired = false,
|
||||
timer;
|
||||
|
||||
function clearTapTimer() {
|
||||
clearTimeout( timer );
|
||||
}
|
||||
|
||||
function clearTapHandlers() {
|
||||
clearTapTimer();
|
||||
|
||||
$this.unbind( "vclick", clickHandler )
|
||||
.unbind( "vmouseup", clearTapTimer );
|
||||
$document.unbind( "vmousecancel", clearTapHandlers );
|
||||
}
|
||||
|
||||
function clickHandler( event ) {
|
||||
clearTapHandlers();
|
||||
|
||||
// ONLY trigger a 'tap' event if the start target is
|
||||
// the same as the stop target.
|
||||
//if ( origTarget === event.target ) {
|
||||
// Vuze: don't fire tap when taphold is pressed
|
||||
if ( origTarget === event.target && !tapFired) {
|
||||
triggerCustomEvent( thisObject, "tap", event );
|
||||
}
|
||||
}
|
||||
|
||||
$this.bind( "vmouseup", clearTapTimer )
|
||||
.bind( "vclick", clickHandler );
|
||||
$document.bind( "vmousecancel", clearTapHandlers );
|
||||
|
||||
timer = setTimeout( function() {
|
||||
// >> Vuze: Added originalEvent, so we can get pageY
|
||||
tapFired = true;
|
||||
triggerCustomEvent( thisObject, "taphold", $.Event( "taphold", { target: origTarget, originalEvent: event } ) );
|
||||
// << Vuze
|
||||
}, $.event.special.tap.tapholdThreshold );
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// also handles swipeleft, swiperight
|
||||
$.event.special.swipe = {
|
||||
scrollSupressionThreshold: 30, // More than this horizontal displacement, and we will suppress scrolling.
|
||||
|
||||
durationThreshold: 1000, // More time than this, and it isn't a swipe.
|
||||
|
||||
horizontalDistanceThreshold: 30, // Swipe horizontal displacement must be more than this.
|
||||
|
||||
verticalDistanceThreshold: 75, // Swipe vertical displacement must be less than this.
|
||||
|
||||
start: function( event ) {
|
||||
var data = event.originalEvent.touches ?
|
||||
event.originalEvent.touches[ 0 ] : event;
|
||||
return {
|
||||
time: ( new Date() ).getTime(),
|
||||
coords: [ data.pageX, data.pageY ],
|
||||
origin: $( event.target )
|
||||
};
|
||||
},
|
||||
|
||||
stop: function( event ) {
|
||||
var data = event.originalEvent.touches ?
|
||||
event.originalEvent.touches[ 0 ] : event;
|
||||
return {
|
||||
time: ( new Date() ).getTime(),
|
||||
coords: [ data.pageX, data.pageY ]
|
||||
};
|
||||
},
|
||||
|
||||
handleSwipe: function( start, stop ) {
|
||||
if ( stop.time - start.time < $.event.special.swipe.durationThreshold &&
|
||||
Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.horizontalDistanceThreshold &&
|
||||
Math.abs( start.coords[ 1 ] - stop.coords[ 1 ] ) < $.event.special.swipe.verticalDistanceThreshold ) {
|
||||
|
||||
start.origin.trigger( "swipe" )
|
||||
.trigger( start.coords[0] > stop.coords[ 0 ] ? "swipeleft" : "swiperight" );
|
||||
}
|
||||
},
|
||||
|
||||
setup: function() {
|
||||
var thisObject = this,
|
||||
$this = $( thisObject );
|
||||
|
||||
$this.bind( touchStartEvent, function( event ) {
|
||||
var start = $.event.special.swipe.start( event ),
|
||||
stop;
|
||||
|
||||
function moveHandler( event ) {
|
||||
if ( !start ) {
|
||||
return;
|
||||
}
|
||||
|
||||
stop = $.event.special.swipe.stop( event );
|
||||
|
||||
// prevent scrolling
|
||||
if ( Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.scrollSupressionThreshold ) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
$this.bind( touchMoveEvent, moveHandler )
|
||||
.one( touchStopEvent, function() {
|
||||
$this.unbind( touchMoveEvent, moveHandler );
|
||||
|
||||
if ( start && stop ) {
|
||||
$.event.special.swipe.handleSwipe( start, stop );
|
||||
}
|
||||
start = stop = undefined;
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
$.each({
|
||||
scrollstop: "scrollstart",
|
||||
taphold: "tap",
|
||||
swipeleft: "swipe",
|
||||
swiperight: "swipe"
|
||||
}, function( event, sourceEvent ) {
|
||||
|
||||
$.event.special[ event ] = {
|
||||
setup: function() {
|
||||
$( this ).bind( sourceEvent, $.noop );
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
})( jQuery, this );
|
||||
|
||||
}));
|
1
src/jsp/web/javascript/jquery/json2.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
var JSON;JSON||(JSON={}),function(){function f(a){return a<10?"0"+a:a}function quote(a){return escapable.lastIndex=0,escapable.test(a)?'"'+a.replace(escapable,function(a){var b=meta[a];return typeof b=="string"?b:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function str(a,b){var c,d,e,f,g=gap,h,i=b[a];i&&typeof i=="object"&&typeof i.toJSON=="function"&&(i=i.toJSON(a)),typeof rep=="function"&&(i=rep.call(b,a,i));switch(typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";gap+=indent,h=[];if(Object.prototype.toString.apply(i)==="[object Array]"){f=i.length;for(c=0;c<f;c+=1)h[c]=str(c,i)||"null";return e=h.length===0?"[]":gap?"[\n"+gap+h.join(",\n"+gap)+"\n"+g+"]":"["+h.join(",")+"]",gap=g,e}if(rep&&typeof rep=="object"){f=rep.length;for(c=0;c<f;c+=1)typeof rep[c]=="string"&&(d=rep[c],e=str(d,i),e&&h.push(quote(d)+(gap?": ":":")+e))}else for(d in i)Object.prototype.hasOwnProperty.call(i,d)&&(e=str(d,i),e&&h.push(quote(d)+(gap?": ":":")+e));return e=h.length===0?"{}":gap?"{\n"+gap+h.join(",\n"+gap)+"\n"+g+"}":"{"+h.join(",")+"}",gap=g,e}}"use strict",typeof Date.prototype.toJSON!="function"&&(Date.prototype.toJSON=function(a){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(a){return this.valueOf()});var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;typeof JSON.stringify!="function"&&(JSON.stringify=function(a,b,c){var d;gap="",indent="";if(typeof c=="number")for(d=0;d<c;d+=1)indent+=" ";else typeof c=="string"&&(indent=c);rep=b;if(!b||typeof b=="function"||typeof b=="object"&&typeof b.length=="number")return str("",{"":a});throw new Error("JSON.stringify")}),typeof JSON.parse!="function"&&(JSON.parse=function(text,reviver){function walk(a,b){var c,d,e=a[b];if(e&&typeof e=="object")for(c in e)Object.prototype.hasOwnProperty.call(e,c)&&(d=walk(e,c),d!==undefined?e[c]=d:delete e[c]);return reviver.call(a,b,e)}var j;text=String(text),cx.lastIndex=0,cx.test(text)&&(text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return j=eval("("+text+")"),typeof reviver=="function"?walk({"":j},""):j;throw new SyntaxError("JSON.parse")})}()
|
267
src/jsp/web/javascript/jquery/purl.js
vendored
Normal file
@ -0,0 +1,267 @@
|
||||
/*
|
||||
* Purl (A JavaScript URL parser) v2.3.1
|
||||
* Developed and maintanined by Mark Perkins, mark@allmarkedup.com
|
||||
* Source repository: https://github.com/allmarkedup/jQuery-URL-Parser
|
||||
* Licensed under an MIT-style license. See https://github.com/allmarkedup/jQuery-URL-Parser/blob/master/LICENSE for details.
|
||||
*/
|
||||
|
||||
;(function(factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(factory);
|
||||
} else {
|
||||
window.purl = factory();
|
||||
}
|
||||
})(function() {
|
||||
|
||||
var tag2attr = {
|
||||
a : 'href',
|
||||
img : 'src',
|
||||
form : 'action',
|
||||
base : 'href',
|
||||
script : 'src',
|
||||
iframe : 'src',
|
||||
link : 'href',
|
||||
embed : 'src',
|
||||
object : 'data'
|
||||
},
|
||||
|
||||
key = ['source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'fragment'], // keys available to query
|
||||
|
||||
aliases = { 'anchor' : 'fragment' }, // aliases for backwards compatability
|
||||
|
||||
parser = {
|
||||
strict : /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/, //less intuitive, more accurate to the specs
|
||||
loose : /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ // more intuitive, fails on relative paths and deviates from specs
|
||||
},
|
||||
|
||||
isint = /^[0-9]+$/;
|
||||
|
||||
function parseUri( url, strictMode ) {
|
||||
var str = decodeURI( url ),
|
||||
res = parser[ strictMode || false ? 'strict' : 'loose' ].exec( str ),
|
||||
uri = { attr : {}, param : {}, seg : {} },
|
||||
i = 14;
|
||||
|
||||
while ( i-- ) {
|
||||
uri.attr[ key[i] ] = res[i] || '';
|
||||
}
|
||||
|
||||
// build query and fragment parameters
|
||||
uri.param['query'] = parseString(uri.attr['query']);
|
||||
uri.param['fragment'] = parseString(uri.attr['fragment']);
|
||||
|
||||
// split path and fragement into segments
|
||||
uri.seg['path'] = uri.attr.path.replace(/^\/+|\/+$/g,'').split('/');
|
||||
uri.seg['fragment'] = uri.attr.fragment.replace(/^\/+|\/+$/g,'').split('/');
|
||||
|
||||
// compile a 'base' domain attribute
|
||||
uri.attr['base'] = uri.attr.host ? (uri.attr.protocol ? uri.attr.protocol+'://'+uri.attr.host : uri.attr.host) + (uri.attr.port ? ':'+uri.attr.port : '') : '';
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
function getAttrName( elm ) {
|
||||
var tn = elm.tagName;
|
||||
if ( typeof tn !== 'undefined' ) return tag2attr[tn.toLowerCase()];
|
||||
return tn;
|
||||
}
|
||||
|
||||
function promote(parent, key) {
|
||||
if (parent[key].length === 0) return parent[key] = {};
|
||||
var t = {};
|
||||
for (var i in parent[key]) t[i] = parent[key][i];
|
||||
parent[key] = t;
|
||||
return t;
|
||||
}
|
||||
|
||||
function parse(parts, parent, key, val) {
|
||||
var part = parts.shift();
|
||||
if (!part) {
|
||||
if (isArray(parent[key])) {
|
||||
parent[key].push(val);
|
||||
} else if ('object' == typeof parent[key]) {
|
||||
parent[key] = val;
|
||||
} else if ('undefined' == typeof parent[key]) {
|
||||
parent[key] = val;
|
||||
} else {
|
||||
parent[key] = [parent[key], val];
|
||||
}
|
||||
} else {
|
||||
var obj = parent[key] = parent[key] || [];
|
||||
if (']' == part) {
|
||||
if (isArray(obj)) {
|
||||
if ('' !== val) obj.push(val);
|
||||
} else if ('object' == typeof obj) {
|
||||
obj[keys(obj).length] = val;
|
||||
} else {
|
||||
obj = parent[key] = [parent[key], val];
|
||||
}
|
||||
} else if (~part.indexOf(']')) {
|
||||
part = part.substr(0, part.length - 1);
|
||||
if (!isint.test(part) && isArray(obj)) obj = promote(parent, key);
|
||||
parse(parts, obj, part, val);
|
||||
// key
|
||||
} else {
|
||||
if (!isint.test(part) && isArray(obj)) obj = promote(parent, key);
|
||||
parse(parts, obj, part, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function merge(parent, key, val) {
|
||||
if (~key.indexOf(']')) {
|
||||
var parts = key.split('[');
|
||||
parse(parts, parent, 'base', val);
|
||||
} else {
|
||||
if (!isint.test(key) && isArray(parent.base)) {
|
||||
var t = {};
|
||||
for (var k in parent.base) t[k] = parent.base[k];
|
||||
parent.base = t;
|
||||
}
|
||||
if (key !== '') {
|
||||
set(parent.base, key, val);
|
||||
}
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
function parseString(str) {
|
||||
return reduce(String(str).split(/&|;/), function(ret, pair) {
|
||||
try {
|
||||
pair = decodeURIComponent(pair.replace(/\+/g, ' '));
|
||||
} catch(e) {
|
||||
// ignore
|
||||
}
|
||||
var eql = pair.indexOf('='),
|
||||
brace = lastBraceInKey(pair),
|
||||
key = pair.substr(0, brace || eql),
|
||||
val = pair.substr(brace || eql, pair.length);
|
||||
|
||||
val = val.substr(val.indexOf('=') + 1, val.length);
|
||||
|
||||
if (key === '') {
|
||||
key = pair;
|
||||
val = '';
|
||||
}
|
||||
|
||||
return merge(ret, key, val);
|
||||
}, { base: {} }).base;
|
||||
}
|
||||
|
||||
function set(obj, key, val) {
|
||||
var v = obj[key];
|
||||
if (typeof v === 'undefined') {
|
||||
obj[key] = val;
|
||||
} else if (isArray(v)) {
|
||||
v.push(val);
|
||||
} else {
|
||||
obj[key] = [v, val];
|
||||
}
|
||||
}
|
||||
|
||||
function lastBraceInKey(str) {
|
||||
var len = str.length,
|
||||
brace,
|
||||
c;
|
||||
for (var i = 0; i < len; ++i) {
|
||||
c = str[i];
|
||||
if (']' == c) brace = false;
|
||||
if ('[' == c) brace = true;
|
||||
if ('=' == c && !brace) return i;
|
||||
}
|
||||
}
|
||||
|
||||
function reduce(obj, accumulator){
|
||||
var i = 0,
|
||||
l = obj.length >> 0,
|
||||
curr = arguments[2];
|
||||
while (i < l) {
|
||||
if (i in obj) curr = accumulator.call(undefined, curr, obj[i], i, obj);
|
||||
++i;
|
||||
}
|
||||
return curr;
|
||||
}
|
||||
|
||||
function isArray(vArg) {
|
||||
return Object.prototype.toString.call(vArg) === "[object Array]";
|
||||
}
|
||||
|
||||
function keys(obj) {
|
||||
var key_array = [];
|
||||
for ( var prop in obj ) {
|
||||
if ( obj.hasOwnProperty(prop) ) key_array.push(prop);
|
||||
}
|
||||
return key_array;
|
||||
}
|
||||
|
||||
function purl( url, strictMode ) {
|
||||
if ( arguments.length === 1 && url === true ) {
|
||||
strictMode = true;
|
||||
url = undefined;
|
||||
}
|
||||
strictMode = strictMode || false;
|
||||
url = url || window.location.toString();
|
||||
|
||||
return {
|
||||
|
||||
data : parseUri(url, strictMode),
|
||||
|
||||
// get various attributes from the URI
|
||||
attr : function( attr ) {
|
||||
attr = aliases[attr] || attr;
|
||||
return typeof attr !== 'undefined' ? this.data.attr[attr] : this.data.attr;
|
||||
},
|
||||
|
||||
// return query string parameters
|
||||
param : function( param ) {
|
||||
return typeof param !== 'undefined' ? this.data.param.query[param] : this.data.param.query;
|
||||
},
|
||||
|
||||
// return fragment parameters
|
||||
fparam : function( param ) {
|
||||
return typeof param !== 'undefined' ? this.data.param.fragment[param] : this.data.param.fragment;
|
||||
},
|
||||
|
||||
// return path segments
|
||||
segment : function( seg ) {
|
||||
if ( typeof seg === 'undefined' ) {
|
||||
return this.data.seg.path;
|
||||
} else {
|
||||
seg = seg < 0 ? this.data.seg.path.length + seg : seg - 1; // negative segments count from the end
|
||||
return this.data.seg.path[seg];
|
||||
}
|
||||
},
|
||||
|
||||
// return fragment segments
|
||||
fsegment : function( seg ) {
|
||||
if ( typeof seg === 'undefined' ) {
|
||||
return this.data.seg.fragment;
|
||||
} else {
|
||||
seg = seg < 0 ? this.data.seg.fragment.length + seg : seg - 1; // negative segments count from the end
|
||||
return this.data.seg.fragment[seg];
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
purl.jQuery = function($){
|
||||
if ($ != null) {
|
||||
$.fn.url = function( strictMode ) {
|
||||
var url = '';
|
||||
if ( this.length ) {
|
||||
url = $(this).attr( getAttrName(this[0]) ) || '';
|
||||
}
|
||||
return purl( url, strictMode );
|
||||
};
|
||||
|
||||
$.url = purl;
|
||||
}
|
||||
};
|
||||
|
||||
purl.jQuery(window.jQuery);
|
||||
|
||||
return purl;
|
||||
|
||||
});
|
45
src/jsp/web/javascript/menu.js
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright © Dave Perrett and Malcolm Jarvis
|
||||
* This code is licensed under the GPL version 2.
|
||||
* For more details, see http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
*
|
||||
* Menu Configuration Properties
|
||||
*/
|
||||
|
||||
Menu = {
|
||||
|
||||
context: {
|
||||
|
||||
menu_style: {
|
||||
width: '310px',
|
||||
backgroundColor: '#fff',
|
||||
border: 'none',
|
||||
padding: '5px 0',
|
||||
textAlign: 'left'},
|
||||
|
||||
item_style: {
|
||||
backgroundColor: 'transparent',
|
||||
margin: '0',
|
||||
padding: '0 10px 2px 20px',
|
||||
color: '#000',
|
||||
fontSize: '14px',
|
||||
cursor: 'default',
|
||||
border: 'none'},
|
||||
|
||||
item_hover_style: {
|
||||
backgroundColor: '#24e',
|
||||
color: '#fff',
|
||||
border: 'none'},
|
||||
|
||||
item_disabled_style: {
|
||||
backgroundColor: 'transparent',
|
||||
margin: '0',
|
||||
padding: '0 10px 2px 20px',
|
||||
color: '#aaa',
|
||||
fontSize: '1.5em',
|
||||
cursor: 'default',
|
||||
border: 'none'}
|
||||
}
|
||||
|
||||
|
||||
}
|
42
src/jsp/web/javascript/notifications.js
Normal file
@ -0,0 +1,42 @@
|
||||
var Notifications = {};
|
||||
|
||||
$(document).ready(function () {
|
||||
if (!window.webkitNotifications) {
|
||||
return;
|
||||
}
|
||||
|
||||
var notificationsEnabled = (window.webkitNotifications.checkPermission() === 0),
|
||||
toggle = $('#toggle_notifications > a');
|
||||
|
||||
toggle.show();
|
||||
updateMenuTitle();
|
||||
$(transmission).bind('downloadComplete seedingComplete', function (event, torrent) {
|
||||
if (notificationsEnabled) {
|
||||
var title = (event.type == 'downloadComplete' ? 'Download' : 'Seeding') + ' complete',
|
||||
content = torrent.getName(),
|
||||
notification;
|
||||
|
||||
notification = window.webkitNotifications.createNotification('style/transmission/images/logo.png', title, content);
|
||||
notification.show();
|
||||
setTimeout(function () {
|
||||
notification.cancel();
|
||||
}, 5000);
|
||||
};
|
||||
});
|
||||
|
||||
function updateMenuTitle() {
|
||||
toggle.html((notificationsEnabled ? 'Disable' : 'Enable') + ' Notifications');
|
||||
}
|
||||
|
||||
Notifications.toggle = function () {
|
||||
if (window.webkitNotifications.checkPermission() !== 0) {
|
||||
window.webkitNotifications.requestPermission(function () {
|
||||
notificationsEnabled = (window.webkitNotifications.checkPermission() === 0);
|
||||
updateMenuTitle();
|
||||
});
|
||||
} else {
|
||||
notificationsEnabled = !notificationsEnabled;
|
||||
updateMenuTitle();
|
||||
}
|
||||
};
|
||||
});
|
313
src/jsp/web/javascript/prefs-dialog.js
Normal file
@ -0,0 +1,313 @@
|
||||
/**
|
||||
* 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 PrefsDialog(remote) {
|
||||
|
||||
var data = {
|
||||
dialog: null,
|
||||
remote: null,
|
||||
elements: { },
|
||||
|
||||
// all the RPC session keys that we have gui controls for
|
||||
keys: [
|
||||
'alt-speed-down',
|
||||
'alt-speed-time-begin',
|
||||
'alt-speed-time-day',
|
||||
'alt-speed-time-enabled',
|
||||
'alt-speed-time-end',
|
||||
'alt-speed-up',
|
||||
'blocklist-enabled',
|
||||
'blocklist-size',
|
||||
'blocklist-url',
|
||||
'dht-enabled',
|
||||
'download-dir',
|
||||
'encryption',
|
||||
'idle-seeding-limit',
|
||||
'idle-seeding-limit-enabled',
|
||||
'lpd-enabled',
|
||||
'peer-limit-global',
|
||||
'peer-limit-per-torrent',
|
||||
'peer-port',
|
||||
'peer-port-random-on-start',
|
||||
'pex-enabled',
|
||||
'port-forwarding-enabled',
|
||||
'rename-partial-files',
|
||||
'seedRatioLimit',
|
||||
'seedRatioLimited',
|
||||
'speed-limit-down',
|
||||
'speed-limit-down-enabled',
|
||||
'speed-limit-up',
|
||||
'speed-limit-up-enabled',
|
||||
'start-added-torrents',
|
||||
'utp-enabled'
|
||||
],
|
||||
|
||||
// map of keys that are enabled only if a 'parent' key is enabled
|
||||
groups: {
|
||||
'alt-speed-time-enabled': ['alt-speed-time-begin',
|
||||
'alt-speed-time-day',
|
||||
'alt-speed-time-end' ],
|
||||
'blocklist-enabled': ['blocklist-url',
|
||||
'blocklist-update-button' ],
|
||||
'idle-seeding-limit-enabled': [ 'idle-seeding-limit' ],
|
||||
'seedRatioLimited': [ 'seedRatioLimit' ],
|
||||
'speed-limit-down-enabled': [ 'speed-limit-down' ],
|
||||
'speed-limit-up-enabled': [ 'speed-limit-up' ]
|
||||
}
|
||||
},
|
||||
|
||||
initTimeDropDown = function(e)
|
||||
{
|
||||
var i, hour, mins, value, content;
|
||||
|
||||
for (i=0; i<24*4; ++i) {
|
||||
hour = parseInt(i/4, 10);
|
||||
mins = ((i%4) * 15);
|
||||
value = i * 15;
|
||||
content = hour + ':' + (mins || '00');
|
||||
e.options[i] = new Option(content, value);
|
||||
}
|
||||
},
|
||||
|
||||
onPortChecked = function(response)
|
||||
{
|
||||
var is_open = response['arguments']['port-is-open'],
|
||||
text = 'Port is <b>' + (is_open ? 'Open' : 'Closed') + '</b>',
|
||||
e = data.elements.root.find('#port-label');
|
||||
setInnerHTML(e[0],text);
|
||||
},
|
||||
|
||||
setGroupEnabled = function(parent_key, enabled)
|
||||
{
|
||||
var i, key, keys, root;
|
||||
|
||||
if (parent_key in data.groups)
|
||||
{
|
||||
root = data.elements.root,
|
||||
keys = data.groups[parent_key];
|
||||
|
||||
for (i=0; key=keys[i]; ++i)
|
||||
root.find('#'+key).attr('disabled',!enabled);
|
||||
}
|
||||
},
|
||||
|
||||
onBlocklistUpdateClicked = function ()
|
||||
{
|
||||
data.remote.updateBlocklist();
|
||||
setBlocklistButtonEnabled(false);
|
||||
},
|
||||
setBlocklistButtonEnabled = function(b)
|
||||
{
|
||||
var e = data.elements.blocklist_button;
|
||||
e.attr('disabled',!b);
|
||||
e.val(b ? 'Update' : 'Updating...');
|
||||
},
|
||||
|
||||
getValue = function(e)
|
||||
{
|
||||
var str;
|
||||
|
||||
switch (e[0].type)
|
||||
{
|
||||
case 'checkbox':
|
||||
case 'radio':
|
||||
return e.prop('checked');
|
||||
|
||||
case 'text':
|
||||
case 'url':
|
||||
case 'email':
|
||||
case 'number':
|
||||
case 'search':
|
||||
case 'select-one':
|
||||
str = e.val();
|
||||
if( parseInt(str,10).toString() === str)
|
||||
return parseInt(str,10);
|
||||
if( parseFloat(str).toString() === str)
|
||||
return parseFloat(str);
|
||||
return str;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
/* this callback is for controls whose changes can be applied
|
||||
immediately, like checkboxs, radioboxes, and selects */
|
||||
onControlChanged = function(ev)
|
||||
{
|
||||
var o = {};
|
||||
o[ev.target.id] = getValue($(ev.target));
|
||||
data.remote.savePrefs(o);
|
||||
},
|
||||
|
||||
/* these two callbacks are for controls whose changes can't be applied
|
||||
immediately -- like a text entry field -- because it takes many
|
||||
change events for the user to get to the desired result */
|
||||
onControlFocused = function(ev)
|
||||
{
|
||||
data.oldValue = getValue($(ev.target));
|
||||
},
|
||||
onControlBlurred = function(ev)
|
||||
{
|
||||
var newValue = getValue($(ev.target));
|
||||
if (newValue !== data.oldValue)
|
||||
{
|
||||
var o = {};
|
||||
o[ev.target.id] = newValue;
|
||||
data.remote.savePrefs(o);
|
||||
delete data.oldValue;
|
||||
}
|
||||
},
|
||||
|
||||
initialize = function (remote)
|
||||
{
|
||||
var i, key, e, o;
|
||||
|
||||
data.remote = remote;
|
||||
|
||||
e = $('#prefs-dialog');
|
||||
data.elements.root = e;
|
||||
|
||||
initTimeDropDown(e.find('#alt-speed-time-begin')[0]);
|
||||
initTimeDropDown(e.find('#alt-speed-time-end')[0]);
|
||||
|
||||
o = {};
|
||||
o.width = Math.min($(window).width(), 350);
|
||||
o.height = Math.min($(window).height(), 400);
|
||||
o.autoOpen = false;
|
||||
o.show = o.hide = 'fade';
|
||||
o.close = onDialogClosed;
|
||||
e.tabbedDialog(o);
|
||||
|
||||
e = e.find('#blocklist-update-button');
|
||||
data.elements.blocklist_button = e;
|
||||
e.click(onBlocklistUpdateClicked);
|
||||
|
||||
// listen for user input
|
||||
for (i=0; key=data.keys[i]; ++i)
|
||||
{
|
||||
e = data.elements.root.find('#'+key);
|
||||
switch (e[0].type)
|
||||
{
|
||||
case 'checkbox':
|
||||
case 'radio':
|
||||
case 'select-one':
|
||||
e.change(onControlChanged);
|
||||
break;
|
||||
|
||||
case 'text':
|
||||
case 'url':
|
||||
case 'email':
|
||||
case 'number':
|
||||
case 'search':
|
||||
e.focus(onControlFocused);
|
||||
e.blur(onControlBlurred);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getValues = function()
|
||||
{
|
||||
var i, key, val, o={},
|
||||
keys = data.keys,
|
||||
root = data.elements.root;
|
||||
|
||||
for (i=0; key=keys[i]; ++i) {
|
||||
val = getValue(root.find('#'+key));
|
||||
if (val !== null)
|
||||
o[key] = val;
|
||||
}
|
||||
|
||||
return o;
|
||||
},
|
||||
|
||||
onDialogClosed = function()
|
||||
{
|
||||
transmission.hideMobileAddressbar();
|
||||
|
||||
$(data.dialog).trigger('closed', getValues());
|
||||
};
|
||||
|
||||
/****
|
||||
***** PUBLIC FUNCTIONS
|
||||
****/
|
||||
|
||||
// update the dialog's controls
|
||||
this.set = function (o)
|
||||
{
|
||||
var e, i, key, val, option,
|
||||
keys = data.keys,
|
||||
root = data.elements.root;
|
||||
|
||||
setBlocklistButtonEnabled(true);
|
||||
|
||||
for (i=0; key=keys[i]; ++i)
|
||||
{
|
||||
val = o[key];
|
||||
e = root.find('#'+key);
|
||||
|
||||
if (key === 'blocklist-size')
|
||||
{
|
||||
// special case -- regular text area
|
||||
e.text('' + val.toStringWithCommas());
|
||||
}
|
||||
else switch (e[0].type)
|
||||
{
|
||||
case 'checkbox':
|
||||
case 'radio':
|
||||
e.prop('checked', val);
|
||||
setGroupEnabled(key, val);
|
||||
break;
|
||||
case 'text':
|
||||
case 'url':
|
||||
case 'email':
|
||||
case 'number':
|
||||
case 'search':
|
||||
// don't change the text if the user's editing it.
|
||||
// it's very annoying when that happens!
|
||||
try {
|
||||
//document.activeElement.focus();
|
||||
if (e[0] === document.activeElement)
|
||||
break;
|
||||
} catch (e) {}
|
||||
e.val(val);
|
||||
break;
|
||||
case 'select-one':
|
||||
e.val(val);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.show = function ()
|
||||
{
|
||||
transmission.hideMobileAddressbar();
|
||||
|
||||
setBlocklistButtonEnabled(true);
|
||||
data.remote.checkPort(onPortChecked,this);
|
||||
data.elements.root.dialog('open');
|
||||
};
|
||||
|
||||
this.close = function ()
|
||||
{
|
||||
transmission.hideMobileAddressBar();
|
||||
data.elements.root.dialog('close');
|
||||
}
|
||||
|
||||
this.shouldAddedTorrentsStart = function()
|
||||
{
|
||||
return data.elements.root.find('#start-added-torrents')[0].checked;
|
||||
};
|
||||
|
||||
data.dialog = this;
|
||||
initialize (remote);
|
||||
};
|
451
src/jsp/web/javascript/remote.js
Normal file
@ -0,0 +1,451 @@
|
||||
/* Transmission Revision 14025 */
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
var RPC = {
|
||||
_DaemonVersion : 'version',
|
||||
_DownSpeedLimit : 'speed-limit-down',
|
||||
_DownSpeedLimited : 'speed-limit-down-enabled',
|
||||
_StartAddedTorrent : 'start-added-torrents',
|
||||
_QueueMoveTop : 'queue-move-top',
|
||||
_QueueMoveBottom : 'queue-move-bottom',
|
||||
_QueueMoveUp : 'queue-move-up',
|
||||
_QueueMoveDown : 'queue-move-down',
|
||||
/* Original:
|
||||
_Root : '../rpc',
|
||||
*/
|
||||
_Root : './transmission/rpc',
|
||||
_TurtleDownSpeedLimit : 'alt-speed-down',
|
||||
_TurtleState : 'alt-speed-enabled',
|
||||
_TurtleUpSpeedLimit : 'alt-speed-up',
|
||||
_UpSpeedLimit : 'speed-limit-up',
|
||||
_UpSpeedLimited : 'speed-limit-up-enabled',
|
||||
_AzMode : 'az-mode',
|
||||
_BasicAuth : ''
|
||||
};
|
||||
|
||||
// >> Vuze: Override some RPC prefs via url parameters
|
||||
var url = window.location.toString();
|
||||
// Android API 11-15 doesn't support url parameters on local files. We
|
||||
// hack it into userAgent :)
|
||||
if (navigator.userAgent.lastIndexOf("?", 0) === 0) {
|
||||
url = url + navigator.userAgent;
|
||||
url = url.replace(/[\r\n]/g, "");
|
||||
}
|
||||
var urlParser = $.url(url);
|
||||
root = urlParser.param("_Root");
|
||||
if (typeof root !== "undefined") {
|
||||
RPC._Root = root;
|
||||
}
|
||||
|
||||
basicAuth = urlParser.param("_BasicAuth");
|
||||
if (typeof basicAuth !== "undefined") {
|
||||
RPC._BasicAuth = basicAuth;
|
||||
}
|
||||
// << Vuze
|
||||
|
||||
function TransmissionRemote(controller)
|
||||
{
|
||||
this.initialize(controller);
|
||||
return this;
|
||||
}
|
||||
|
||||
TransmissionRemote.prototype =
|
||||
{
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
initialize: function(controller) {
|
||||
this._controller = controller;
|
||||
this._error = '';
|
||||
this._lastCallWasError = false;
|
||||
this._token = '';
|
||||
/* >> Vuze Added */
|
||||
this._request_count = 0;
|
||||
this._error_array = new Array();
|
||||
this._errors_tolerated = 5;
|
||||
/* << Vuze Added */
|
||||
},
|
||||
|
||||
/* >> Vuze Added */
|
||||
isPersistentError: function(){
|
||||
var remote = this;
|
||||
var max = remote._error_array.length > 0 ? Math.max.apply(null, remote._error_array) : 0;
|
||||
return $.grep(remote._error_array, function(n, i){
|
||||
return n >= ( max - remote._errors_tolerated )
|
||||
}).length > remote._errors_tolerated
|
||||
},
|
||||
|
||||
hasError: function() {
|
||||
return this._lastCallWasError;
|
||||
},
|
||||
/* << Vuze Added */
|
||||
|
||||
/*
|
||||
* Display an error if an ajax request fails, and stop sending requests
|
||||
* or on a 409, globally set the X-Transmission-Session-Id and resend
|
||||
*/
|
||||
ajaxError: function(jqXHR, textStatus, exception, ajaxObject) {
|
||||
var token,
|
||||
remote = this;
|
||||
|
||||
// set the Transmission-Session-Id on a 409
|
||||
if (jqXHR.status === 409 && (token = jqXHR.getResponseHeader('X-Transmission-Session-Id'))){
|
||||
remote._token = token;
|
||||
$.ajax(ajaxObject);
|
||||
return;
|
||||
}
|
||||
|
||||
remote._error = jqXHR.responseText
|
||||
? jqXHR.responseText.trim().replace(/(<([^>]+)>)/ig,"")
|
||||
: "";
|
||||
if(!remote._error.length ) {
|
||||
remote._error = 'Server not responding' + ' (' + String(textStatus);
|
||||
if (String(exception).length > 0) {
|
||||
remote._error += ': ' + String(exception);
|
||||
}
|
||||
remote._error += ')';
|
||||
}
|
||||
|
||||
/* >> Vuze Added */
|
||||
remote._lastCallWasError = true;
|
||||
// remote._error_array.push( remote._request_count )
|
||||
//
|
||||
// if( !remote.isPersistentError() ) {
|
||||
// console.log("not a persistent error -- exit");
|
||||
// return;
|
||||
// }
|
||||
if (vz.handleConnectionError(jqXHR.status, remote._error, textStatus)) {
|
||||
return;
|
||||
}
|
||||
/* << Vuze Added */
|
||||
|
||||
|
||||
// Vuze: Fixes Uncaught ReferenceError: remote is not defined
|
||||
errorQuoted = remote._error.replace(/"/g, '\\"');
|
||||
|
||||
dialog.confirm('Connection Failed',
|
||||
'Could not connect to the server. You may need to reload the page to reconnect.',
|
||||
'Details',
|
||||
'alert("' + errorQuoted + '");',
|
||||
null,
|
||||
'Dismiss');
|
||||
remote._controller.togglePeriodicSessionRefresh(false);
|
||||
},
|
||||
|
||||
appendSessionId: function(XHR) {
|
||||
/* >> Vuze Added */
|
||||
if (RPC._BasicAuth.length > 0) {
|
||||
XHR.setRequestHeader('Authorization', 'Basic ' + RPC._BasicAuth);
|
||||
}
|
||||
if(this._token)
|
||||
/* << Vuze Added */
|
||||
XHR.setRequestHeader('X-Transmission-Session-Id', this._token);
|
||||
},
|
||||
|
||||
sendRequest: function(data, callback, context, async) {
|
||||
var remote = this;
|
||||
if (typeof async != 'boolean')
|
||||
async = true;
|
||||
|
||||
/* >> Vuze Added */
|
||||
remote._request_count += 1;
|
||||
remote._lastCallWasError = false;
|
||||
/* >> Vuze Added */
|
||||
|
||||
// >> Vuze: iPod caches without this
|
||||
data['math'] = Math.random();
|
||||
// << Vuze
|
||||
|
||||
var ajaxSettings = {
|
||||
odata: data,
|
||||
startTime: new Date().getTime(),
|
||||
url: RPC._Root,
|
||||
type: 'POST',
|
||||
contentType: 'json',
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
timeout: 60000,
|
||||
data: JSON.stringify(data),
|
||||
beforeSend: function(XHR){ remote.appendSessionId(XHR); },
|
||||
error: function(request, textStatus, exception){
|
||||
if (request.status !== 409) {
|
||||
try {
|
||||
console.log('ajax error for ' + JSON.stringify(request));
|
||||
console.log('ajax textStatus: ' + textStatus);
|
||||
console.log('ajax error: ' + exception);
|
||||
} catch (err) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
remote.ajaxError(request, textStatus, exception, ajaxSettings);
|
||||
},
|
||||
success: callback,
|
||||
context: context,
|
||||
async: async
|
||||
};
|
||||
|
||||
|
||||
if (async) {
|
||||
ajaxSettings.slowAjaxTimeOut = setTimeout(function() {
|
||||
ajaxSettings.slowAjaxTimeOut = null;
|
||||
vz.slowAjax(ajaxSettings.odata['method']);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
$.ajax(ajaxSettings).always(function() {
|
||||
diff = (new Date().getTime()) - ajaxSettings.startTime ;
|
||||
if (typeof ajaxSettings.slowAjaxTimeOut !== "undefined") {
|
||||
if (ajaxSettings.slowAjaxTimeOut != null) {
|
||||
clearTimeout(ajaxSettings.slowAjaxTimeOut);
|
||||
} else {
|
||||
vz.slowAjaxDone(ajaxSettings.odata['method'], diff);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
loadDaemonPrefs: function(callback, context, async) {
|
||||
var o = { method: 'session-get' };
|
||||
this.sendRequest(o, callback, context, async);
|
||||
},
|
||||
|
||||
checkPort: function(callback, context, async) {
|
||||
var o = { method: 'port-test' };
|
||||
this.sendRequest(o, callback, context, async);
|
||||
},
|
||||
|
||||
loadDaemonStats: function(callback, context, async) {
|
||||
var o = { method: 'session-stats' };
|
||||
this.sendRequest(o, callback, context, async);
|
||||
},
|
||||
|
||||
updateTorrents: function(torrentIds, fields, callback, context) {
|
||||
var o = {
|
||||
method: 'torrent-get',
|
||||
'arguments': {
|
||||
'fields': fields
|
||||
}
|
||||
};
|
||||
if (torrentIds)
|
||||
o['arguments'].ids = torrentIds;
|
||||
o['math'] = Math.random();
|
||||
this.sendRequest(o, function(response) {
|
||||
var args = response['arguments'];
|
||||
callback.call(context,args.torrents,args.removed);
|
||||
});
|
||||
},
|
||||
|
||||
getFreeSpace: function(dir, callback, context) {
|
||||
var remote = this;
|
||||
var o = {
|
||||
method: 'free-space',
|
||||
arguments: { path: dir }
|
||||
};
|
||||
this.sendRequest(o, function(response) {
|
||||
var args = response['arguments'];
|
||||
callback.call (context, args.path, args['size-bytes']);
|
||||
});
|
||||
},
|
||||
|
||||
changeFileCommand: function(torrentId, fileIndices, command) {
|
||||
var remote = this,
|
||||
args = { ids: [torrentId] };
|
||||
args[command] = fileIndices;
|
||||
this.sendRequest({
|
||||
arguments: args,
|
||||
method: 'torrent-set'
|
||||
}, function() {
|
||||
remote._controller.refreshTorrents(true);
|
||||
// There was never a refreshTorrents that accepted an ID..
|
||||
//remote._controller.refreshTorrents([torrentId]);
|
||||
});
|
||||
},
|
||||
|
||||
sendTorrentSetRequests: function(method, torrent_ids, args, callback, context) {
|
||||
if (!args) args = { };
|
||||
args['ids'] = torrent_ids;
|
||||
var o = {
|
||||
method: method,
|
||||
arguments: args
|
||||
};
|
||||
this.sendRequest(o, callback, context);
|
||||
},
|
||||
|
||||
sendTorrentActionRequests: function(method, torrent_ids, callback, context) {
|
||||
this.sendTorrentSetRequests(method, torrent_ids, null, callback, context);
|
||||
},
|
||||
|
||||
startTorrents: function(torrent_ids, noqueue, callback, context) {
|
||||
var name = noqueue ? 'torrent-start-now' : 'torrent-start';
|
||||
this.sendTorrentActionRequests(name, torrent_ids, callback, context);
|
||||
},
|
||||
stopTorrents: function(torrent_ids, callback, context) {
|
||||
this.sendTorrentActionRequests('torrent-stop', torrent_ids, callback, context);
|
||||
},
|
||||
|
||||
moveTorrents: function(torrent_ids, new_location, callback, context) {
|
||||
var remote = this;
|
||||
this.sendTorrentSetRequests( 'torrent-set-location', torrent_ids,
|
||||
{"move": true, "location": new_location}, callback, context);
|
||||
},
|
||||
|
||||
/* Vuze:Tag list! */
|
||||
getTagList: function(callback, context) {
|
||||
var remote = this;
|
||||
var o = {
|
||||
method: 'tags-get-list',
|
||||
};
|
||||
this.sendRequest(o, function(response) {
|
||||
var args = response['arguments'];
|
||||
var tagArray = [];
|
||||
args.tags.forEach(function(tag) {
|
||||
tagArray[tag.uid] = tag;
|
||||
});
|
||||
callback.call (context, tagArray);
|
||||
});
|
||||
},
|
||||
|
||||
removeTorrents: function(torrent_ids, callback, context) {
|
||||
this.sendTorrentActionRequests('torrent-remove', torrent_ids, callback, context);
|
||||
},
|
||||
removeTorrentsAndData: function(torrents) {
|
||||
var remote = this;
|
||||
var o = {
|
||||
method: 'torrent-remove',
|
||||
arguments: {
|
||||
'delete-local-data': true,
|
||||
ids: [ ]
|
||||
}
|
||||
};
|
||||
|
||||
if (torrents) {
|
||||
for (var i=0, len=torrents.length; i<len; ++i) {
|
||||
o.arguments.ids.push(torrents[i].getId());
|
||||
}
|
||||
}
|
||||
this.sendRequest(o, function() {
|
||||
remote._controller.refreshTorrents(true);
|
||||
});
|
||||
},
|
||||
// >> Vuze
|
||||
removeTorrentById: function(id, deleteData) {
|
||||
if (typeof deleteData != "boolean") {
|
||||
deleteData = true;
|
||||
}
|
||||
var remote = this;
|
||||
var o = {
|
||||
method: 'torrent-remove',
|
||||
arguments: {
|
||||
'delete-local-data': deleteData,
|
||||
ids: [ id ]
|
||||
}
|
||||
};
|
||||
|
||||
this.sendRequest(o, function() {
|
||||
remote._controller.refreshTorrents(true);
|
||||
});
|
||||
},
|
||||
// << Vuze
|
||||
verifyTorrents: function(torrent_ids, callback, context) {
|
||||
this.sendTorrentActionRequests('torrent-verify', torrent_ids, callback, context);
|
||||
},
|
||||
reannounceTorrents: function(torrent_ids, callback, context) {
|
||||
this.sendTorrentActionRequests('torrent-reannounce', torrent_ids, callback, context);
|
||||
},
|
||||
addTorrentByUrl: function(url, options) {
|
||||
// >> Vuze: fill in default options
|
||||
if (typeof options !== "object" || options == null) {
|
||||
options = {
|
||||
'download-dir' : $("#download-dir").val(),
|
||||
paused : !transmission.shouldAddedTorrentsStart()
|
||||
};
|
||||
}
|
||||
// << Vuze
|
||||
|
||||
var remote = this;
|
||||
if (url.match(/^[0-9a-f]{40}$/i)) {
|
||||
url = 'magnet:?xt=urn:btih:'+url;
|
||||
}
|
||||
var o = {
|
||||
method: 'torrent-add',
|
||||
arguments: {
|
||||
paused: (options.paused),
|
||||
'download-dir': (options.destination),
|
||||
filename: url
|
||||
}
|
||||
};
|
||||
this.sendRequest(o, function(response) {
|
||||
var result = response['result'];
|
||||
if (result != "success") {
|
||||
alert("Error adding torrent:\n\n" + result);
|
||||
}
|
||||
remote._controller.refreshTorrents(true);
|
||||
});
|
||||
},
|
||||
// >> Vuze
|
||||
addTorrentByMetainfo: function(metainfo, options) {
|
||||
var remote = this;
|
||||
|
||||
if (typeof options !== "object" || options == null) {
|
||||
options = {
|
||||
destination : $("#download-dir").val(),
|
||||
paused : !transmission.shouldAddedTorrentsStart()
|
||||
};
|
||||
}
|
||||
|
||||
var o = {
|
||||
'method' : 'torrent-add',
|
||||
arguments : {
|
||||
paused: (options.paused),
|
||||
'download-dir': (options.destination),
|
||||
'metainfo' : metainfo
|
||||
}
|
||||
};
|
||||
remote.sendRequest(o, function(response) {
|
||||
if (response.result != 'success') {
|
||||
alert('Error adding torrent: ' + response.result);
|
||||
}
|
||||
remote._controller.refreshTorrents(true);
|
||||
});
|
||||
},
|
||||
// << Vuze
|
||||
savePrefs: function(args) {
|
||||
var remote = this;
|
||||
var o = {
|
||||
method: 'session-set',
|
||||
arguments: args
|
||||
};
|
||||
this.sendRequest(o, function() {
|
||||
remote._controller.loadDaemonPrefs();
|
||||
});
|
||||
},
|
||||
updateBlocklist: function() {
|
||||
var remote = this;
|
||||
var o = {
|
||||
method: 'blocklist-update'
|
||||
};
|
||||
this.sendRequest(o, function() {
|
||||
remote._controller.loadDaemonPrefs();
|
||||
});
|
||||
},
|
||||
|
||||
// Added queue calls
|
||||
moveTorrentsToTop: function(torrent_ids, callback, context) {
|
||||
this.sendTorrentActionRequests(RPC._QueueMoveTop, torrent_ids, callback, context);
|
||||
},
|
||||
moveTorrentsToBottom: function(torrent_ids, callback, context) {
|
||||
this.sendTorrentActionRequests(RPC._QueueMoveBottom, torrent_ids, callback, context);
|
||||
},
|
||||
moveTorrentsUp: function(torrent_ids, callback, context) {
|
||||
this.sendTorrentActionRequests(RPC._QueueMoveUp, torrent_ids, callback, context);
|
||||
},
|
||||
moveTorrentsDown: function(torrent_ids, callback, context) {
|
||||
this.sendTorrentActionRequests(RPC._QueueMoveDown, torrent_ids, callback, context);
|
||||
}
|
||||
};
|
429
src/jsp/web/javascript/torrent-row.js
Normal file
@ -0,0 +1,429 @@
|
||||
/* 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 TorrentRendererHelper()
|
||||
{
|
||||
}
|
||||
|
||||
TorrentRendererHelper.getProgressInfo = function(controller, t)
|
||||
{
|
||||
var pct, extra,
|
||||
s = t.getStatus(),
|
||||
seed_ratio_limit = t.seedRatioLimit(controller);
|
||||
|
||||
if (t.needsMetaData())
|
||||
pct = t.getMetadataPercentComplete() * 100;
|
||||
else if (!t.isDone())
|
||||
pct = Math.round(t.getPercentDone() * 100);
|
||||
else if (seed_ratio_limit > 0 && t.isSeeding()) // don't split up the bar if paused or queued
|
||||
pct = Math.round(t.getUploadRatio() * 100 / seed_ratio_limit);
|
||||
else
|
||||
pct = 100;
|
||||
|
||||
/* >> Vuze: Protect against bounds overrun */
|
||||
pct = pct < 0 ? 0 : ( pct > 100 ? 100 : pct );
|
||||
/* << Vuze */
|
||||
|
||||
if (s === Torrent._StatusStopped)
|
||||
extra = 'paused';
|
||||
else if (s === Torrent._StatusDownloadWait)
|
||||
extra = 'leeching queued';
|
||||
else if (t.needsMetaData())
|
||||
extra = 'magnet';
|
||||
else if (s === Torrent._StatusDownload)
|
||||
extra = 'leeching';
|
||||
else if (s === Torrent._StatusSeedWait)
|
||||
extra = 'seeding queued';
|
||||
else if (s === Torrent._StatusSeed)
|
||||
extra = 'seeding';
|
||||
else
|
||||
extra = '';
|
||||
|
||||
return {
|
||||
percent: pct,
|
||||
complete: [ 'torrent_progress_bar', 'complete', extra ].join(' '),
|
||||
incomplete: [ 'torrent_progress_bar', 'incomplete', extra ].join(' ')
|
||||
};
|
||||
};
|
||||
|
||||
TorrentRendererHelper.createProgressbar = function(classes)
|
||||
{
|
||||
var complete, incomplete, progressbar;
|
||||
|
||||
complete = document.createElement('div');
|
||||
complete.className = 'torrent_progress_bar complete';
|
||||
|
||||
incomplete = document.createElement('div');
|
||||
incomplete.className = 'torrent_progress_bar incomplete';
|
||||
|
||||
progressbar = document.createElement('div');
|
||||
progressbar.className = 'torrent_progress_bar_container ' + classes;
|
||||
progressbar.appendChild(complete);
|
||||
progressbar.appendChild(incomplete);
|
||||
|
||||
return { 'element': progressbar, 'complete': complete, 'incomplete': incomplete };
|
||||
};
|
||||
|
||||
TorrentRendererHelper.renderProgressbar = function(controller, t, progressbar)
|
||||
{
|
||||
var e, style, width, display,
|
||||
info = TorrentRendererHelper.getProgressInfo(controller, t);
|
||||
|
||||
// update the complete progressbar
|
||||
e = progressbar.complete;
|
||||
style = e.style;
|
||||
width = '' + info.percent + '%';
|
||||
display = info.percent > 0 ? 'block' : 'none';
|
||||
if (style.width!==width || style.display!==display)
|
||||
$(e).css({ width: ''+info.percent+'%', display: display });
|
||||
if (e.className !== info.complete)
|
||||
e.className = info.complete;
|
||||
|
||||
// update the incomplete progressbar
|
||||
e = progressbar.incomplete;
|
||||
display = (info.percent < 100) ? 'block' : 'none';
|
||||
if (e.style.display !== display)
|
||||
e.style.display = display;
|
||||
if (e.className !== info.incomplete)
|
||||
e.className = info.incomplete;
|
||||
};
|
||||
|
||||
TorrentRendererHelper.formatUL = function(t)
|
||||
{
|
||||
return '↑ ' + Transmission.fmt.speedBps(t.getUploadSpeed());
|
||||
};
|
||||
|
||||
TorrentRendererHelper.formatDL = function(t)
|
||||
{
|
||||
return '↓ ' + Transmission.fmt.speedBps(t.getDownloadSpeed());
|
||||
};
|
||||
|
||||
/****
|
||||
*****
|
||||
*****
|
||||
****/
|
||||
|
||||
function TorrentRendererFull()
|
||||
{
|
||||
}
|
||||
TorrentRendererFull.prototype =
|
||||
{
|
||||
createRow: function()
|
||||
{
|
||||
var root, name, peers, progressbar, details, image, button;
|
||||
|
||||
root = document.createElement('li');
|
||||
root.className = 'torrent';
|
||||
|
||||
name = document.createElement('div');
|
||||
name.className = 'torrent_name';
|
||||
|
||||
peers = document.createElement('div');
|
||||
peers.className = 'torrent_peer_details';
|
||||
|
||||
progressbar = TorrentRendererHelper.createProgressbar('full');
|
||||
|
||||
details = document.createElement('div');
|
||||
details.className = 'torrent_progress_details';
|
||||
|
||||
//image = document.createElement('div');
|
||||
//button = document.createElement('a');
|
||||
//button.appendChild(image);
|
||||
|
||||
/* >> Vuze: Info Button */
|
||||
var image2 = document.createElement('div');
|
||||
image2.className = 'torrent_info';
|
||||
var button2 = document.createElement('a');
|
||||
button2.href="javascript:void(0)";
|
||||
button2.className = "torrent_button_info";
|
||||
button2.appendChild(image2);
|
||||
/* << Vuze: Info Button */
|
||||
|
||||
root.appendChild(name);
|
||||
root.appendChild(peers);
|
||||
//root.appendChild(button);
|
||||
/* >> Vuze: Info Button */
|
||||
root.appendChild(button2);
|
||||
/* << Vuze: Info Button */
|
||||
root.appendChild(progressbar.element);
|
||||
root.appendChild(details);
|
||||
|
||||
root._name_container = name;
|
||||
root._peer_details_container = peers;
|
||||
root._progress_details_container = details;
|
||||
root._progressbar = progressbar;
|
||||
//root._pause_resume_button_image = image;
|
||||
/* >> Vuze: Info Button */
|
||||
root._toggle_info_button = image2;
|
||||
/* << Vuze: Info Button */
|
||||
//root._toggle_running_button = button;
|
||||
|
||||
return root;
|
||||
},
|
||||
|
||||
getPeerDetails: function(t)
|
||||
{
|
||||
var err,
|
||||
peer_count,
|
||||
webseed_count,
|
||||
fmt = Transmission.fmt;
|
||||
|
||||
if ((err = t.getErrorMessage()))
|
||||
return err;
|
||||
|
||||
if (t.isDownloading())
|
||||
{
|
||||
peer_count = t.getPeersConnected();
|
||||
webseed_count = t.getWebseedsSendingToUs();
|
||||
|
||||
if (webseed_count && peer_count)
|
||||
{
|
||||
// Downloading from 2 of 3 peer(s) and 2 webseed(s)
|
||||
return [ 'Downloading from',
|
||||
t.getPeersSendingToUs(),
|
||||
'of',
|
||||
fmt.countString('peer','peers',peer_count),
|
||||
'and',
|
||||
fmt.countString('web seed','web seeds',webseed_count),
|
||||
'-',
|
||||
TorrentRendererHelper.formatDL(t),
|
||||
TorrentRendererHelper.formatUL(t) ].join(' ');
|
||||
}
|
||||
else if (webseed_count)
|
||||
{
|
||||
// Downloading from 2 webseed(s)
|
||||
return [ 'Downloading from',
|
||||
fmt.countString('web seed','web seeds',webseed_count),
|
||||
'-',
|
||||
TorrentRendererHelper.formatDL(t),
|
||||
TorrentRendererHelper.formatUL(t) ].join(' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
// Downloading from 2 of 3 peer(s)
|
||||
return [ 'Downloading from',
|
||||
t.getPeersSendingToUs(),
|
||||
'of',
|
||||
fmt.countString('peer','peers',peer_count),
|
||||
'-',
|
||||
TorrentRendererHelper.formatDL(t),
|
||||
TorrentRendererHelper.formatUL(t) ].join(' ');
|
||||
}
|
||||
}
|
||||
|
||||
if (t.isSeeding())
|
||||
return [ 'Seeding to',
|
||||
t.getPeersGettingFromUs(),
|
||||
'of',
|
||||
fmt.countString ('peer','peers',t.getPeersConnected()),
|
||||
'-',
|
||||
TorrentRendererHelper.formatUL(t) ].join(' ');
|
||||
|
||||
if (t.isChecking())
|
||||
return [ 'Verifying local data (',
|
||||
Transmission.fmt.percentString(100.0 * t.getRecheckProgress()),
|
||||
'% tested)' ].join('');
|
||||
|
||||
return t.getStateString();
|
||||
},
|
||||
|
||||
getProgressDetails: function(controller, t)
|
||||
{
|
||||
if (t.needsMetaData()) {
|
||||
var percent = 100 * t.getMetadataPercentComplete();
|
||||
return [ "Magnetized transfer - retrieving metadata (",
|
||||
Transmission.fmt.percentString(percent),
|
||||
"%)" ].join('');
|
||||
}
|
||||
|
||||
var c,
|
||||
sizeWhenDone = t.getSizeWhenDone(),
|
||||
totalSize = t.getTotalSize(),
|
||||
is_done = t.isDone() || t.isSeeding();
|
||||
|
||||
if (is_done) {
|
||||
if (totalSize === sizeWhenDone) // seed: '698.05 MiB'
|
||||
c = [ Transmission.fmt.size(totalSize) ];
|
||||
else // partial seed: '127.21 MiB of 698.05 MiB (18.2%)'
|
||||
c = [ Transmission.fmt.size(sizeWhenDone),
|
||||
' of ',
|
||||
Transmission.fmt.size(t.getTotalSize()),
|
||||
' (', t.getPercentDoneStr(), '%)' ];
|
||||
// append UL stats: ', uploaded 8.59 GiB (Ratio: 12.3)'
|
||||
c.push(', uploaded ',
|
||||
Transmission.fmt.size(t.getUploadedEver()),
|
||||
' (Ratio ',
|
||||
Transmission.fmt.ratioString(t.getUploadRatio()),
|
||||
')');
|
||||
} else { // not done yet
|
||||
c = [ Transmission.fmt.size(sizeWhenDone - t.getLeftUntilDone()),
|
||||
' of ', Transmission.fmt.size(sizeWhenDone),
|
||||
' (', t.getPercentDoneStr(), '%)' ];
|
||||
}
|
||||
|
||||
// maybe append eta
|
||||
if (!t.isStopped() && (!is_done || t.seedRatioLimit(controller)>0)) {
|
||||
c.push(' - ');
|
||||
var eta = t.getETA();
|
||||
if (eta < 0 || eta >= (999*60*60) /* arbitrary */)
|
||||
c.push('remaining time unknown');
|
||||
else
|
||||
c.push(Transmission.fmt.timeInterval(t.getETA()),
|
||||
' remaining');
|
||||
}
|
||||
|
||||
return c.join('');
|
||||
},
|
||||
|
||||
render: function(controller, t, root)
|
||||
{
|
||||
// name
|
||||
setTextContent(root._name_container, t.getName());
|
||||
|
||||
// progressbar
|
||||
TorrentRendererHelper.renderProgressbar(controller, t, root._progressbar);
|
||||
|
||||
// peer details
|
||||
var has_error = t.getError() !== Torrent._ErrNone;
|
||||
var e = root._peer_details_container;
|
||||
$(e).toggleClass('error',has_error);
|
||||
setTextContent(e, this.getPeerDetails(t));
|
||||
|
||||
// progress details
|
||||
e = root._progress_details_container;
|
||||
setTextContent(e, this.getProgressDetails(controller, t));
|
||||
|
||||
/*
|
||||
// pause/resume button
|
||||
var is_stopped = t.isStopped();
|
||||
e = root._pause_resume_button_image;
|
||||
// Vuze: Change Resume/Pause to Start/Stop
|
||||
e.alt = is_stopped ? 'Start' : 'Stop';
|
||||
e.className = is_stopped ? 'torrent_resume' : 'torrent_pause';
|
||||
*/
|
||||
}
|
||||
};
|
||||
|
||||
/****
|
||||
*****
|
||||
*****
|
||||
****/
|
||||
|
||||
function TorrentRendererCompact()
|
||||
{
|
||||
}
|
||||
TorrentRendererCompact.prototype =
|
||||
{
|
||||
createRow: function()
|
||||
{
|
||||
var progressbar, details, name, root;
|
||||
|
||||
progressbar = TorrentRendererHelper.createProgressbar('compact');
|
||||
|
||||
details = document.createElement('div');
|
||||
details.className = 'torrent_peer_details compact';
|
||||
|
||||
name = document.createElement('div');
|
||||
name.className = 'torrent_name compact';
|
||||
|
||||
root = document.createElement('li');
|
||||
root.appendChild(progressbar.element);
|
||||
root.appendChild(details);
|
||||
root.appendChild(name);
|
||||
root.className = 'torrent compact';
|
||||
root._progressbar = progressbar;
|
||||
root._details_container = details;
|
||||
root._name_container = name;
|
||||
return root;
|
||||
},
|
||||
|
||||
getPeerDetails: function(t)
|
||||
{
|
||||
var c;
|
||||
if ((c = t.getErrorMessage()))
|
||||
return c;
|
||||
if (t.isDownloading()) {
|
||||
var have_dn = t.getDownloadSpeed() > 0,
|
||||
have_up = t.getUploadSpeed() > 0;
|
||||
if (!have_up && !have_dn)
|
||||
return 'Idle';
|
||||
var s = '';
|
||||
if (have_dn)
|
||||
s += TorrentRendererHelper.formatDL(t);
|
||||
if (have_dn && have_up)
|
||||
s += ' '
|
||||
if (have_up)
|
||||
s += TorrentRendererHelper.formatUL(t);
|
||||
return s;
|
||||
}
|
||||
if (t.isSeeding())
|
||||
return [ 'Ratio: ',
|
||||
Transmission.fmt.ratioString(t.getUploadRatio()),
|
||||
', ',
|
||||
TorrentRendererHelper.formatUL(t) ].join('');
|
||||
return t.getStateString();
|
||||
},
|
||||
|
||||
render: function(controller, t, root)
|
||||
{
|
||||
// name
|
||||
var is_stopped = t.isStopped();
|
||||
var e = root._name_container;
|
||||
$(e).toggleClass('paused', is_stopped);
|
||||
setTextContent(e, t.getName());
|
||||
|
||||
// peer details
|
||||
var has_error = t.getError() !== Torrent._ErrNone;
|
||||
e = root._details_container;
|
||||
$(e).toggleClass('error', has_error);
|
||||
setTextContent(e, this.getPeerDetails(t));
|
||||
|
||||
// progressbar
|
||||
TorrentRendererHelper.renderProgressbar(controller, t, root._progressbar);
|
||||
}
|
||||
};
|
||||
|
||||
/****
|
||||
*****
|
||||
*****
|
||||
****/
|
||||
|
||||
function TorrentRow(view, controller, torrent)
|
||||
{
|
||||
this.initialize(view, controller, torrent);
|
||||
}
|
||||
TorrentRow.prototype =
|
||||
{
|
||||
initialize: function(view, controller, torrent) {
|
||||
var row = this;
|
||||
this._view = view;
|
||||
this._torrent = torrent;
|
||||
this._element = view.createRow();
|
||||
this.render(controller);
|
||||
$(this._torrent).bind('dataChanged.torrentRowListener',function(){row.render(controller);});
|
||||
|
||||
},
|
||||
getElement: function() {
|
||||
return this._element;
|
||||
},
|
||||
render: function(controller) {
|
||||
var tor = this.getTorrent();
|
||||
if (tor)
|
||||
this._view.render(controller, tor, this.getElement());
|
||||
},
|
||||
isSelected: function() {
|
||||
return this.getElement().className.indexOf('selected') !== -1;
|
||||
},
|
||||
|
||||
getTorrent: function() {
|
||||
return this._torrent;
|
||||
},
|
||||
getTorrentId: function() {
|
||||
return this.getTorrent().getId();
|
||||
}
|
||||
};
|
529
src/jsp/web/javascript/torrent.js
Normal file
@ -0,0 +1,529 @@
|
||||
/* 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 Torrent(data, onDataChangeCB)
|
||||
{
|
||||
this.initialize(data, onDataChangeCB);
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
**** Constants
|
||||
****
|
||||
***/
|
||||
|
||||
// Torrent.fields.status
|
||||
Torrent._StatusStopped = 0;
|
||||
Torrent._StatusCheckWait = 1;
|
||||
Torrent._StatusCheck = 2;
|
||||
Torrent._StatusDownloadWait = 3;
|
||||
Torrent._StatusDownload = 4;
|
||||
Torrent._StatusSeedWait = 5;
|
||||
Torrent._StatusSeed = 6;
|
||||
|
||||
// Torrent.fields.seedRatioMode
|
||||
Torrent._RatioUseGlobal = 0;
|
||||
Torrent._RatioUseLocal = 1;
|
||||
Torrent._RatioUnlimited = 2;
|
||||
|
||||
// Torrent.fields.error
|
||||
Torrent._ErrNone = 0;
|
||||
Torrent._ErrTrackerWarning = 1;
|
||||
Torrent._ErrTrackerError = 2;
|
||||
Torrent._ErrLocalError = 3;
|
||||
|
||||
// TrackerStats' announceState
|
||||
Torrent._TrackerInactive = 0;
|
||||
Torrent._TrackerWaiting = 1;
|
||||
Torrent._TrackerQueued = 2;
|
||||
Torrent._TrackerActive = 3;
|
||||
|
||||
|
||||
Torrent.Fields = { };
|
||||
|
||||
// commonly used fields which only need to be loaded once,
|
||||
// either on startup or when a magnet finishes downloading its metadata
|
||||
// finishes downloading its metadata
|
||||
Torrent.Fields.Metadata = [
|
||||
'addedDate',
|
||||
'name',
|
||||
'totalSize'
|
||||
];
|
||||
|
||||
// commonly used fields which need to be periodically refreshed
|
||||
Torrent.Fields.Stats = [
|
||||
'error',
|
||||
'errorString',
|
||||
'eta',
|
||||
'isFinished',
|
||||
'isStalled',
|
||||
'leftUntilDone',
|
||||
'metadataPercentComplete',
|
||||
'peersConnected',
|
||||
'peersGettingFromUs',
|
||||
'peersSendingToUs',
|
||||
'percentDone',
|
||||
'queuePosition',
|
||||
'rateDownload',
|
||||
'rateUpload',
|
||||
'recheckProgress',
|
||||
'seedRatioMode',
|
||||
'seedRatioLimit',
|
||||
'sizeWhenDone',
|
||||
'status',
|
||||
'trackers',
|
||||
'downloadDir',
|
||||
'uploadedEver',
|
||||
'uploadRatio',
|
||||
'webseedsSendingToUs'
|
||||
];
|
||||
|
||||
// fields used by the inspector which only need to be loaded once
|
||||
Torrent.Fields.InfoExtra = [
|
||||
'comment',
|
||||
'creator',
|
||||
'dateCreated',
|
||||
'files',
|
||||
'hashString',
|
||||
'isPrivate',
|
||||
'pieceCount',
|
||||
'pieceSize'
|
||||
];
|
||||
|
||||
// fields used in the inspector which need to be periodically refreshed
|
||||
Torrent.Fields.StatsExtra = [
|
||||
'activityDate',
|
||||
'activityDateRelative', // Vuze: relative time!
|
||||
'corruptEver',
|
||||
'desiredAvailable',
|
||||
'downloadedEver',
|
||||
'fileStats',
|
||||
'haveUnchecked',
|
||||
'haveValid',
|
||||
'peers',
|
||||
'startDate',
|
||||
'trackerStats',
|
||||
'tag-uids'
|
||||
];
|
||||
|
||||
/***
|
||||
****
|
||||
**** Methods
|
||||
****
|
||||
***/
|
||||
|
||||
Torrent.prototype =
|
||||
{
|
||||
initialize: function(data, onDataChangeCB)
|
||||
{
|
||||
if (onDataChangeCB !== undefined) {
|
||||
$(this).bind('dataChanged',onDataChangeCB);
|
||||
}
|
||||
|
||||
this.fields = {};
|
||||
this.fieldObservers = {};
|
||||
this.refresh (data);
|
||||
},
|
||||
|
||||
notifyOnFieldChange: function(field, callback) {
|
||||
this.fieldObservers[field] = this.fieldObservers[field] || [];
|
||||
this.fieldObservers[field].push(callback);
|
||||
},
|
||||
|
||||
setField: function(o, name, value)
|
||||
{
|
||||
var i, observer;
|
||||
|
||||
if (o[name] === value)
|
||||
return false;
|
||||
if (o == this.fields && this.fieldObservers[name] && this.fieldObservers[name].length) {
|
||||
for (i=0; observer=this.fieldObservers[name][i]; ++i) {
|
||||
observer.call(this, value, o[name], name);
|
||||
}
|
||||
}
|
||||
o[name] = value;
|
||||
return true;
|
||||
},
|
||||
|
||||
// fields.files is an array of unions of RPC's "files" and "fileStats" objects.
|
||||
updateFiles: function(files)
|
||||
{
|
||||
var changed = false,
|
||||
myfiles = this.fields.files || [],
|
||||
keys = [ 'length', 'name', 'bytesCompleted', 'wanted', 'priority' ],
|
||||
i, f, j, key, myfile;
|
||||
|
||||
for (i=0; f=files[i]; ++i) {
|
||||
myfile = myfiles[i] || {};
|
||||
for (j=0; key=keys[j]; ++j)
|
||||
if(key in f)
|
||||
changed |= this.setField(myfile,key,f[key]);
|
||||
myfiles[i] = myfile;
|
||||
}
|
||||
this.fields.files = myfiles;
|
||||
return changed;
|
||||
},
|
||||
|
||||
collateTrackers: function(trackers)
|
||||
{
|
||||
var i, t, announces = [];
|
||||
|
||||
for (i=0; t=trackers[i]; ++i)
|
||||
announces.push(t.announce.toLowerCase());
|
||||
return announces.join('\t');
|
||||
},
|
||||
|
||||
refreshFields: function(data)
|
||||
{
|
||||
var key,
|
||||
changed = false;
|
||||
|
||||
for (key in data) {
|
||||
switch (key) {
|
||||
case 'files':
|
||||
case 'fileStats': // merge files and fileStats together
|
||||
changed |= this.updateFiles(data[key]);
|
||||
break;
|
||||
case 'trackerStats': // 'trackerStats' is a superset of 'trackers'...
|
||||
changed |= this.setField(this.fields,'trackers',data[key]);
|
||||
break;
|
||||
case 'trackers': // ...so only save 'trackers' if we don't have it already
|
||||
if (!(key in this.fields))
|
||||
changed |= this.setField(this.fields,key,data[key]);
|
||||
break;
|
||||
default:
|
||||
changed |= this.setField(this.fields,key,data[key]);
|
||||
}
|
||||
}
|
||||
|
||||
//console.log("refreshFields: " + changed);
|
||||
return changed;
|
||||
},
|
||||
|
||||
refresh: function(data)
|
||||
{
|
||||
if (this.refreshFields(data))
|
||||
$(this).trigger('dataChanged', this);
|
||||
},
|
||||
|
||||
/****
|
||||
*****
|
||||
****/
|
||||
|
||||
testState: function(state)
|
||||
{
|
||||
var s = this.getStatus();
|
||||
|
||||
switch(state)
|
||||
{
|
||||
case Prefs._FilterActive:
|
||||
return this.getPeersGettingFromUs() > 0
|
||||
|| this.getPeersSendingToUs() > 0
|
||||
|| this.getWebseedsSendingToUs() > 0
|
||||
|| this.isChecking();
|
||||
case Prefs._FilterSeeding:
|
||||
return (s === Torrent._StatusSeed)
|
||||
|| (s === Torrent._StatusSeedWait);
|
||||
case Prefs._FilterDownloading:
|
||||
return (s === Torrent._StatusDownload)
|
||||
|| (s === Torrent._StatusDownloadWait);
|
||||
case Prefs._FilterPaused:
|
||||
return this.isStopped();
|
||||
case Prefs._FilterFinished:
|
||||
return this.isFinished();
|
||||
case Prefs._FilterComplete:
|
||||
return this.isDone();
|
||||
case Prefs._FilterIncomplete:
|
||||
return !this.isDone();
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
// simple accessors
|
||||
getComment: function() { return this.fields.comment; },
|
||||
getCreator: function() { return this.fields.creator; },
|
||||
getDateAdded: function() { return this.fields.addedDate; },
|
||||
getDateCreated: function() { return this.fields.dateCreated; },
|
||||
getDesiredAvailable: function() { return this.fields.desiredAvailable; },
|
||||
getDownloadDir: function() { return this.fields.downloadDir; },
|
||||
getDownloadSpeed: function() { return this.fields.rateDownload; },
|
||||
getDownloadedEver: function() { return this.fields.downloadedEver; },
|
||||
getError: function() { return this.fields.error; },
|
||||
getErrorString: function() { return this.fields.errorString; },
|
||||
getETA: function() { return this.fields.eta; },
|
||||
getFailedEver: function(i) { return this.fields.corruptEver; },
|
||||
getFile: function(i) { return this.fields.files[i]; },
|
||||
getFileCount: function() { return this.fields.files ? this.fields.files.length : 0; },
|
||||
getHashString: function() { return this.fields.hashString; },
|
||||
getHave: function() { return this.getHaveValid() + this.getHaveUnchecked() },
|
||||
getHaveUnchecked: function() { return this.fields.haveUnchecked; },
|
||||
getHaveValid: function() { return this.fields.haveValid; },
|
||||
getId: function() { return this.fields.id; },
|
||||
/** Vuze: Use activityDateRelative if exists */
|
||||
getLastActivity: function() { return ("activityDateRelative" in this.fields) ? (Date.now() / 1000) + this.fields.activityDateRelative : this.fields.activityDate; },
|
||||
getLeftUntilDone: function() { return this.fields.leftUntilDone; },
|
||||
getMetadataPercentComplete: function() { return this.fields.metadataPercentComplete; },
|
||||
getName: function() { return this.fields.name || 'Unknown'; },
|
||||
getPeers: function() { return this.fields.peers; },
|
||||
getPeersConnected: function() { return this.fields.peersConnected; },
|
||||
getPeersGettingFromUs: function() { return this.fields.peersGettingFromUs; },
|
||||
getPeersSendingToUs: function() { return this.fields.peersSendingToUs; },
|
||||
getPieceCount: function() { return this.fields.pieceCount; },
|
||||
getPieceSize: function() { return this.fields.pieceSize; },
|
||||
getPrivateFlag: function() { return this.fields.isPrivate; },
|
||||
getQueuePosition: function() { return this.fields.queuePosition; },
|
||||
getRecheckProgress: function() { return this.fields.recheckProgress; },
|
||||
getSeedRatioLimit: function() { return this.fields.seedRatioLimit; },
|
||||
getSeedRatioMode: function() { return this.fields.seedRatioMode; },
|
||||
getSizeWhenDone: function() { return this.fields.sizeWhenDone; },
|
||||
getStartDate: function() { return this.fields.startDate; },
|
||||
getStatus: function() { return this.fields.status; },
|
||||
/** >> Vuze */
|
||||
getTagUIDs: function() { return this.fields['tag-uids']; },
|
||||
/** << Vuze */
|
||||
getTotalSize: function() { return this.fields.totalSize; },
|
||||
getTrackers: function() { return this.fields.trackers; },
|
||||
getUploadSpeed: function() { return this.fields.rateUpload; },
|
||||
getUploadRatio: function() { return this.fields.uploadRatio; },
|
||||
getUploadedEver: function() { return this.fields.uploadedEver; },
|
||||
getWebseedsSendingToUs: function() { return this.fields.webseedsSendingToUs; },
|
||||
isFinished: function() { return this.fields.isFinished; },
|
||||
|
||||
// derived accessors
|
||||
hasExtraInfo: function() { return 'hashString' in this.fields; },
|
||||
/* >> Vuze: Not sure if used/needed */
|
||||
isQueued: function() { return ( this.state() == Torrent._StatusSeedWait ) || ( this.state() == Torrent._StatusDownloadWait ); },
|
||||
/* << Vuze */
|
||||
isSeeding: function() { return this.getStatus() === Torrent._StatusSeed; },
|
||||
isStopped: function() { return this.getStatus() === Torrent._StatusStopped; },
|
||||
isChecking: function() { return this.getStatus() === Torrent._StatusCheck; },
|
||||
isDownloading: function() { return this.getStatus() === Torrent._StatusDownload; },
|
||||
isDone: function() { return this.getLeftUntilDone() < 1; },
|
||||
needsMetaData: function(){ return this.getMetadataPercentComplete() < 1; },
|
||||
getActivity: function() { return this.getDownloadSpeed() + this.getUploadSpeed(); },
|
||||
getPercentDoneStr: function() { return Transmission.fmt.percentString(100*this.getPercentDone()); },
|
||||
getPercentDone: function() { return this.fields.percentDone; },
|
||||
getStateString: function() {
|
||||
switch(this.getStatus()) {
|
||||
case Torrent._StatusStopped: return this.isFinished() ? 'Seeding complete' : 'Stopped' // Vuze: Paused -> Stopped
|
||||
case Torrent._StatusCheckWait: return 'Queued for verification';
|
||||
case Torrent._StatusCheck: return 'Verifying local data';
|
||||
case Torrent._StatusDownloadWait: return 'Queued for download';
|
||||
case Torrent._StatusDownload: return 'Downloading';
|
||||
case Torrent._StatusSeedWait: return 'Queued for seeding';
|
||||
case Torrent._StatusSeed: return 'Seeding';
|
||||
case null:
|
||||
case undefined: return 'Unknown';
|
||||
default: return 'Error';
|
||||
}
|
||||
},
|
||||
seedRatioLimit: function(controller){
|
||||
switch(this.getSeedRatioMode()) {
|
||||
case Torrent._RatioUseGlobal: return controller.seedRatioLimit();
|
||||
case Torrent._RatioUseLocal: return this.getSeedRatioLimit();
|
||||
default: return -1;
|
||||
}
|
||||
},
|
||||
getErrorMessage: function() {
|
||||
var str = this.getErrorString();
|
||||
switch(this.getError()) {
|
||||
case Torrent._ErrTrackerWarning:
|
||||
return 'Tracker returned a warning: ' + str;
|
||||
case Torrent._ErrTrackerError:
|
||||
return 'Tracker returned an error: ' + str;
|
||||
case Torrent._ErrLocalError:
|
||||
return 'Error: ' + str;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
getCollatedName: function() {
|
||||
var f = this.fields;
|
||||
if (!f.collatedName && f.name)
|
||||
f.collatedName = f.name.toLowerCase();
|
||||
return f.collatedName || '';
|
||||
},
|
||||
getCollatedTrackers: function() {
|
||||
var f = this.fields;
|
||||
if (!f.collatedTrackers && f.trackers)
|
||||
f.collatedTrackers = this.collateTrackers(f.trackers);
|
||||
return f.collatedTrackers || '';
|
||||
},
|
||||
|
||||
/****
|
||||
*****
|
||||
****/
|
||||
|
||||
/**
|
||||
* @param filter one of Prefs._Filter*
|
||||
* @param search substring to look for, or null
|
||||
* @return true if it passes the test, false if it fails
|
||||
*/
|
||||
test: function(state, search, tracker)
|
||||
{
|
||||
// flter by state...
|
||||
var pass = this.testState(state);
|
||||
|
||||
// maybe filter by text...
|
||||
if (pass && search && search.length)
|
||||
pass = this.getCollatedName().indexOf(search.toLowerCase()) !== -1;
|
||||
|
||||
// maybe filter by tracker...
|
||||
if (pass && tracker && tracker.length)
|
||||
pass = this.getCollatedTrackers().indexOf(tracker) !== -1;
|
||||
|
||||
return pass;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/***
|
||||
****
|
||||
**** SORTING
|
||||
****
|
||||
***/
|
||||
|
||||
Torrent.compareById = function(ta, tb)
|
||||
{
|
||||
return ta.getId() - tb.getId();
|
||||
};
|
||||
Torrent.compareByName = function(ta, tb)
|
||||
{
|
||||
return ta.getCollatedName().localeCompare(tb.getCollatedName())
|
||||
|| Torrent.compareById(ta, tb);
|
||||
};
|
||||
Torrent.compareByQueue = function(ta, tb)
|
||||
{
|
||||
return ta.getQueuePosition() - tb.getQueuePosition();
|
||||
};
|
||||
Torrent.compareByAge = function(ta, tb)
|
||||
{
|
||||
var a = ta.getDateAdded(),
|
||||
b = tb.getDateAdded();
|
||||
|
||||
return (b - a) || Torrent.compareByQueue(ta, tb);
|
||||
};
|
||||
Torrent.compareByState = function(ta, tb)
|
||||
{
|
||||
var a = ta.getStatus(),
|
||||
b = tb.getStatus();
|
||||
|
||||
return (b - a) || Torrent.compareByQueue(ta, tb);
|
||||
};
|
||||
Torrent.compareByActivity = function(ta, tb)
|
||||
{
|
||||
var a = ta.getActivity(),
|
||||
b = tb.getActivity();
|
||||
|
||||
return (b - a) || Torrent.compareByState(ta, tb);
|
||||
};
|
||||
Torrent.compareByRatio = function(ta, tb)
|
||||
{
|
||||
var a = ta.getUploadRatio(),
|
||||
b = tb.getUploadRatio();
|
||||
|
||||
if (a < b) return 1;
|
||||
if (a > b) return -1;
|
||||
return Torrent.compareByState(ta, tb);
|
||||
};
|
||||
Torrent.compareByProgress = function(ta, tb)
|
||||
{
|
||||
var a = ta.getPercentDone(),
|
||||
b = tb.getPercentDone();
|
||||
|
||||
return (a - b) || Torrent.compareByRatio(ta, tb);
|
||||
};
|
||||
|
||||
Torrent.compareBySize = function(ta, tb)
|
||||
{
|
||||
var a = ta.getTotalSize(),
|
||||
b = tb.getTotalSize();
|
||||
|
||||
return (a - b) || Torrent.compareByName(ta, tb);
|
||||
}
|
||||
|
||||
Torrent.compareTorrents = function(a, b, sortMethod, sortDirection)
|
||||
{
|
||||
var i;
|
||||
|
||||
switch(sortMethod)
|
||||
{
|
||||
case Prefs._SortByActivity:
|
||||
i = Torrent.compareByActivity(a,b);
|
||||
break;
|
||||
case Prefs._SortByAge:
|
||||
i = Torrent.compareByAge(a,b);
|
||||
break;
|
||||
case Prefs._SortByQueue:
|
||||
i = Torrent.compareByQueue(a,b);
|
||||
break;
|
||||
case Prefs._SortByProgress:
|
||||
i = Torrent.compareByProgress(a,b);
|
||||
break;
|
||||
case Prefs._SortBySize:
|
||||
i = Torrent.compareBySize(a,b);
|
||||
break;
|
||||
case Prefs._SortByState:
|
||||
i = Torrent.compareByState(a,b);
|
||||
break;
|
||||
case Prefs._SortByRatio:
|
||||
i = Torrent.compareByRatio(a,b);
|
||||
break;
|
||||
default:
|
||||
i = Torrent.compareByName(a,b);
|
||||
break;
|
||||
}
|
||||
|
||||
if (sortDirection === Prefs._SortDescending)
|
||||
i = -i;
|
||||
|
||||
return i;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param torrents an array of Torrent objects
|
||||
* @param sortMethod one of Prefs._SortBy*
|
||||
* @param sortDirection Prefs._SortAscending or Prefs._SortDescending
|
||||
*/
|
||||
Torrent.sortTorrents = function(torrents, sortMethod, sortDirection)
|
||||
{
|
||||
switch(sortMethod)
|
||||
{
|
||||
case Prefs._SortByActivity:
|
||||
torrents.sort(this.compareByActivity);
|
||||
break;
|
||||
case Prefs._SortByAge:
|
||||
torrents.sort(this.compareByAge);
|
||||
break;
|
||||
case Prefs._SortByQueue:
|
||||
torrents.sort(this.compareByQueue);
|
||||
break;
|
||||
case Prefs._SortByProgress:
|
||||
torrents.sort(this.compareByProgress);
|
||||
break;
|
||||
case Prefs._SortBySize:
|
||||
torrents.sort(this.compareBySize);
|
||||
break;
|
||||
case Prefs._SortByState:
|
||||
torrents.sort(this.compareByState);
|
||||
break;
|
||||
case Prefs._SortByRatio:
|
||||
torrents.sort(this.compareByRatio);
|
||||
break;
|
||||
default:
|
||||
torrents.sort(this.compareByName);
|
||||
break;
|
||||
}
|
||||
|
||||
if (sortDirection === Prefs._SortDescending)
|
||||
torrents.reverse();
|
||||
|
||||
return torrents;
|
||||
};
|
2156
src/jsp/web/javascript/transmission.js
Normal file
409
src/jsp/web/javascript/vuze.js
Normal file
@ -0,0 +1,409 @@
|
||||
var vz = window.vz || {};
|
||||
|
||||
vz.mode = "trial";
|
||||
vz.lastSelectionChanged = "";
|
||||
vz.lastTorrenStates = "";
|
||||
|
||||
vz.updatePrefs = function( prefs ){
|
||||
var az_mode = prefs["az-mode"];
|
||||
if ( typeof az_mode == 'undefined' ){
|
||||
vz.mode = "trial";
|
||||
}else{
|
||||
vz.mode = az_mode;
|
||||
}
|
||||
};
|
||||
|
||||
vz.searchQuery = null;
|
||||
|
||||
vz.validateSearch = function(str){
|
||||
if(!str || str == "" || str == "find...") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
vz.executeSearch = function(search_input){
|
||||
if (typeof search_input === 'undefined') {
|
||||
search_input = $("#search_input").get(0).value;
|
||||
}
|
||||
if(! vz.validateSearch( search_input ) ) return;
|
||||
|
||||
if (vz.hasExternalOSFunctions()) {
|
||||
try {
|
||||
if (externalOSFunctions.executeSearch(search_input)) {
|
||||
return true;
|
||||
}
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
var search_url;
|
||||
if (window.location.href.lastIndexOf("file:", 0) === 0) {
|
||||
var root_url = $.url(RPC._Root);
|
||||
var search_source = root_url.attr("source").substring(0, root_url.attr("source").length - root_url.attr("relative").length + 1);
|
||||
search_url = "http://search.vuze.com/xsearch/?q=" + encodeURIComponent(search_input) + "&xdmv=no&source=android&search_source=" + encodeURIComponent(search_source);
|
||||
$("#remotesearch_container").html("<iframe id='remotesearch'></iframe>");
|
||||
$("#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);
|
||||
}
|
BIN
src/jsp/web/style/jqueryui/images/animated-overlay.gif
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
src/jsp/web/style/jqueryui/images/ui-bg_flat_0_aaaaaa_40x100.png
Normal file
After Width: | Height: | Size: 212 B |
After Width: | Height: | Size: 208 B |
After Width: | Height: | Size: 335 B |
After Width: | Height: | Size: 207 B |
After Width: | Height: | Size: 262 B |
After Width: | Height: | Size: 262 B |
After Width: | Height: | Size: 332 B |
After Width: | Height: | Size: 280 B |
BIN
src/jsp/web/style/jqueryui/images/ui-icons_222222_256x240.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
src/jsp/web/style/jqueryui/images/ui-icons_2e83ff_256x240.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
src/jsp/web/style/jqueryui/images/ui-icons_454545_256x240.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
src/jsp/web/style/jqueryui/images/ui-icons_888888_256x240.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
src/jsp/web/style/jqueryui/images/ui-icons_cd0a0a_256x240.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
1188
src/jsp/web/style/jqueryui/jquery-ui-1.10.3.css
vendored
Normal file
7
src/jsp/web/style/jqueryui/jquery-ui-1.10.3.custom.min.css
vendored
Normal file
294
src/jsp/web/style/transmission/common.css
Normal file
@ -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; }
|
1057
src/jsp/web/style/transmission/common.scss
Normal file
BIN
src/jsp/web/style/transmission/images/arrow-down.png
Normal file
After Width: | Height: | Size: 179 B |
BIN
src/jsp/web/style/transmission/images/arrow-up.png
Normal file
After Width: | Height: | Size: 179 B |
BIN
src/jsp/web/style/transmission/images/blank.gif
Normal file
After Width: | Height: | Size: 49 B |
BIN
src/jsp/web/style/transmission/images/buttons/cancel.png
Normal file
After Width: | Height: | Size: 426 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 6.5 KiB |
BIN
src/jsp/web/style/transmission/images/file-priority-high.png
Normal file
After Width: | Height: | Size: 340 B |
BIN
src/jsp/web/style/transmission/images/file-priority-low.png
Normal file
After Width: | Height: | Size: 335 B |
BIN
src/jsp/web/style/transmission/images/file-priority-normal.png
Normal file
After Width: | Height: | Size: 185 B |
BIN
src/jsp/web/style/transmission/images/filter_icon.png
Normal file
After Width: | Height: | Size: 446 B |
After Width: | Height: | Size: 709 B |
After Width: | Height: | Size: 2.8 KiB |
BIN
src/jsp/web/style/transmission/images/inspector-activity.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/jsp/web/style/transmission/images/inspector-files.png
Normal file
After Width: | Height: | Size: 358 B |
BIN
src/jsp/web/style/transmission/images/inspector-info.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
src/jsp/web/style/transmission/images/inspector-peers.png
Normal file
After Width: | Height: | Size: 425 B |
BIN
src/jsp/web/style/transmission/images/inspector-trackers.png
Normal file
After Width: | Height: | Size: 515 B |
BIN
src/jsp/web/style/transmission/images/lock_icon.png
Normal file
After Width: | Height: | Size: 456 B |
BIN
src/jsp/web/style/transmission/images/logo.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
src/jsp/web/style/transmission/images/progress.png
Normal file
After Width: | Height: | Size: 938 B |
BIN
src/jsp/web/style/transmission/images/row-info-active.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
src/jsp/web/style/transmission/images/row-info.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
src/jsp/web/style/transmission/images/settings.png
Normal file
After Width: | Height: | Size: 319 B |
BIN
src/jsp/web/style/transmission/images/toolbar-add.png
Normal file
After Width: | Height: | Size: 231 B |