Initial checkin, v0.1

This commit is contained in:
zzz
2017-03-20 16:51:17 +00:00
commit 94ed23e74b
118 changed files with 20183 additions and 0 deletions

2
CHANGES.txt Normal file
View File

@ -0,0 +1,2 @@
* 2017-03-20 0.1
- Initial version

23
LICENSE.txt Normal file
View 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
View 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
View 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
View 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>

View 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.

View 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
View 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
View 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
View 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>

View File

@ -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";
}

View File

@ -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);
}
}

View 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();
}
}

View 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( "]" );
}
}

View 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
}
}
}

View 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 "";
}
}

View 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;
}
}

View 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();
}
}
}
}
}
}

View 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+"|");
}
}

View File

@ -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 );
}

View 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));
}
}

View 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;
}
}
}

View 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();
}
}

View 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);
}
}

View 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();
}
}

View 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" };
}

File diff suppressed because it is too large Load Diff

30
src/jsp/WEB-INF/web.xml Normal file
View 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
View 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
View 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>

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

461
src/jsp/web/index.html Normal file
View 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">&nbsp;</div>
<div id="compact-button" title="Compact View">&nbsp;</div>
<div id="prefs-button" title="Edit Preferences...">&nbsp;</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">&nbsp;</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 &quot;.az!&quot; 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">&nbsp;</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">&nbsp;</div></div>
<div class="row"><div class="key">Availability:</div><div class="value" id="inspector-info-availability">&nbsp;</div></div>
<div class="row"><div class="key">Downloaded:</div><div class="value" id="inspector-info-downloaded">&nbsp;</div></div>
<div class="row"><div class="key">Uploaded:</div><div class="value" id="inspector-info-uploaded">&nbsp;</div></div>
<div class="row"><div class="key">State:</div><div class="value" id="inspector-info-state">&nbsp;</div></div>
<div class="row"><div class="key">Running Time:</div><div class="value" id="inspector-info-running-time">&nbsp;</div></div>
<div class="row"><div class="key">Remaining Time:</div><div class="value" id="inspector-info-remaining-time">&nbsp;</div></div>
<div class="row"><div class="key">Last Activity:</div><div class="value" id="inspector-info-last-activity">&nbsp;</div></div>
<div class="row"><div class="key">Error:</div><div class="value" id="inspector-info-error">&nbsp;</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">&nbsp;</div></div>
<div class="row"><div class="key">Location:</div><div class="value" id="inspector-info-location">&nbsp;</div></div>
<div class="row"><div class="key">Hash:</div><div class="value" id="inspector-info-hash">&nbsp;</div></div>
<div class="row"><div class="key">Privacy:</div><div class="value" id="inspector-info-privacy">&nbsp;</div></div>
<div class="row"><div class="key">Origin:</div><div class="value" id="inspector-info-origin">&nbsp;</div></div>
<div class="row"><div class="key">Comment:</div><div class="value" id="inspector-info-comment">&nbsp;</div></div>
<div class="row"><div class="key">Tags:</div><div class="value" id="inspector-info-tags">&nbsp;</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'>&nbsp;</div></div>
<div class="row"><div class="key">Downloaded:</div><div class="value" id='stats-session-downloaded'>&nbsp;</div></div>
<div class="row"><div class="key">Ratio:</div><div class="value" id='stats-session-ratio'>&nbsp;</div></div>
<div class="row"><div class="key">Running Time:</div><div class="value" id='stats-session-duration'>&nbsp;</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'>&nbsp;</div></div>
<div class="row"><div class="key">Uploaded:</div><div class="value" id='stats-total-uploaded'>&nbsp;</div></div>
<div class="row"><div class="key">Downloaded:</div><div class="value" id='stats-total-downloaded'>&nbsp;</div></div>
<div class="row"><div class="key">Ratio:</div><div class="value" id='stats-total-ratio'>&nbsp;</div></div>
<div class="row"><div class="key">Running Time:</div><div class="value" id='stats-total-duration'>&nbsp;</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 &amp; 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>

View 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, "&infin;" 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, "&lt;").replace(/>/g, "&gt;");
};
/**
* 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*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
}
};

View 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();
}
}

File diff suppressed because one or more lines are too long

Binary file not shown.

View 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);
};

View 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 '&infin;';
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 );
};

View 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);
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View 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 );
}));

View 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
View 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;
});

View 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'}
}
}

View 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();
}
};
});

View 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);
};

View 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);
}
};

View 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();
}
};

View 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;
};

File diff suppressed because it is too large Load Diff

View 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);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View 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; }

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 446 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 709 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 515 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 938 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 B

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