Browse Source

Merge pull request #2284 from pmzqla/webui-features

WebUI changes
adaptive-webui-19844
sledgehammer999 10 years ago
parent
commit
3d7ca1da77
  1. 19
      src/webui/btjson.cpp
  2. 2
      src/webui/httprequestparser.cpp
  3. 101
      src/webui/requesthandler.cpp
  4. 2
      src/webui/requesthandler.h
  5. 3
      src/webui/webapplication.cpp
  6. 11
      src/webui/www/private/index.html
  7. 4
      src/webui/www/public/css/Layout.css
  8. 81
      src/webui/www/public/downloadlimit.html
  9. 12
      src/webui/www/public/filters.html
  10. 295
      src/webui/www/public/scripts/client.js
  11. 1
      src/webui/www/public/scripts/contextmenu.js
  12. 4
      src/webui/www/public/scripts/mocha-init.js
  13. 2
      src/webui/www/public/scripts/mocha-yc.js
  14. 10
      src/webui/www/public/scripts/mocha.js
  15. 81
      src/webui/www/public/uploadlimit.html

19
src/webui/btjson.cpp

@ -140,6 +140,8 @@ static const char KEY_TRANSFER_DLRATELIMIT[] = "dl_rate_limit"; @@ -140,6 +140,8 @@ static const char KEY_TRANSFER_DLRATELIMIT[] = "dl_rate_limit";
static const char KEY_TRANSFER_UPSPEED[] = "up_info_speed";
static const char KEY_TRANSFER_UPDATA[] = "up_info_data";
static const char KEY_TRANSFER_UPRATELIMIT[] = "up_rate_limit";
static const char KEY_TRANSFER_DHT_NODES[] = "dht_nodes";
static const char KEY_TRANSFER_CONNECTION_STATUS[] = "connection_status";
class QTorrentCompare
{
@ -213,7 +215,7 @@ static QVariantMap toMap(const QTorrentHandle& h) @@ -213,7 +215,7 @@ static QVariantMap toMap(const QTorrentHandle& h)
ret[KEY_TORRENT_LEECHS] = status.num_peers - status.num_seeds;
ret[KEY_TORRENT_NUM_INCOMPLETE] = status.num_incomplete;
const qreal ratio = QBtSession::instance()->getRealRatio(status);
ret[KEY_TORRENT_RATIO] = (ratio > 100.) ? -1 : ratio;
ret[KEY_TORRENT_RATIO] = (ratio > QBtSession::MAX_RATIO) ? -1 : ratio;
ret[KEY_TORRENT_STATE] = h.torrentState().toString();
ret[KEY_TORRENT_ETA] = h.eta();
if (h.has_metadata()) {
@ -245,6 +247,8 @@ static QVariantMap toMap(const QTorrentHandle& h) @@ -245,6 +247,8 @@ static QVariantMap toMap(const QTorrentHandle& h)
* - "ratio": Torrent share ratio
* - "eta": Torrent ETA
* - "state": Torrent state
* - "seq_dl": Torrent sequential download state
* - "f_l_piece_prio": Torrent first last piece priority state
*/
QByteArray btjson::getTorrents(QString filter, QString label,
QString sortedColumn, bool reverse, int limit, int offset)
@ -381,7 +385,7 @@ QByteArray btjson::getPropertiesForTorrent(const QString& hash) @@ -381,7 +385,7 @@ QByteArray btjson::getPropertiesForTorrent(const QString& hash)
data[KEY_PROP_CONNECT_COUNT] = status.num_connections;
data[KEY_PROP_CONNECT_COUNT_LIMIT] = status.connections_limit;
const qreal ratio = QBtSession::instance()->getRealRatio(status);
data[KEY_PROP_RATIO] = ratio > 100. ? -1 : ratio;
data[KEY_PROP_RATIO] = ratio > QBtSession::MAX_RATIO ? -1 : ratio;
}
catch(const std::exception& e) {
qWarning() << Q_FUNC_INFO << "Invalid torrent: " << misc::toQStringU(e.what());
@ -446,10 +450,14 @@ QByteArray btjson::getFilesForTorrent(const QString& hash) @@ -446,10 +450,14 @@ QByteArray btjson::getFilesForTorrent(const QString& hash)
* - "dl_info_data": Data downloaded this session
* - "up_info_speed": Global upload rate
* - "up_info_data": Data uploaded this session
* - "dl_rate_limit": Download rate limit
* - "up_rate_limit": Upload rate limit
* - "dht_nodes": DHT nodes connected to
* - "connection_status": Connection status
*/
QByteArray btjson::getTransferInfo()
{
CACHED_VARIABLE(QVariantMap, info, CACHE_DURATION_MS);
QVariantMap info;
session_status sessionStatus = QBtSession::instance()->getSessionStatus();
session_settings sessionSettings = QBtSession::instance()->getSession()->settings();
info[KEY_TRANSFER_DLSPEED] = sessionStatus.payload_download_rate;
@ -460,5 +468,10 @@ QByteArray btjson::getTransferInfo() @@ -460,5 +468,10 @@ QByteArray btjson::getTransferInfo()
info[KEY_TRANSFER_DLRATELIMIT] = sessionSettings.download_rate_limit;
if (sessionSettings.upload_rate_limit)
info[KEY_TRANSFER_UPRATELIMIT] = sessionSettings.upload_rate_limit;
info[KEY_TRANSFER_DHT_NODES] = sessionStatus.dht_nodes;
if (!QBtSession::instance()->getSession()->is_listening())
info[KEY_TRANSFER_CONNECTION_STATUS] = "disconnected";
else
info[KEY_TRANSFER_CONNECTION_STATUS] = sessionStatus.has_incoming_connections ? "connected" : "firewalled";
return json::toJson(info);
}

2
src/webui/httprequestparser.cpp

@ -243,7 +243,7 @@ bool HttpRequestParser::parseContent(const QByteArray& data) @@ -243,7 +243,7 @@ bool HttpRequestParser::parseContent(const QByteArray& data)
while (i.hasNext())
{
QPair<QString, QString> pair = i.next();
request_.posts[pair.first] = pair.second;
request_.posts[pair.first.toLower()] = pair.second;
}
return true;

101
src/webui/requesthandler.cpp

@ -100,6 +100,8 @@ QMap<QString, QMap<QString, RequestHandler::Action> > RequestHandler::initialize @@ -100,6 +100,8 @@ QMap<QString, QMap<QString, RequestHandler::Action> > RequestHandler::initialize
ADD_ACTION(command, getTorrentDlLimit);
ADD_ACTION(command, setTorrentUpLimit);
ADD_ACTION(command, setTorrentDlLimit);
ADD_ACTION(command, alternativeSpeedLimitsEnabled);
ADD_ACTION(command, toggleAlternativeSpeedLimits);
ADD_ACTION(command, toggleSequentialDownload);
ADD_ACTION(command, toggleFirstLastPiecePrio);
ADD_ACTION(command, delete);
@ -116,6 +118,25 @@ QMap<QString, QMap<QString, RequestHandler::Action> > RequestHandler::initialize @@ -116,6 +118,25 @@ QMap<QString, QMap<QString, RequestHandler::Action> > RequestHandler::initialize
return actions;
}
#define CHECK_URI() \
if (!args_.isEmpty()) { \
status(404, "Not Found"); \
return; \
}
#define CHECK_PARAMETERS(PARAMETERS) \
QStringList parameters; \
parameters << PARAMETERS; \
if (parameters.size() != request().posts.size()) { \
status(400, "Bad Request"); \
return; \
} \
foreach (QString key, request().posts.keys()) { \
if (!parameters.contains(key, Qt::CaseInsensitive)) { \
status(400, "Bad Request"); \
return; \
} \
}
void RequestHandler::action_public_index()
{
QString path;
@ -163,6 +184,7 @@ void RequestHandler::action_public_login() @@ -163,6 +184,7 @@ void RequestHandler::action_public_login()
void RequestHandler::action_public_logout()
{
CHECK_URI();
sessionEnd();
}
@ -200,6 +222,7 @@ void RequestHandler::action_public_images() @@ -200,6 +222,7 @@ void RequestHandler::action_public_images()
// - offset (int): set offset (if less than 0 - offset from end)
void RequestHandler::action_json_torrents()
{
CHECK_URI();
const QStringMap& gets = request().gets;
print(btjson::getTorrents(
@ -210,41 +233,49 @@ void RequestHandler::action_json_torrents() @@ -210,41 +233,49 @@ void RequestHandler::action_json_torrents()
void RequestHandler::action_json_preferences()
{
CHECK_URI();
print(prefjson::getPreferences(), CONTENT_TYPE_JS);
}
void RequestHandler::action_json_transferInfo()
{
CHECK_URI();
print(btjson::getTransferInfo(), CONTENT_TYPE_JS);
}
void RequestHandler::action_json_propertiesGeneral()
{
CHECK_URI();
print(btjson::getPropertiesForTorrent(args_.front()), CONTENT_TYPE_JS);
}
void RequestHandler::action_json_propertiesTrackers()
{
CHECK_URI();
print(btjson::getTrackersForTorrent(args_.front()), CONTENT_TYPE_JS);
}
void RequestHandler::action_json_propertiesFiles()
{
CHECK_URI();
print(btjson::getFilesForTorrent(args_.front()), CONTENT_TYPE_JS);
}
void RequestHandler::action_version_api()
{
CHECK_URI();
print(QString::number(API_VERSION), CONTENT_TYPE_TXT);
}
void RequestHandler::action_version_api_min()
{
CHECK_URI();
print(QString::number(API_VERSION_MIN), CONTENT_TYPE_TXT);
}
void RequestHandler::action_version_qbittorrent()
{
CHECK_URI();
print(QString(VERSION), CONTENT_TYPE_TXT);
}
@ -255,11 +286,14 @@ void RequestHandler::action_command_shutdown() @@ -255,11 +286,14 @@ void RequestHandler::action_command_shutdown()
// need to reply to the Web UI before
// actually shutting down.
CHECK_URI();
QTimer::singleShot(0, qApp, SLOT(quit()));
}
void RequestHandler::action_command_download()
{
CHECK_URI();
CHECK_PARAMETERS("urls");
QString urls = request().posts["urls"];
QStringList list = urls.split('\n');
@ -284,6 +318,7 @@ void RequestHandler::action_command_download() @@ -284,6 +318,7 @@ void RequestHandler::action_command_download()
void RequestHandler::action_command_upload()
{
qDebug() << Q_FUNC_INFO;
CHECK_URI();
foreach(const UploadedFile& torrent, request().files) {
QString filePath = saveTmpFile(torrent.data);
@ -307,6 +342,8 @@ void RequestHandler::action_command_upload() @@ -307,6 +342,8 @@ void RequestHandler::action_command_upload()
void RequestHandler::action_command_addTrackers()
{
CHECK_URI();
CHECK_PARAMETERS("hash" << "urls");
QString hash = request().posts["hash"];
if (!hash.isEmpty()) {
@ -326,31 +363,41 @@ void RequestHandler::action_command_addTrackers() @@ -326,31 +363,41 @@ void RequestHandler::action_command_addTrackers()
void RequestHandler::action_command_resumeAll()
{
CHECK_URI();
QBtSession::instance()->resumeAllTorrents();
}
void RequestHandler::action_command_pauseAll()
{
CHECK_URI();
QBtSession::instance()->pauseAllTorrents();
}
void RequestHandler::action_command_resume()
{
CHECK_URI();
CHECK_PARAMETERS("hash");
QBtSession::instance()->resumeTorrent(request().posts["hash"]);
}
void RequestHandler::action_command_pause()
{
CHECK_URI();
CHECK_PARAMETERS("hash");
QBtSession::instance()->pauseTorrent(request().posts["hash"]);
}
void RequestHandler::action_command_setPreferences()
{
CHECK_URI();
CHECK_PARAMETERS("json");
prefjson::setPreferences(request().posts["json"]);
}
void RequestHandler::action_command_setFilePrio()
{
CHECK_URI();
CHECK_PARAMETERS("hash" << "id" << "priority");
QString hash = request().posts["hash"];
int file_id = request().posts["id"].toInt();
int priority = request().posts["priority"].toInt();
@ -362,34 +409,48 @@ void RequestHandler::action_command_setFilePrio() @@ -362,34 +409,48 @@ void RequestHandler::action_command_setFilePrio()
void RequestHandler::action_command_getGlobalUpLimit()
{
CHECK_URI();
print(QByteArray::number(QBtSession::instance()->getSession()->settings().upload_rate_limit));
}
void RequestHandler::action_command_getGlobalDlLimit()
{
CHECK_URI();
print(QByteArray::number(QBtSession::instance()->getSession()->settings().download_rate_limit));
}
void RequestHandler::action_command_setGlobalUpLimit()
{
CHECK_URI();
CHECK_PARAMETERS("limit");
qlonglong limit = request().posts["limit"].toLongLong();
if (limit == 0) limit = -1;
QBtSession::instance()->setUploadRateLimit(limit);
Preferences::instance()->setGlobalUploadLimit(limit / 1024.);
if (Preferences::instance()->isAltBandwidthEnabled())
Preferences::instance()->setAltGlobalUploadLimit(limit / 1024.);
else
Preferences::instance()->setGlobalUploadLimit(limit / 1024.);
}
void RequestHandler::action_command_setGlobalDlLimit()
{
CHECK_URI();
CHECK_PARAMETERS("limit");
qlonglong limit = request().posts["limit"].toLongLong();
if (limit == 0) limit = -1;
QBtSession::instance()->setDownloadRateLimit(limit);
Preferences::instance()->setGlobalDownloadLimit(limit / 1024.);
if (Preferences::instance()->isAltBandwidthEnabled())
Preferences::instance()->setAltGlobalDownloadLimit(limit / 1024.);
else
Preferences::instance()->setGlobalDownloadLimit(limit / 1024.);
}
void RequestHandler::action_command_getTorrentUpLimit()
{
CHECK_URI();
CHECK_PARAMETERS("hash");
QString hash = request().posts["hash"];
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
@ -399,6 +460,8 @@ void RequestHandler::action_command_getTorrentUpLimit() @@ -399,6 +460,8 @@ void RequestHandler::action_command_getTorrentUpLimit()
void RequestHandler::action_command_getTorrentDlLimit()
{
CHECK_URI();
CHECK_PARAMETERS("hash");
QString hash = request().posts["hash"];
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
@ -408,6 +471,8 @@ void RequestHandler::action_command_getTorrentDlLimit() @@ -408,6 +471,8 @@ void RequestHandler::action_command_getTorrentDlLimit()
void RequestHandler::action_command_setTorrentUpLimit()
{
CHECK_URI();
CHECK_PARAMETERS("hash" << "limit");
QString hash = request().posts["hash"];
qlonglong limit = request().posts["limit"].toLongLong();
if (limit == 0) limit = -1;
@ -419,6 +484,8 @@ void RequestHandler::action_command_setTorrentUpLimit() @@ -419,6 +484,8 @@ void RequestHandler::action_command_setTorrentUpLimit()
void RequestHandler::action_command_setTorrentDlLimit()
{
CHECK_URI();
CHECK_PARAMETERS("hash" << "limit");
QString hash = request().posts["hash"];
qlonglong limit = request().posts["limit"].toLongLong();
if (limit == 0) limit = -1;
@ -428,8 +495,22 @@ void RequestHandler::action_command_setTorrentDlLimit() @@ -428,8 +495,22 @@ void RequestHandler::action_command_setTorrentDlLimit()
h.set_download_limit(limit);
}
void RequestHandler::action_command_toggleAlternativeSpeedLimits()
{
CHECK_URI();
QBtSession::instance()->useAlternativeSpeedsLimit(!Preferences::instance()->isAltBandwidthEnabled());
}
void RequestHandler::action_command_alternativeSpeedLimitsEnabled()
{
CHECK_URI();
print(QByteArray::number(Preferences::instance()->isAltBandwidthEnabled()));
}
void RequestHandler::action_command_toggleSequentialDownload()
{
CHECK_URI();
CHECK_PARAMETERS("hashes");
QStringList hashes = request().posts["hashes"].split("|");
foreach (const QString &hash, hashes) {
try {
@ -442,6 +523,8 @@ void RequestHandler::action_command_toggleSequentialDownload() @@ -442,6 +523,8 @@ void RequestHandler::action_command_toggleSequentialDownload()
void RequestHandler::action_command_toggleFirstLastPiecePrio()
{
CHECK_URI();
CHECK_PARAMETERS("hashes");
QStringList hashes = request().posts["hashes"].split("|");
foreach (const QString &hash, hashes) {
try {
@ -454,6 +537,8 @@ void RequestHandler::action_command_toggleFirstLastPiecePrio() @@ -454,6 +537,8 @@ void RequestHandler::action_command_toggleFirstLastPiecePrio()
void RequestHandler::action_command_delete()
{
CHECK_URI();
CHECK_PARAMETERS("hashes");
QStringList hashes = request().posts["hashes"].split("|");
foreach (const QString &hash, hashes)
QBtSession::instance()->deleteTorrent(hash, false);
@ -461,6 +546,8 @@ void RequestHandler::action_command_delete() @@ -461,6 +546,8 @@ void RequestHandler::action_command_delete()
void RequestHandler::action_command_deletePerm()
{
CHECK_URI();
CHECK_PARAMETERS("hashes");
QStringList hashes = request().posts["hashes"].split("|");
foreach (const QString &hash, hashes)
QBtSession::instance()->deleteTorrent(hash, true);
@ -468,6 +555,8 @@ void RequestHandler::action_command_deletePerm() @@ -468,6 +555,8 @@ void RequestHandler::action_command_deletePerm()
void RequestHandler::action_command_increasePrio()
{
CHECK_URI();
CHECK_PARAMETERS("hashes");
QStringList hashes = request().posts["hashes"].split("|");
std::priority_queue<QPair<int, QTorrentHandle>,
@ -499,6 +588,8 @@ void RequestHandler::action_command_increasePrio() @@ -499,6 +588,8 @@ void RequestHandler::action_command_increasePrio()
void RequestHandler::action_command_decreasePrio()
{
CHECK_URI();
CHECK_PARAMETERS("hashes");
QStringList hashes = request().posts["hashes"].split("|");
std::priority_queue<QPair<int, QTorrentHandle>,
@ -531,6 +622,8 @@ void RequestHandler::action_command_decreasePrio() @@ -531,6 +622,8 @@ void RequestHandler::action_command_decreasePrio()
void RequestHandler::action_command_topPrio()
{
CHECK_URI();
CHECK_PARAMETERS("hashes");
foreach (const QString &hash, request().posts["hashes"].split("|")) {
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
if (h.is_valid()) h.queue_position_top();
@ -539,6 +632,8 @@ void RequestHandler::action_command_topPrio() @@ -539,6 +632,8 @@ void RequestHandler::action_command_topPrio()
void RequestHandler::action_command_bottomPrio()
{
CHECK_URI();
CHECK_PARAMETERS("hashes");
foreach (const QString &hash, request().posts["hashes"].split("|")) {
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
if (h.is_valid()) h.queue_position_bottom();
@ -547,6 +642,8 @@ void RequestHandler::action_command_bottomPrio() @@ -547,6 +642,8 @@ void RequestHandler::action_command_bottomPrio()
void RequestHandler::action_command_recheck()
{
CHECK_URI();
CHECK_PARAMETERS("hash");
QBtSession::instance()->recheckTorrent(request().posts["hash"]);
}

2
src/webui/requesthandler.h

@ -74,6 +74,8 @@ private: @@ -74,6 +74,8 @@ private:
void action_command_getTorrentDlLimit();
void action_command_setTorrentUpLimit();
void action_command_setTorrentDlLimit();
void action_command_alternativeSpeedLimitsEnabled();
void action_command_toggleAlternativeSpeedLimits();
void action_command_toggleSequentialDownload();
void action_command_toggleFirstLastPiecePrio();
void action_command_delete();

3
src/webui/webapplication.cpp

@ -193,7 +193,8 @@ void WebApplication::translateDocument(QString& data) @@ -193,7 +193,8 @@ void WebApplication::translateDocument(QString& data)
"TransferListFiltersWidget", "TransferListWidget", "PropertiesWidget",
"HttpServer", "confirmDeletionDlg", "TrackerList", "TorrentFilesModel",
"options_imp", "Preferences", "TrackersAdditionDlg", "ScanFoldersModel",
"PropTabBar", "TorrentModel", "downloadFromURL", "MainWindow", "misc"
"PropTabBar", "TorrentModel", "downloadFromURL", "MainWindow", "misc",
"StatusBar"
};
const size_t context_count = sizeof(contexts) / sizeof(contexts[0]);
int i = 0;

11
src/webui/www/private/index.html

@ -117,7 +117,16 @@ @@ -117,7 +117,16 @@
<div id="desktopFooter">
<span id="error_div"></span>
<table style="position: absolute; right: 5px;">
<tr><td id="DlInfos" style="cursor:pointer;"></td><td style="width: 2px;margin:0;"><img src="images/skin/toolbox-divider.gif" alt="" style="height: 18px; padding-left: 10px; padding-right: 10px; margin-bottom: -2px;"/></td><td id="UpInfos" style="cursor:pointer;"></td></tr>
<tr>
<td id="DHTNodes"></td>
<td style="width: 2px;margin:0;"><img src="images/skin/toolbox-divider.gif" alt="" style="height: 18px; padding-left: 10px; padding-right: 10px; margin-bottom: -2px;"/></td>
<td><img id="connectionStatus" alt="Connection Status" src="images/skin/firewalled.png" /></td>
<td style="width: 2px;margin:0;"><img src="images/skin/toolbox-divider.gif" alt="" style="height: 18px; padding-left: 10px; padding-right: 10px; margin-bottom: -2px;"/></td>
<td style="cursor:pointer;"><img id="alternativeSpeedLimits" alt="_(Alternative speed limits)" src="images/slow_off.png" /></td>
<td style="width: 2px;margin:0;"><img src="images/skin/toolbox-divider.gif" alt="" style="height: 18px; padding-left: 10px; padding-right: 10px; margin-bottom: -2px;"/></td>
<td id="DlInfos" style="cursor:pointer; min-width: 200px"></td>
<td style="width: 2px;margin:0;"><img src="images/skin/toolbox-divider.gif" alt="" style="height: 18px; padding-left: 10px; padding-right: 10px; margin-bottom: -2px;"/></td>
<td id="UpInfos" style="cursor:pointer; min-width: 200px"></td></tr>
</table>
</div>
</div>

4
src/webui/www/public/css/Layout.css

@ -423,3 +423,7 @@ div.toolbox.divider { /* Have to specify div here for IE6's sake */ @@ -423,3 +423,7 @@ div.toolbox.divider { /* Have to specify div here for IE6's sake */
height: 16px;
}
#desktopFooter td {
vertical-align: top;
text-align: center;
}

81
src/webui/www/public/downloadlimit.html

@ -1,52 +1,61 @@ @@ -1,52 +1,61 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>_(Torrent Download Speed Limiting)</title>
<link rel="stylesheet" href="css/style.css" type="text/css" />
<script type="text/javascript" src="scripts/mootools-1.2-core-yc.js" charset="utf-8"></script>
<script type="text/javascript" src="scripts/mootools-1.2-more.js" charset="utf-8"></script>
<script type="text/javascript" src="scripts/mocha-yc.js" charset="utf-8"></script>
<script type="text/javascript" src="scripts/parametrics.js" charset="utf-8"></script>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>_(Torrent Download Speed Limiting)</title>
<link rel="stylesheet" href="css/style.css" type="text/css" />
<script type="text/javascript" src="scripts/mootools-1.2-core-yc.js" charset="utf-8"></script>
<script type="text/javascript" src="scripts/mootools-1.2-more.js" charset="utf-8"></script>
<script type="text/javascript" src="scripts/mocha-yc.js" charset="utf-8"></script>
<script type="text/javascript" src="scripts/parametrics.js" charset="utf-8"></script>
</head>
<body>
<div style="width: 100%; text-align: center; margin: 0 auto;">
<div style="width: 100%; text-align: center; margin: 0 auto; overflow: hidden">
<div id="dllimitSlider" class="slider">
<div id="dllimitUpdate" class="update">_(Download limit:) <input id="dllimitUpdatevalue" size="6" placeholder="∞" style="text-align: center;"> <span id="dlLimitUnit">_(KiB/s)</span></div>
<div class="sliderWrapper">
<div id="dllimitSliderknob" class="sliderknob"></div>
<div id="dllimitSliderarea" class="sliderarea"></div>
</div>
<div class="clear"></div>
<div id="dllimitUpdate" class="update">_(Download limit:) <input id="dllimitUpdatevalue" size="6" placeholder="∞" style="text-align: center;"> <span id="dlLimitUnit">_(KiB/s)</span></div>
<div class="sliderWrapper">
<div id="dllimitSliderknob" class="sliderknob"></div>
<div id="dllimitSliderarea" class="sliderarea"></div>
</div>
<div class="clear"></div>
</div>
<script type="text/javascript">
var hash = new URI().getData('hash');
setDlLimit = function() {
var limit = $("dllimitUpdatevalue").value.toInt() * 1024;
if(hash == "global") {
new Request({url: 'command/setGlobalDlLimit',
method: 'post',
data: {'limit': limit},
onComplete: function() {
window.parent.closeWindows();
}
}).send();
} else {
new Request({url: 'command/setTorrentDlLimit',
method: 'post',
data: {'hash': hash, 'limit': limit},
onComplete: function() {
window.parent.closeWindows();
}
}).send();
}
}
var hash = new URI().getData('hash');
setDlLimit = function() {
var limit = $("dllimitUpdatevalue").value.toInt() * 1024;
if (hash == "global") {
new Request({
url: 'command/setGlobalDlLimit',
method: 'post',
data: {
'limit': limit
},
onComplete: function() {
window.parent.updateTransferInfo();
window.parent.closeWindows();
}
}).send();
}
else {
new Request({
url: 'command/setTorrentDlLimit',
method: 'post',
data: {
'hash': hash,
'limit': limit
},
onComplete: function() {
window.parent.closeWindows();
}
}).send();
}
}
</script>
<input type="button" value="_(Apply)" onclick="setDlLimit()"/>
</div>
<script type="text/javascript">
MochaUI.addDlLimitSlider(hash);
MochaUI.addDlLimitSlider(hash);
</script>
</body>

12
src/webui/www/public/filters.html

@ -1,8 +1,8 @@ @@ -1,8 +1,8 @@
<ul class="filterList">
<li id="all_filter"><a href="#" onclick="setFilter('all');"><img src="images/skin/filterall.png"/>_(All)</a></li>
<li id="downloading_filter"><a href="#" onclick="setFilter('downloading');"><img src="images/skin/downloading.png"/>_(Downloading)</a></li>
<li id="completed_filter"><a href="#" onclick="setFilter('completed');"><img src="images/skin/uploading.png"/>_(Completed)</a></li>
<li id="paused_filter"><a href="#" onclick="setFilter('paused');"><img src="images/skin/paused.png"/>_(Paused)</a></li>
<li id="active_filter"><a href="#" onclick="setFilter('active');"><img src="images/skin/filteractive.png"/>_(Active)</a></li>
<li id="inactive_filter"><a href="#" onclick="setFilter('inactive');"><img src="images/skin/filterinactive.png"/>_(Inactive)</a></li>
<li id="all_filter"><a href="#" onclick="setFilter('all');return false;"><img src="images/skin/filterall.png"/>_(All)</a></li>
<li id="downloading_filter"><a href="#" onclick="setFilter('downloading');return false;"><img src="images/skin/downloading.png"/>_(Downloading)</a></li>
<li id="completed_filter"><a href="#" onclick="setFilter('completed');return false;"><img src="images/skin/uploading.png"/>_(Completed)</a></li>
<li id="paused_filter"><a href="#" onclick="setFilter('paused');return false;"><img src="images/skin/paused.png"/>_(Paused)</a></li>
<li id="active_filter"><a href="#" onclick="setFilter('active');return false;"><img src="images/skin/filteractive.png"/>_(Active)</a></li>
<li id="inactive_filter"><a href="#" onclick="setFilter('inactive');return false;"><img src="images/skin/filterinactive.png"/>_(Inactive)</a></li>
</ul>

295
src/webui/www/public/scripts/client.js

@ -25,6 +25,9 @@ @@ -25,6 +25,9 @@
myTable = new dynamicTable();
var updatePropertiesPanel = function(){};
var updateTransferInfo = function(){};
var updateTransferList = function(){};
var alternativeSpeedsLimit = false;
var stateToImg = function (state) {
if (state == "pausedUP" || state == "pausedDL") {
@ -43,122 +46,20 @@ var stateToImg = function (state) { @@ -43,122 +46,20 @@ var stateToImg = function (state) {
filter = getLocalStorageItem('selected_filter', 'all');
var loadTorrentsInfoTimer;
var loadTorrentsInfo = function () {
var queueing_enabled = false;
var url = new URI('json/torrents');
url.setData('filter', filter);
url.setData('sort', myTable.table.sortedColumn);
url.setData('reverse', myTable.table.reverseSort);
var request = new Request.JSON({
url : url,
noCache : true,
method : 'get',
onFailure : function () {
$('error_div').set('html', '_(qBittorrent client is not reachable)');
clearTimeout(loadTorrentsInfoTimer);
loadTorrentsInfoTimer = loadTorrentsInfo.delay(2000);
},
onSuccess : function (events) {
$('error_div').set('html', '');
if (events) {
// Add new torrents or update them
torrent_hashes = myTable.getRowIds();
events_hashes = new Array();
pos = 0;
events.each(function (event) {
events_hashes[events_hashes.length] = event.hash;
var row = new Array();
var data = new Array();
row.length = 10;
row[0] = stateToImg(event.state);
row[1] = event.name;
row[2] = event.priority > -1 ? event.priority : null;
data[2] = event.priority;
row[3] = friendlyUnit(event.size, false);
data[3] = event.size;
row[4] = (event.progress * 100).round(1);
if (row[4] == 100.0 && event.progress != 1.0)
row[4] = 99.9;
data[4] = event.progress;
row[5] = event.num_seeds;
if (event.num_complete != -1)
row[5] += " (" + event.num_complete + ")";
data[5] = event.num_seeds;
row[6] = event.num_leechs;
if (event.num_incomplete != -1)
row[6] += " (" + event.num_incomplete + ")";
data[6] = event.num_leechs;
row[7] = friendlyUnit(event.dlspeed, true);
data[7] = event.dlspeed;
row[8] = friendlyUnit(event.upspeed, true);
data[8] = event.upspeed;
row[9] = friendlyDuration(event.eta);
data[9] = event.eta;
if (event.ratio == -1)
row[10] = "∞";
else
row[10] = (Math.floor(100 * event.ratio) / 100).toFixed(2); //Don't round up
data[10] = event.ratio;
if (row[2] != null)
queueing_enabled = true;
attrs = {};
attrs['downloaded'] = (event.progress == 1.0);
attrs['state'] = event.state;
attrs['seq_dl'] = (event.seq_dl == true);
attrs['f_l_piece_prio'] = (event.f_l_piece_prio == true);
if (!torrent_hashes.contains(event.hash)) {
// New unfinished torrent
torrent_hashes[torrent_hashes.length] = event.hash;
//alert("Inserting row");
myTable.insertRow(event.hash, row, data, attrs, pos);
} else {
// Update torrent data
myTable.updateRow(event.hash, row, data, attrs, pos);
}
pos++;
});
// Remove deleted torrents
torrent_hashes.each(function (hash) {
if (!events_hashes.contains(hash)) {
myTable.removeRow(hash);
}
});
if (queueing_enabled) {
$('queueingButtons').removeClass('invisible');
$('queueingMenuItems').removeClass('invisible');
myTable.showPriority();
} else {
$('queueingButtons').addClass('invisible');
$('queueingMenuItems').addClass('invisible');
myTable.hidePriority();
}
myTable.altRow();
}
clearTimeout(loadTorrentsInfoTimer);
loadTorrentsInfoTimer = loadTorrentsInfo.delay(1500);
}
}).send();
};
var updateTransferList = function() {
clearTimeout(loadTorrentsInfoTimer);
loadTorrentsInfo();
}
window.addEvent('load', function () {
var saveColumnSizes = function () {
var filters_width = $('Filters').getSize().x;
var properties_height = $('propertiesPanel').getSize().y;
var properties_height_rel = $('propertiesPanel').getSize().y / Window.getSize().y;
localStorage.setItem('filters_width', filters_width);
localStorage.setItem('properties_height', properties_height);
localStorage.setItem('properties_height_rel', properties_height_rel);
}
window.addEvent('resize', function() {
// Resizing might takes some time.
saveColumnSizes.delay(200);
});
/*MochaUI.Desktop = new MochaUI.Desktop();
MochaUI.Desktop.desktop.setStyles({
'background': '#fff',
@ -226,6 +127,113 @@ window.addEvent('load', function () { @@ -226,6 +127,113 @@ window.addEvent('load', function () {
if (!speedInTitle)
$('speedInBrowserTitleBarLink').firstChild.style.opacity = '0';
var loadTorrentsInfoTimer;
var loadTorrentsInfo = function () {
var queueing_enabled = false;
var url = new URI('json/torrents');
url.setData('filter', filter);
url.setData('sort', myTable.table.sortedColumn);
url.setData('reverse', myTable.table.reverseSort);
var request = new Request.JSON({
url : url,
noCache : true,
method : 'get',
onFailure : function () {
$('error_div').set('html', '_(qBittorrent client is not reachable)');
clearTimeout(loadTorrentsInfoTimer);
loadTorrentsInfoTimer = loadTorrentsInfo.delay(2000);
},
onSuccess : function (events) {
$('error_div').set('html', '');
if (events) {
// Add new torrents or update them
torrent_hashes = myTable.getRowIds();
events_hashes = new Array();
pos = 0;
events.each(function (event) {
events_hashes[events_hashes.length] = event.hash;
var row = new Array();
var data = new Array();
row.length = 10;
row[0] = stateToImg(event.state);
row[1] = event.name;
row[2] = event.priority > -1 ? event.priority : null;
data[2] = event.priority;
row[3] = friendlyUnit(event.size, false);
data[3] = event.size;
row[4] = (event.progress * 100).round(1);
if (row[4] == 100.0 && event.progress != 1.0)
row[4] = 99.9;
data[4] = event.progress;
row[5] = event.num_seeds;
if (event.num_complete != -1)
row[5] += " (" + event.num_complete + ")";
data[5] = event.num_seeds;
row[6] = event.num_leechs;
if (event.num_incomplete != -1)
row[6] += " (" + event.num_incomplete + ")";
data[6] = event.num_leechs;
row[7] = friendlyUnit(event.dlspeed, true);
data[7] = event.dlspeed;
row[8] = friendlyUnit(event.upspeed, true);
data[8] = event.upspeed;
row[9] = friendlyDuration(event.eta);
data[9] = event.eta;
if (event.ratio == -1)
row[10] = "∞";
else
row[10] = (Math.floor(100 * event.ratio) / 100).toFixed(2); //Don't round up
data[10] = event.ratio;
if (row[2] != null)
queueing_enabled = true;
attrs = {};
attrs['downloaded'] = (event.progress == 1.0);
attrs['state'] = event.state;
attrs['seq_dl'] = (event.seq_dl == true);
attrs['f_l_piece_prio'] = (event.f_l_piece_prio == true);
if (!torrent_hashes.contains(event.hash)) {
// New unfinished torrent
torrent_hashes[torrent_hashes.length] = event.hash;
//alert("Inserting row");
myTable.insertRow(event.hash, row, data, attrs, pos);
} else {
// Update torrent data
myTable.updateRow(event.hash, row, data, attrs, pos);
}
pos++;
});
// Remove deleted torrents
torrent_hashes.each(function (hash) {
if (!events_hashes.contains(hash)) {
myTable.removeRow(hash);
}
});
if (queueing_enabled) {
$('queueingButtons').removeClass('invisible');
$('queueingMenuItems').removeClass('invisible');
myTable.showPriority();
} else {
$('queueingButtons').addClass('invisible');
$('queueingMenuItems').addClass('invisible');
myTable.hidePriority();
}
myTable.altRow();
}
clearTimeout(loadTorrentsInfoTimer);
loadTorrentsInfoTimer = loadTorrentsInfo.delay(1500);
}
}).send();
};
updateTransferList = function() {
clearTimeout(loadTorrentsInfoTimer);
loadTorrentsInfo();
}
var loadTransferInfoTimer;
var loadTransferInfo = function () {
var url = 'json/transferInfo';
@ -240,24 +248,29 @@ window.addEvent('load', function () { @@ -240,24 +248,29 @@ window.addEvent('load', function () {
},
onSuccess : function (info) {
if (info) {
dl_limit = "";
var transfer_info = "";
if (info.dl_rate_limit != undefined)
dl_limit = "[%1] ".replace("%1", friendlyUnit(info.dl_rate_limit, true));
$("DlInfos").set('html', "%3_(D: %1 - T: %2)"
.replace("%1", friendlyUnit(info.dl_info_speed, true))
.replace("%2", friendlyUnit(info.dl_info_data, false))
.replace("%3", dl_limit));
up_limit = "";
transfer_info += "[" + friendlyUnit(info.dl_rate_limit, true) + "] ";
transfer_info += friendlyUnit(info.dl_info_speed, true);
transfer_info += " (" + friendlyUnit(info.dl_info_data, false) + ")"
$("DlInfos").set('html', transfer_info);
transfer_info = "";
if (info.up_rate_limit != undefined)
up_limit = "[%1] ".replace("%1", friendlyUnit(info.up_rate_limit, true));
$("UpInfos").set('html', "%3_(U: %1 - T: %2)"
.replace("%1", friendlyUnit(info.up_info_speed, true))
.replace("%2", friendlyUnit(info.up_info_data, false))
.replace("%3", up_limit));
transfer_info += "[" + friendlyUnit(info.up_rate_limit, true) + "] ";
transfer_info += friendlyUnit(info.up_info_speed, true)
transfer_info += " (" + friendlyUnit(info.up_info_data, false) + ")"
$("UpInfos").set('html', transfer_info);
if (speedInTitle)
document.title = "_(D:%1 U:%2)".replace("%1", friendlyUnit(info.dl_info_speed, true)).replace("%2", friendlyUnit(info.up_info_speed, true));
else
document.title = "_(qBittorrent web User Interface)";
$('DHTNodes').set('html', '_(DHT: %1 nodes)'.replace("%1", info.dht_nodes));
if (info.connection_status == "connected")
$('connectionStatus').src = 'images/skin/connected.png';
else if (info.connection_status == "firewalled")
$('connectionStatus').src = 'images/skin/firewalled.png';
else
$('connectionStatus').src = 'images/skin/disconnected.png';
clearTimeout(loadTransferInfoTimer);
loadTransferInfoTimer = loadTransferInfo.delay(3000);
}
@ -265,7 +278,7 @@ window.addEvent('load', function () { @@ -265,7 +278,7 @@ window.addEvent('load', function () {
}).send();
};
var updateTransferInfo = function() {
updateTransferInfo = function() {
clearTimeout(loadTransferInfoTimer);
loadTransferInfo();
}
@ -273,6 +286,40 @@ window.addEvent('load', function () { @@ -273,6 +286,40 @@ window.addEvent('load', function () {
// Start fetching data now
loadTransferInfo();
var updateAltSpeedIcon = function(enabled) {
if (enabled)
$('alternativeSpeedLimits').src = "images/slow.png";
else
$('alternativeSpeedLimits').src = "images/slow_off.png"
}
// Determine whether the alternative speed limits are enabled or not
new Request({url: 'command/alternativeSpeedLimitsEnabled',
method: 'get',
onSuccess : function (isEnabled) {
alternativeSpeedsLimit = !!isEnabled;
if (alternativeSpeedsLimit)
$('alternativeSpeedLimits').src = "images/slow.png"
}
}).send();
$('alternativeSpeedLimits').addEvent('click', function() {
// Change icon immediately to give some feedback
updateAltSpeedIcon(!alternativeSpeedsLimit);
new Request({url: 'command/toggleAlternativeSpeedLimits',
method: 'post',
onComplete: function() {
alternativeSpeedsLimit = !alternativeSpeedsLimit;
updateTransferInfo();
},
onFailure: function() {
// Restore icon in case of failure
updateAltSpeedIcon(alternativeSpeedsLimit)
}
}).send();
});
$('DlInfos').addEvent('click', globalDownloadLimitFN);
$('UpInfos').addEvent('click', globalUploadLimitFN);
@ -310,9 +357,9 @@ window.addEvent('load', function () { @@ -310,9 +357,9 @@ window.addEvent('load', function () {
onResize : saveColumnSizes,
height : null
});
var prop_h = localStorage.getItem('properties_height');
var prop_h = localStorage.getItem('properties_height_rel');
if ($defined(prop_h))
prop_h = prop_h.toInt();
prop_h = prop_h.toFloat() * Window.getSize().y;
else
prop_h = Window.getSize().y / 2.;
new MochaUI.Panel({

1
src/webui/www/public/scripts/contextmenu.js

@ -114,6 +114,7 @@ var ContextMenu = new Class({ @@ -114,6 +114,7 @@ var ContextMenu = new Class({
/* menu items */
this.menu.getElements('a').each(function(item) {
item.addEvent('click', function(e) {
e.preventDefault();
if (!item.hasClass('disabled')) {
this.execute(item.get('href').split('#')[1], $(this.options.element));
this.fireEvent('click', [item, e]);

4
src/webui/www/public/scripts/mocha-init.js

@ -101,7 +101,7 @@ initializeWindows = function() { @@ -101,7 +101,7 @@ initializeWindows = function() {
globalUploadLimitFN = function() {
new MochaUI.Window({
id: 'uploadLimitPage',
title: "_(Global Upload Speed Limiting)",
title: "_(Global Upload Speed Limit)",
loadMethod: 'iframe',
contentURL: 'uploadlimit.html?hash=global',
scrollbars: false,
@ -165,7 +165,7 @@ initializeWindows = function() { @@ -165,7 +165,7 @@ initializeWindows = function() {
globalDownloadLimitFN = function() {
new MochaUI.Window({
id: 'downloadLimitPage',
title: "_(Global Download Speed Limiting)",
title: "_(Global Download Speed Limit)",
loadMethod: 'iframe',
contentURL: 'downloadlimit.html?hash=global',
scrollbars: false,

2
src/webui/www/public/scripts/mocha-yc.js

File diff suppressed because one or more lines are too long

10
src/webui/www/public/scripts/mocha.js

@ -4648,7 +4648,7 @@ MUI.Column = new Class({ @@ -4648,7 +4648,7 @@ MUI.Column = new Class({
this.columnToggle();
}.bind(this));
this.resize.attach();
this.handleEl.setStyle('cursor', Browser.Engine.webkit ? 'col-resize' : 'e-resize').addClass('attached');
this.handleEl.setStyle('cursor', (Browser.Engine.webkit || Browser.Engine.gecko) ? 'col-resize' : 'e-resize').addClass('attached');
MUI.rWidth();
this.fireEvent('onExpand');
@ -5104,7 +5104,7 @@ MUI.extend({ @@ -5104,7 +5104,7 @@ MUI.extend({
instance.resize.attach();
instance.handleEl.setStyles({
'display': 'block',
'cursor': Browser.Engine.webkit ? 'row-resize' : 'n-resize'
'cursor': (Browser.Engine.webkit || Browser.Engine.gecko) ? 'row-resize' : 'n-resize'
}).removeClass('detached');
} else {
instance.resize.detach();
@ -5345,7 +5345,7 @@ function addResizeRight(element, min, max){ @@ -5345,7 +5345,7 @@ function addResizeRight(element, min, max){
var instance = instances.get(element.id);
var handle = element.getNext('.columnHandle');
handle.setStyle('cursor', Browser.Engine.webkit ? 'col-resize' : 'e-resize');
handle.setStyle('cursor', (Browser.Engine.webkit || Browser.Engine.gecko) ? 'col-resize' : 'e-resize');
if (!min) min = 50;
if (!max) max = 250;
if (MUI.ieLegacySupport) {
@ -5401,7 +5401,7 @@ function addResizeLeft(element, min, max){ @@ -5401,7 +5401,7 @@ function addResizeLeft(element, min, max){
var instance = instances.get(element.id);
var handle = element.getPrevious('.columnHandle');
handle.setStyle('cursor', Browser.Engine.webkit ? 'col-resize' : 'e-resize');
handle.setStyle('cursor', (Browser.Engine.webkit || Browser.Engine.gecko) ? 'col-resize' : 'e-resize');
var partner = element.getPrevious('.column');
if (!min) min = 50;
if (!max) max = 250;
@ -5443,7 +5443,7 @@ function addResizeBottom(element){ @@ -5443,7 +5443,7 @@ function addResizeBottom(element){
var instances = MUI.Panels.instances;
var instance = instances.get(element.id);
var handle = instance.handleEl;
handle.setStyle('cursor', Browser.Engine.webkit ? 'row-resize' : 'n-resize');
handle.setStyle('cursor', (Browser.Engine.webkit || Browser.Engine.gecko) ? 'row-resize' : 'n-resize');
partner = instance.partner;
min = 0;
max = function(){

81
src/webui/www/public/uploadlimit.html

@ -1,52 +1,61 @@ @@ -1,52 +1,61 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>_(Torrent Upload Speed Limiting)</title>
<link rel="stylesheet" href="css/style.css" type="text/css" />
<script type="text/javascript" src="scripts/mootools-1.2-core-yc.js" charset="utf-8"></script>
<script type="text/javascript" src="scripts/mootools-1.2-more.js" charset="utf-8"></script>
<script type="text/javascript" src="scripts/mocha-yc.js" charset="utf-8"></script>
<script type="text/javascript" src="scripts/parametrics.js" charset="utf-8"></script>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>_(Torrent Upload Speed Limiting)</title>
<link rel="stylesheet" href="css/style.css" type="text/css" />
<script type="text/javascript" src="scripts/mootools-1.2-core-yc.js" charset="utf-8"></script>
<script type="text/javascript" src="scripts/mootools-1.2-more.js" charset="utf-8"></script>
<script type="text/javascript" src="scripts/mocha-yc.js" charset="utf-8"></script>
<script type="text/javascript" src="scripts/parametrics.js" charset="utf-8"></script>
</head>
<body>
<div style="width: 100%; text-align: center; margin: 0 auto;">
<div style="width: 100%; text-align: center; margin: 0 auto; overflow: hidden">
<div id="uplimitSlider" class="slider">
<div id="uplimitUpdate" class="update">_(Upload limit:) <input id="uplimitUpdatevalue" size="6" placeholder="∞" style="text-align: center;"> <span id="upLimitUnit">_(KiB/s)</span></div>
<div class="sliderWrapper">
<div id="uplimitSliderknob" class="sliderknob"></div>
<div id="uplimitSliderarea" class="sliderarea"></div>
</div>
<div class="clear"></div>
<div id="uplimitUpdate" class="update">_(Upload limit:) <input id="uplimitUpdatevalue" size="6" placeholder="∞" style="text-align: center;"> <span id="upLimitUnit">_(KiB/s)</span></div>
<div class="sliderWrapper">
<div id="uplimitSliderknob" class="sliderknob"></div>
<div id="uplimitSliderarea" class="sliderarea"></div>
</div>
<div class="clear"></div>
</div>
<script type="text/javascript">
var hash = new URI().getData('hash');
setUpLimit = function() {
var limit = $("uplimitUpdatevalue").value.toInt() * 1024;
if(hash == "global") {
new Request({url: 'command/setGlobalUpLimit',
method: 'post',
data: {'limit': limit},
onComplete: function() {
window.parent.closeWindows();
}
}).send();
}else {
new Request({url: 'command/setTorrentUpLimit',
method: 'post',
data: {'hash': hash, 'limit': limit},
onComplete: function() {
window.parent.closeWindows();
}
}).send();
}
}
var hash = new URI().getData('hash');
setUpLimit = function() {
var limit = $("uplimitUpdatevalue").value.toInt() * 1024;
if (hash == "global") {
new Request({
url: 'command/setGlobalUpLimit',
method: 'post',
data: {
'limit': limit
},
onComplete: function() {
window.parent.updateTransferInfo();
window.parent.closeWindows();
}
}).send();
}
else {
new Request({
url: 'command/setTorrentUpLimit',
method: 'post',
data: {
'hash': hash,
'limit': limit
},
onComplete: function() {
window.parent.closeWindows();
}
}).send();
}
}
</script>
<input type="button" value="_(Apply)" onclick="setUpLimit()"/>
</div>
<script type="text/javascript">
MochaUI.addUpLimitSlider(hash);
MochaUI.addUpLimitSlider(hash);
</script>
</body>

Loading…
Cancel
Save